JavaFX: ConcurrentModificationException while adding TreeItem objects in TreeView, in a seperate thread - treeview

I have the following code
public void start(Stage primaryStage) {
BorderPane border_pane = new BorderPane();
TreeView tree = addTreeView(); //TreeView on the LEFT
border_pane.setLeft(tree);
/* more stuff added to the border_pane here... */
Scene scene = new Scene(border_pane, 900, 700);
scene.setFill(Color.GHOSTWHITE);
primaryStage.setTitle("PlugControl v0.1e");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
With addTreeView being a function that reads data off an SQL DB and adds around ~35 TreeItems, based on that data. The addition of TreeItems to treeItemRoot is done in a seperate thread. NOTE: treeItemRoot is declared in the main class, and is null before here.
public TreeView addTreeView() { //Our treeView is positioned on the LEFT
treeItemRoot = new PlugTreeItem<>("Active Plugs", new ImageView(new Image(getClass().getResourceAsStream("graphics/plugicon.png"))), new Plug()); //Root of the tree, contains a dummy Plug object.
selectedTreeItem = treeItemRoot;
treeItemRoot.setExpanded(true); //always expand it
selectedTreeItem.getPlugItem()
.getSIHUid().addListener(new ChangeListener<String>() {
#Override
public void changed(ObservableValue<? extends String> observable, String oldValue, String newValue
) {
System.err.println("changed " + oldValue + "->" + newValue);
}
}
);
TreeView<String> treeView = new TreeView<>(treeItemRoot); //Build the tree with our root node.
final Task task;
task = new Task<Void>() {
#Override
protected Void call() throws Exception {
//=========== SQL STUFF BEGINS HERE ============================
Statement sta = null;
ResultSet result_set = null;
Connection conn = null;
try {
try {
System.err.println("Loading JDBC driver...");
Class.forName("com.mysql.jdbc.Driver");
System.err.println("Driver loaded!");
} catch (ClassNotFoundException e) {
throw new RuntimeException("Cannot find JDBC driver in the classpath!", e);
}
System.err.println("Connecting to database...");
conn = DriverManager.getConnection("[DB link here]", "[username]", "[password]"); //Username is PlugControl, pw is woof
System.err.println("Connected to Database!");
sta = conn.createStatement();
String sql_query = "SELECT * FROM pwnodes INNER JOIN pwcomports ON pwnodes.NetworkID = pwcomports.NetworkID WHERE pwnodes.connection = 'on' ORDER BY pwnodes.Location";
result_set = sta.executeQuery(sql_query);
System.err.println("SQL query successfuly executed!");
int count = 0;
while (result_set.next()) {
Plug pl = null; //MARKER: We might need to do switch (result_set.getString("Server")) for SIHU1 and SIHU2.
count++;
pl = new Plug(result_set.getString("SIHUid"), result_set.getString("sensorID"), result_set.getString("Location"), result_set.getString("Appliance"), result_set.getString("Type"), result_set.getString("connection"), result_set.getString("Server"), result_set.getString("ServerIP"));
PlugTreeItem<String> pti = new PlugTreeItem(pl.getSIHUid().getValue() + " " + pl.getLocation() + " " + pl.getAppliance(), new ImageView(new Image(getClass().getResourceAsStream("graphics/smiley.png"))), pl); //icon does not work in children
treeItemRoot.getChildren().add(pti); //CONCURRENCY ERRORS HERE
}
System.err.println("ALERT SQL QUERY RESULTS: " + count);
} catch (SQLException e) //linked try clause # line 50
{
throw new RuntimeException("Cannot connect the database!", e);
} finally { // Time to wrap things up, by closing all open SQL procs.
try {
if (sta != null) {
sta.close();
}
if (result_set != null) {
result_set.close();
}
if (conn != null) {
System.err.println("Closing the connection.");
conn.close();
}
} catch (SQLException e) //We might as well ignore this, but just in case.
{
throw new RuntimeException("Error while closing up statement, result set and connection!", e);
}
}
//============== SQL STUFF ENDS HERE ===========================
System.err.println("Finished");
return null;
}
};
new Thread(task).start(); //Run the task!
treeView.getSelectionModel()
.selectedItemProperty().addListener(new ChangeListener() {
#Override
public void changed(ObservableValue observable, Object oldValue, Object newValue
) {
selectedTreeItem = (PlugTreeItem<String>) newValue;
System.err.println("DEBUG: Selection plug SIHUid: " + selectedTreeItem.getPlugItem().print()); //MARKER: REMOVE
updateTextFields(); //Update TextAreas.
if (!"DUMMY".equals(selectedTreeItem.getPlugItem().getSIHUid().getValue())) {
buttonOn.setDisable(false);
buttonOff.setDisable(false);
} else {
buttonOn.setDisable(true);
buttonOff.setDisable(true);
}
}
}
);
return treeView;
}
Once every 5-6 runs I receive a ConcurrentModificationException because I think the Thread task() doesn't manage to finish before addTreeView's returned TreeView is added to the border_pane, and maybe it begins iterating through it while it's still having items added to it?
Executing C:\Users\74\Documents\NetBeansProjects\PlugControl_v0.5\dist\run1559674105\PlugControl.jar using platform C:\Program Files\Java\jdk1.7.0_25\jre/bin/java
Loading JDBC driver...
Driver loaded!
Connecting to database...
Connected to Database!
SQL query successfuly executed!
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:819)
at java.util.ArrayList$Itr.next(ArrayList.java:791)
at com.sun.javafx.collections.ObservableListWrapper$ObservableListIterator.next(ObservableListWrapper.java:681)
at javafx.scene.control.TreeItem.updateExpandedDescendentCount(TreeItem.java:788)
at javafx.scene.control.TreeItem.getExpandedDescendentCount(TreeItem.java:777)
at javafx.scene.control.TreeView.getExpandedDescendantCount(TreeView.java:864)
at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:873)
at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
at com.sun.javafx.scene.control.skin.TreeViewSkin.getItemCount(TreeViewSkin.java:207)
at com.sun.javafx.scene.control.skin.TreeViewSkin.updateItemCount(TreeViewSkin.java:220)
at com.sun.javafx.scene.control.skin.TreeViewSkin.handleControlPropertyChanged(TreeViewSkin.java:135)
at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.IntegerPropertyBase.fireValueChangedEvent(IntegerPropertyBase.java:123)
at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
at javafx.scene.control.TreeView.setTreeItemCount(TreeView.java:515)
at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:876)
at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
at javafx.scene.control.TreeCell.updateItem(TreeCell.java:391)
at javafx.scene.control.TreeCell.access$000(TreeCell.java:67)
at javafx.scene.control.TreeCell$1.invalidated(TreeCell.java:95)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:195)
at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:161)
at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:112)
at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1596)
at com.sun.javafx.scene.control.skin.VirtualFlow.addLeadingCells(VirtualFlow.java:1049)
at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1005)
at com.sun.javafx.scene.control.skin.VirtualFlow.setCellCount(VirtualFlow.java:206)
at com.sun.javafx.scene.control.skin.TreeViewSkin.updateItemCount(TreeViewSkin.java:225)
at com.sun.javafx.scene.control.skin.TreeViewSkin.handleControlPropertyChanged(TreeViewSkin.java:135)
at com.sun.javafx.scene.control.skin.SkinBase$3.changed(SkinBase.java:282)
at javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:107)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:196)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.IntegerPropertyBase.fireValueChangedEvent(IntegerPropertyBase.java:123)
at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
at javafx.scene.control.TreeView.setTreeItemCount(TreeView.java:515)
at javafx.scene.control.TreeView.updateTreeItemCount(TreeView.java:876)
at javafx.scene.control.TreeView.impl_getTreeItemCount(TreeView.java:533)
at javafx.scene.control.TreeCell.updateItem(TreeCell.java:391)
at javafx.scene.control.TreeCell.access$000(TreeCell.java:67)
at javafx.scene.control.TreeCell$1.invalidated(TreeCell.java:95)
at com.sun.javafx.binding.ExpressionHelper$SingleInvalidation.fireValueChangedEvent(ExpressionHelper.java:155)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:100)
at javafx.beans.property.ReadOnlyIntegerWrapper$ReadOnlyPropertyImpl.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:195)
at javafx.beans.property.ReadOnlyIntegerWrapper.fireValueChangedEvent(ReadOnlyIntegerWrapper.java:161)
at javafx.beans.property.IntegerPropertyBase.markInvalid(IntegerPropertyBase.java:130)
at javafx.beans.property.IntegerPropertyBase.set(IntegerPropertyBase.java:163)
at javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:112)
at com.sun.javafx.scene.control.skin.VirtualFlow.setCellIndex(VirtualFlow.java:1596)
at com.sun.javafx.scene.control.skin.VirtualFlow.getCell(VirtualFlow.java:1500)
at com.sun.javafx.scene.control.skin.VirtualFlow.getCellLength(VirtualFlow.java:1523)
at com.sun.javafx.scene.control.skin.VirtualFlow$3.call(VirtualFlow.java:478)
at com.sun.javafx.scene.control.skin.VirtualFlow$3.call(VirtualFlow.java:476)
at com.sun.javafx.scene.control.skin.PositionMapper.computeViewportOffset(PositionMapper.java:143)
at com.sun.javafx.scene.control.skin.VirtualFlow.layoutChildren(VirtualFlow.java:1001)
at javafx.scene.Parent.layout(Parent.java:1018)
at javafx.scene.Parent.layout(Parent.java:1028)
at javafx.scene.Parent.layout(Parent.java:1028)
at javafx.scene.Parent.layout(Parent.java:1028)
at javafx.scene.Scene.layoutDirtyRoots(Scene.java:516)
at javafx.scene.Scene.doLayoutPass(Scene.java:487)
at javafx.scene.Scene.access$3900(Scene.java:170)
at javafx.scene.Scene$ScenePulseListener.pulse(Scene.java:2203)
at com.sun.javafx.tk.Toolkit.firePulse(Toolkit.java:363)
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(QuantumToolkit.java:460)
at com.sun.javafx.tk.quantum.QuantumToolkit$9.run(QuantumToolkit.java:329)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.access$100(WinApplication.java:29)
at com.sun.glass.ui.win.WinApplication$3$1.run(WinApplication.java:73)
at java.lang.Thread.run(Thread.java:724)
ALERT SQL QUERY RESULTS: 35
Closing the connection.
Finished
Any help/advice on dealing with the issue, guys? The exception doesn't point me towards a place in my code, and so far I'm working on hunches.}
EDIT: For reference, PlugTreeItem is just a TreeItem<> that also carries a Plug with it, Plug being a class of mine that holds a few String values. Nothing special.
public class PlugTreeItem<T> extends TreeItem{ \* code *\}

I would suggest you to make a List<Plug> inside your while loop rather than making individual object and adding it to the tree, because all operations on javafx controls must be done on javafx thread and not on task thread !
Create a list outside the Thread body
List<Plug> listOfPlugs = new ArrayList<Plug>();
Then, in the while loop, you can write
int count = 0;
while (result_set.next()) {
Plug pl = null;
count++;
pl = new Plug(result_set.getString("SIHUid"),
result_set.getString("sensorID"), result_set.getString("Location"),
result_set.getString("Appliance"), result_set.getString("Type"),
result_set.getString("connection"), result_set.getString("Server"),
result_set.getString("ServerIP"));
listOfPlugs.add(p1);
}
Later, after you start the thread you can make the following code
new Thread(task).start();
task.setOnSucceeded(new EventHandler<WorkerStateEvent>()
{
#Override
public void handle(WorkerStateEvent workerStateEvent) {
for(Plug p1 : listOfPlugs)
{
PlugTreeItem<String> pti = new PlugTreeItem(pl.getSIHUid().getValue()
+ " " + pl.getLocation() + " " + pl.getAppliance(),
new ImageView(new Image(
getClass().getResourceAsStream("graphics/smiley.png"))), pl);
treeItemRoot.getChildren().add(pti);
}
}

I don't see you syncing on the javafx application thread which is required when coming from another one

Related

How to delete alarge amount of data one by one from a table with their relations using transactional annotation

I have a large amount of data that I want to purge from the database, there are about 6 tables of which 3 have a many to many relationship with cascadeType. All the others are log and history tables independent of the 3 others
i want to purge this data one by one and if any of them have error while deleting i have to undo only the current record and show it in console and keep deleting the others
I am trying to use transactional annotation with springboot but all purging stops if an error occurs
how to manage this kind of need?
here is what i did :
#Transactional
private void purgeCards(List<CardEntity> cardsTobePurge) {
List<Long> nextCardsNumberToUpdate = getNextCardsWhichWillNotBePurge(cardsTobePurge);
TransactionTemplate lTransTemplate = new TransactionTemplate(transactionManager);
lTransTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
lTransTemplate.execute(new TransactionCallback<Object>() {
#Override
public Object doInTransaction(TransactionStatus status) {
cardsTobePurge.forEach(cardTobePurge -> {
Long nextCardNumberOfCurrent = cardTobePurge.getNextCard();
if (nextCardsNumberToUpdate.contains(nextCardNumberOfCurrent)) {
CardEntity cardToUnlik = cardRepository.findByCardNumber(nextCardNumberOfCurrent);
unLink(cardToUnlik);
}
log.info(BATCH_TITLE + " Removing card Number : " + cardTobePurge.getCardNumber() + " with Id : "
+ cardTobePurge.getId());
List<CardHistoryEntity> historyEntitiesOfThisCard = cardHistoryRepository.findByCard(cardTobePurge);
List<LogCreationCardEntity> logCreationEntitiesForThisCard = logCreationCardRepository
.findByCardNumber(cardTobePurge.getCardNumber());
List<LogCustomerMergeEntity> logCustomerMergeEntitiesForThisCard = logCustomerMergeRepository
.findByCard(cardTobePurge);
cardHistoryRepository.deleteAll(historyEntitiesOfThisCard);
logCreationCardRepository.deleteAll(logCreationEntitiesForThisCard);
logCustomerMergeRepository.deleteAll(logCustomerMergeEntitiesForThisCard);
cardRepository.delete(cardTobePurge);
});
return Boolean.TRUE;
}
});
}
As a solution to my question:
I worked with TransactionTemplate to be able to manage transactions manually
so if an exception is raised a rollback will only be applied for the current iteration and will continue to process other cards
private void purgeCards(List<CardEntity> cardsTobePurge) {
int[] counter = { 0 }; //to simulate the exception
List<Long> nextCardsNumberToUpdate = findNextCardsWhichWillNotBePurge(cardsTobePurge);
cardsTobePurge.forEach(cardTobePurge -> {
Long nextCardNumberOfCurrent = cardTobePurge.getNextCard();
CardEntity cardToUnlik = null;
counter[0]++; //to simulate the exception
if (nextCardsNumberToUpdate.contains(nextCardNumberOfCurrent)) {
cardToUnlik = cardRepository.findByCardNumber(nextCardNumberOfCurrent);
}
purgeCard(cardTobePurge, nextCardsNumberToUpdate, cardToUnlik, counter);
});
}
private void purgeCard(#NonNull CardEntity cardToPurge, List<Long> nextCardsNumberToUpdate, CardEntity cardToUnlik,
int[] counter) {
TransactionTemplate lTransTemplate = new TransactionTemplate(transactionManager);
lTransTemplate.setPropagationBehavior(TransactionTemplate.PROPAGATION_REQUIRED);
lTransTemplate.execute(new TransactionCallbackWithoutResult() {
#Override
public void doInTransactionWithoutResult(TransactionStatus status) {
try {
if (cardToUnlik != null)
unLink(cardToUnlik);
log.info(BATCH_TITLE + " Removing card Number : " + cardToPurge.getCardNumber() + " with Id : "
+ cardToPurge.getId());
List<CardHistoryEntity> historyEntitiesOfThisCard = cardHistoryRepository.findByCard(cardToPurge);
List<LogCreationCardEntity> logCreationEntitiesForThisCard = logCreationCardRepository
.findByCardNumber(cardToPurge.getCardNumber());
List<LogCustomerMergeEntity> logCustomerMergeEntitiesForThisCard = logCustomerMergeRepository
.findByCard(cardToPurge);
cardHistoryRepository.deleteAll(historyEntitiesOfThisCard);
logCreationCardRepository.deleteAll(logCreationEntitiesForThisCard);
logCustomerMergeRepository.deleteAll(logCustomerMergeEntitiesForThisCard);
cardRepository.delete(cardToPurge);
if (counter[0] == 2)//to simulate the exception
throw new Exception();//to simulate the exception
} catch (Exception e) {
status.setRollbackOnly();
if (cardToPurge != null)
log.error(BATCH_TITLE + " Problem with card Number : " + cardToPurge.getCardNumber()
+ " with Id : " + cardToPurge.getId(), e);
else
log.error(BATCH_TITLE + "Card entity is null", e);
}
}
});
}

how to set javafx Togglebutton in javafx Tableview to correct image based on boolean from sqlite database(JDBC)

I am working on a program that is going to allow users to select/add sounds as favorites
via a toggle button with an image in a javafx tableview tablecell and set the status of the sound in the database(the updating of the favorite status in the database works) however the updating of the image only partially works.
when pressing the toggle button the image updates correctly as does the database
initial loading
first 10 records before any favorite selection
after button is pressed
database
but the problem comes when I stop and restart the program because I wind up with none of the buttons selected (sound id 5 should be)
here is the code for the database data loading
public void getSounds() {
soundFilelist.removeAll(soundFilelist);
try {
Connection conn = DriverManager.getConnection("jdbc:sqlite:Sphere.db");
// add where userId = VerifiedUserId or something simular//
String sql = "SELECT * FROM Sounds Where userId = ? ";
PreparedStatement ps;
ResultSet rs;
ps = conn.prepareStatement(sql);
ps.setInt(1 , User.getUserId());
rs = ps.executeQuery();
while (rs.next()) {
int favoriteStatus;
soundFilelist.add(new Sound(
rs.getInt("SoundId") ,
rs.getString("SoundName") ,
rs.getString("soundPath") ,
rs.getLong("soundDurration") ,
favoriteStatus = rs.getInt("Favorite")));
System.out.println(favoriteStatus);
if(favoriteStatus == 0){
setFavoritesTableButton(0);
}else if(favoriteStatus == 1){
setFavoritesTableButton(1);
}
}
soundBrowser.setItems(soundFilelist);
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
the addButtoncell code
(favorite column 2) modified from https://riptutorial.com/javafx/example/27946/add-button-to-tableview
private void addButtonToTable() {
Callback<TableColumn<Sound, Void>, TableCell<Sound, Void>> cellFactory = new Callback<TableColumn<Sound, Void>, TableCell<Sound, Void>>() {
#Override
public TableCell<Sound, Void> call(final TableColumn<Sound, Void> param) {
favoritecell = new TableCell<>() {
private final ToggleButton btn = new ToggleButton();
private Image favoritesImage = new Image("SoundSphere/RegularSizeFavoritesImage.png");
private Image favoriteslPressedImage = new Image("SoundSphere/RegularSizeFavoriteslPressedImage.png");
private ImageView tableViewFavorites = new ImageView();
{
tableViewFavorites.setFitWidth(20);
tableViewFavorites.setFitHeight(20);
btn.setAlignment(Pos.CENTER);
// favoritecell.setAlignment(Pos.CENTER);
btn.setGraphic(tableViewFavorites);
btn.setOnAction((new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent actionEvent) {
if ((btn.isSelected())) {
Sound sound = getTableView().getItems().get(getIndex());
sound.setSoundId(soundIdColumn.getCellData(sound));
System.out.println("selected SID: "+ sound.getSoundId());
int selectedSoundId2 = sound.getSoundId();
tableViewFavorites.setImage(favoriteslPressedImage);
tableviewFavoriteButtonIsPressed = false;
//addFavorite();
System.out.println("buttonselectedId"+ selectedSoundId);
addFavorite2(selectedSoundId2);
}else{
Sound sound = getTableView().getItems().get(getIndex());
sound.setSoundId(soundIdColumn.getCellData(sound));
int selectedSoundId2 = sound.getSoundId();
tableViewFavorites.setImage(favoritesImage);
tableviewFavoriteButtonIsPressed = true;
removeFavorite2(selectedSoundId2);
}
}
private void removeFavorite2(int selectedSoundId2) {
try{
Connection conn = DriverManager.getConnection("jdbc:sqlite:Sphere.db");
String sql = "UPDATE Sounds " +
"SET Favorite = ?"+
// "soundName = ?"+
"Where soundId = ? AND userId = ?";
PreparedStatement ps;
ps = conn.prepareStatement(sql);
ps.setInt(1,0);
ps.setInt(2 , selectedSoundId2);
ps.setInt(3 , User.getUserId());
ps.executeUpdate();
System.out.println("Data has been removed");
}catch(Exception e){
System.out.println("we have a problem with add favorite 2");
e.printStackTrace();
}
}
}));
}
private void addFavorite2(int favoriteSoundId) {
try{
Connection conn = DriverManager.getConnection("jdbc:sqlite:Sphere.db");
String sql = "UPDATE Sounds " +
"SET Favorite = ?"+
// "soundName = ?"+
"Where soundId = ? AND userId = ?";
PreparedStatement ps;
ps = conn.prepareStatement(sql);
ps.setInt(1,1);
ps.setInt(2 , favoriteSoundId);
System.out.println(favoriteSoundId);
ps.setInt(3 , User.getUserId());
ps.executeUpdate();
System.out.println("Data has been inserted");
}catch(Exception e){
System.out.println("we have a problem with add favorite 2");
e.printStackTrace();
}
}
#Override
public void updateItem(Void item , boolean empty) {
super.updateItem(item , empty);
if (btn.isSelected() || dbFavorite) {
tableViewFavorites.setImage(tableFavoriteslPressedImage);
setGraphic(btn);
} if (!btn.isSelected() || !dbFavorite) {
tableViewFavorites.setImage(favoritesImage);
setGraphic(btn);
}
}
};
return favoritecell;
}
};
favoritesColumn2.setCellFactory(cellFactory);
soundBrowser.getColumns().add(favoritesColumn2);
}
public boolean setFavoritesTableButton(int favoriteStatus){
if(favoriteStatus == 1) {
dbFavorite = true;
}else if(favoriteStatus == 0){
dbFavorite = false;
}
return dbFavorite;
}
relevant Intializable code
soundNameColumn.setCellValueFactory(new PropertyValueFactory<>("soundName"));
soundPathColumn.setCellValueFactory(new PropertyValueFactory<>("soundPath"));
soundDurationColumn.setCellValueFactory(new PropertyValueFactory<>("soundDurration"));
favoritesColumn.setCellValueFactory(new PropertyValueFactory<>("Favorite"));
addButtonToTable();
getSounds();
I have tried setting the state of the toggle button in the get sounds method and various Booleans but none of my attempts have worked.
thank you for your time and any help
Thomas Gustafson

How do I combine Domino view data and JDBC query in a repeat control

Currently, I have a repeat control with computed fields that display column values from a Domino view. In each row in the repeat control I have another computed field that executes a SQL query that returns a value from a SQL table. The SQL query has a parameter that uses one of the column values from the Domino view.
For the SQL computed field I wrote a function that instantiates a JDBC connection and then executes a SQL query for each row in the repeat control. The function looks like this (the pTextNo argument comes from one of the column values in the Domino view):
function getFormulaTextDetailRows(pTextNo){
if(pTextNo == null){return ""};
var con:java.sql.Connection;
try {
con = #JdbcGetConnection("as400");
vStatement = "SELECT TXSQN, TXDTA FROM LIB1.PRTEXTS WHERE RTRIM(TEXT#) = ? ORDER BY TXSQN";
var vParam = [pTextNo];
var resultset:java.sql.ResultSet = #JdbcExecuteQuery(con, vStatement, vParam);
var returnList = "<ul>";
//format the results
while(resultset.next()){
returnList += ("<li>" + resultset.getString("TXDTA").trim() + "</li>");
}
returnList += "</ul>";
}catch(e){
returnList = e.toString()
}finally{
con.close();
}
return returnList;
}
This works fine but I'm sure this isn't the most efficient way of utilising the JDBC connection. Opening and closing a JDBC connection on each row in the repeat control isn't right and I'm concerned that when more than one person opens the XPage the server will run into difficulties with the number of open connections.
After doing some research on the internet it seems I should be using a jdbcConnectionManager on the page.
I added a jdbcConnectionManager to my custom control and also added a jdbcQuery data source to the panel that holds the repeat control. The jdbcConnectionManager looks like this:
<xe:jdbcConnectionManager
id="jdbcConnectionManager1"
connectionName="as400">
</xe:jdbcConnectionManager>
And the jdbcQuery data source looks like this:
<xe:jdbcQuery
var="jdbcProcessText"
scope="request"
calculateCount="true"
sqlQuery="SELECT TXSQN,TXDTA FROM DOMINO.PRTEXTS WHERE RTRIM(TEXT#) = ? AND TXSQN != '0' ORDER BY TXSQN"
connectionManager="jdbcConnectionManager1">
<xe:this.sqlParameters>
<xe:sqlParameter value="#{javascript:requestScope.processTextNo}">
</xe:sqlParameter>
</xe:this.sqlParameters>
</xe:jdbcQuery>
My computed field's value property in the repeat control looks like this:
requestScope.processTextNo = textrow.getDocument().getItemValueString('ProcessTextNo');
var vCount = jdbcProcessText.getCount();
var returnList = "<ul>";
for(i=0; i<vCount; i++){
returnList += ("<li>" + jdbcProcessText.get(i).getColumnValue("TXDTA") + "</li>");
}
returnList += "</ul>";
return returnList;
The problem I've run into is that I don't get any data from the JDBC query at all. Even if I hard code a value I know exists in the SQL table in the sqlParameter property of the jdbcQuery object I still get no results. I suspect I'm not calling the jdbcQuery object correctly but I can't figure out how to do so. Any help will be greatly appreciated.
You may want to reconsider your approach. I would suggest creating a Java bean to get the Domino view data, loop through that and call out to your query for each row in the view. Build up a List (Java List) of a Row class that has all the data you want to show. Then in the repeat call to your Java bean to a method that returns the List of Row classes. In each control in the repeat you would call to the getXXX method in your Row class. This way you can quickly build the List the repeat works on. Doing it your way in the control in the repeat will be very slow.
Howard
Here's the bean I wrote to do the job. At the start it opens a connection to the SQL data source, grabs a viewEntryCollection using a document UNID as a key, and then puts the column values into a HashMap for each row in the viewEntryCollection. One of the values in the HashMap is pulled from a SQL query. My repeat control iterates over the List returned by the bean. In other words the bean returns a List of HashMaps where most of the values in each HashMap comes from Domino view entry data and one value comes from SQL (not sure if that's the correct way of saying it, but it makes sense to me!).
Here's my code:
package com.foo;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Vector;
import javax.faces.context.FacesContext;
import lotus.domino.Database;
import lotus.domino.NotesException;
import lotus.domino.View;
import lotus.domino.ViewEntry;
import lotus.domino.ViewEntryCollection;
import com.ibm.xsp.extlib.relational.util.JdbcUtil;
import com.ibm.xsp.extlib.util.ExtLibUtil;
public class ProcessTextLines implements Serializable {
private static final long serialVersionUID = 1L;
private Connection conn = null;
public int rowCount = 0;
public int getRowCount() {
return rowCount;
}
// public void setRowCount(int rowCount) {
// this.rowCount = rowCount;
// }
public ProcessTextLines() {
/*
* argumentless constructor
*/
try {
// System.out.println("ProcessTextLines.java - initialising connection to as400");
this.conn = this.initialiseConnection();
} catch (SQLException e) {
e.printStackTrace();
}
finally {
if (this.conn != null) {
// System.out.println("ProcessTextLines.java - closing connection to as400");
try {
this.conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
public List<HashMap<String, String>> getRows(final String unid)
throws NotesException {
List<HashMap<String, String>> result = new ArrayList<HashMap<String, String>>();
try {
Database db = ExtLibUtil.getCurrentSession().getCurrentDatabase();
View view = db.getView("luProductMasterFormula");
view.setAutoUpdate(false);
ViewEntryCollection vec = view.getAllEntriesByKey(unid, true);
ViewEntry ve = vec.getFirstEntry();
while (ve != null) {
result.add(processRowVals(ve));
rowCount++;
ViewEntry tmp = vec.getNextEntry(ve);
ve.recycle();
ve = tmp;
}
view.recycle();
db.recycle();
vec.recycle();
} catch (NotesException e) {
e.printStackTrace();
}
return result;
}
/*
* Create a HashMap of names + column values from a ViewEntry
*/
#SuppressWarnings("unchecked")
private HashMap<String, String> processRowVals(ViewEntry ve) {
HashMap<String, String> processRow = new HashMap<String, String>();
try {
Vector cols = ve.getColumnValues();
processRow.put("sequenceNo", cols.get(1).toString());
processRow.put("textNo", cols.get(3).toString());
processRow.put("status", cols.get(6).toString());
processRow.put("textLines", getProcessTextLines(cols.get(3).toString()));
// unid of the entry's doc
processRow.put("unid", ve.getUniversalID());
} catch (NotesException e) {
e.printStackTrace();
}
return processRow;
}
private Connection initialiseConnection() throws SQLException {
Connection connection = null;
try {
connection = JdbcUtil.createNamedConnection(FacesContext
.getCurrentInstance(), "as400");
} catch (SQLException e) {
e.printStackTrace();
}
return connection;
}
private String getProcessTextLines(String textNo) {
String resultHTML = "<ul class=\"processTextList\">";
try {
// System.out.println("ProcessTextLines.java - setting SQL parameter: " + textNo);
PreparedStatement prep = conn
.prepareStatement("SELECT TXSQN,TXDTA FROM LIB1.PRTEXTS WHERE RTRIM(TEXT#) = ? AND TXSQN != '0' ORDER BY TXSQN");
// supply a value to the PreparedStatement's parameter (the first
// argument is 1 because it is the first parameter)
prep.setString(1, textNo);
ResultSet resultSet = prep.executeQuery();
while (resultSet.next()) {
resultHTML += ("<li>" + resultSet.getString("TXDTA").trim() + "</li>");
}
} catch (SQLException e) {
// e.printStackTrace();
}
resultHTML += "</ul>";
return resultHTML;
}
}
It took me a while because of my lack of Java knowledge but with the pointers #Howard gave plus the few bits and pieces I found on the web I was able to cobble this together.
Opening and closing the SQL connection in the constructor feels counter intuitive to me, but it seems to work.

C3P0 Statement.close deadlock

Google returns lots of people with deadlock issues in C3P0, but none of the solutions appear to apply (most people suggest setting maxStatements = 0 and maxStatementsPerConnection = 0, both of which we have).
I am using a ComboPooledDataSource from C3P0, initialised as;
cpds = new ComboPooledDataSource();
cpds.setDriverClass("org.postgresql.Driver");
cpds.setJdbcUrl("jdbc:postgresql://" + host + ":5432/" + database);
cpds.setUser(user);
cpds.setPassword(pass);
My query function looks like;
public static List<Map<String, Object>> query(String q) {
Connection c = null;
Statement s = null;
ResultSet r = null;
try {
c = cpds.getConnection();
s = c.createStatement();
s.executeQuery(q);
r = s.getResultSet();
/* parse result set into results List<Map> */
return results;
}
catch(Exception e) { MyUtils.logException(e); }
finally {
closeQuietly(r);
closeQuietly(s);
closeQuietly(c);
}
return null;
}
No queries are returning, despite the query() method reaching the return results; line. The issue is that the finally block is hanging. I have determined that the closeQuietly(s); is the line that is hanging indefinitely.
The closeQuietly() method in question is as you would expect;
public static void closeQuietly(Statement s) {
try { if(s != null) s.close(); }
catch(Exception e) { MyUtils.logException(e); }
}
Why would this method hang on s.close()? I guess it is something to do with the way I am using C3P0.
My complete C3P0 configuration (almost entirely defaults) can be viewed here -> http://pastebin.com/K8XDdiBg
MyUtils.logException(); looks something like;
public static void logException(Exception e) {
StackTraceElement ste[] = e.getStackTrace();
String message = " !ERROR!: ";
for(int i = 0; i < ste.length; i++) {
if(ste[i].getClassName().contains("packagename")) {
message += String.format("%s at %s:%d", e.toString(), ste[i].getFileName(), ste[i].getLineNumber());
break;
}
}
System.err.println(message);
}
Everything runs smoothly if I remove the closeQuietly(s); line. Both closing the ResultSet and Connection object work without problem - apart from Connection starvation of course.

Multiple and Repetitive AsyncTask in Android

I have two asynctask working with each other. I'm using them for creating Restaurant menu. First web service gets menu's titles from database. Second web service gets items of title from database. I get title data in my first asynctask and item data in my second asynctask.
For example, I have ten menu titles. There are eight items for each title. I execute first asynctask and get all of menu titles. I want to call second asynctask in first asynctask's onPostExecute for get this title's item and add TextView. I have to wait finished every second task for add item respectively.
In short, I need call first AsyncTask and wait finish it. Then send request to second AsyncTask in First AsyncTask. I have to wait every request to finish. How can I wait ?
Here is the my code.
First AsyncTask
public class BaslikDoldurAS extends AsyncTask<String,String[][],String[][]>{
int ParamID;
public BaslikDoldurAS(String ParamID){
this.ParamID=Integer.parseInt(ParamID);
}
#Override
protected String[][] doInBackground(String... params) {
BaslikDoldur(ParamID);
return sonuc;
}
protected void onPostExecute(String[][] sonuc){
for(int i=0;i<baslikCount;i++){
MenuDoldurAS kontrol = new MenuDoldurAS(firma_id,sonuc[2][i]);
kontrol.execute();
}
}
}
my function which is used in first asyncTask
private String[][] BaslikDoldur(Integer ParamID){
PropertyInfo id = new PropertyInfo();
id.name= "id";
id.setValue(ParamID);
id.type = PropertyInfo.INTEGER_CLASS;
SoapObject request = new SoapObject(NAMESPACE, "BaslikDoldur");
request.addProperty(id);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut=request;
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(MenuURL);
androidHttpTransport.debug = true;
try {
androidHttpTransport.call("http://tempuri.org/BaslikDoldur", envelope);
SoapObject response = (SoapObject) envelope.getResponse();
sonuc[2]=new String[response.getPropertyCount()]; //baslik
baslikCount=response.getPropertyCount();
for(int i=0;i<response.getPropertyCount();i++){
Object property = response.getProperty(i);
if(property instanceof SoapObject){
SoapObject menu = (SoapObject) property;
sonuc[2][i] = menu.getProperty("menu_baslik").toString();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return sonuc;
}
Second AsyncTask
public class MenuDoldurAS extends AsyncTask<String,String[][],String[][]>{
int ParamID;
String Baslik;
public MenuDoldurAS(String ParamID,String Baslik){
this.ParamID=Integer.parseInt(ParamID);
this.Baslik=Baslik;
}
#Override
protected String[][] doInBackground(String... params) {
MenuDoldur(ParamID,Baslik);
return sonuc;
}
protected void onPostExecute(String[][] sonuc){
for(int i=0;i<count;i++){
String baslik="";
if(!baslik.equals(sonuc[2][i])){
baslik=sonuc[2][i];
TextView basliktxt = new TextView(Urun.this);
basliktxt.setText(sonuc[2][i]);
basliktxt.setTextSize(20);
basliktxt.setTextColor(Color.RED);
basliktxt.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
urunLayout.addView(basliktxt);
}
else{
TextView aciklamatxt = new TextView(Urun.this);
aciklamatxt.setText(sonuc[3][i]);
aciklamatxt.setTextColor(Color.parseColor("#0c0c7c"));
aciklamatxt.setTextSize(17);
aciklamatxt.setGravity(Gravity.CENTER_VERTICAL | Gravity.CENTER_HORIZONTAL);
urunLayout.addView(aciklamatxt);
}
}
}
}
my function which is used in second asyncTask
private String[][] MenuDoldur(Integer ParamID,String Baslik){
PropertyInfo id = new PropertyInfo();
id.name= "id";
id.setValue(ParamID);
id.type = PropertyInfo.INTEGER_CLASS;
PropertyInfo baslik = new PropertyInfo();
baslik.name= "baslik";
baslik.setValue(Baslik);
baslik.type = PropertyInfo.STRING_CLASS;
SoapObject request = new SoapObject(NAMESPACE, "MenuDoldur");
request.addProperty(id);
SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
envelope.bodyOut=request;
envelope.dotNet = true;
envelope.setOutputSoapObject(request);
HttpTransportSE androidHttpTransport = new HttpTransportSE(MenuURL);
androidHttpTransport.debug = true;
try {
androidHttpTransport.call("http://tempuri.org/MenuDoldur", envelope);
SoapObject response = (SoapObject) envelope.getResponse();
sonuc[3]=new String[response.getPropertyCount()]; //aciklama ve fiyat
count = response.getPropertyCount();
for(int i=0;i<response.getPropertyCount();i++){
Object property = response.getProperty(i);
if(property instanceof SoapObject){
SoapObject menu = (SoapObject) property;
sonuc[3][i] = menu.getProperty("menu_aciklama").toString() + " - " + menu.getProperty("menu_fiyat").toString();
}
}
}
catch (Exception e) {
e.printStackTrace();
}
return sonuc;
}
If you want to wait until all AsyncTasks are done before proceeding, why don't you just put all of you work in doInBackground of the first AsyncTask?
Or you don't want to do this because you want to run the 10 "second tasks" in parallel? (Which, incidentally you're not doing anyway, because you're not using the THREAD_POOL Executor for your tasks.) If this is the case then why not just do something like
// variable accessible to both tasks
ArrayList<AsyncTask> mRunningTasks = new ArrayList<AsyncTask>();
// AsyncTask1
protected void onPostExecute(String[][] sonuc){
for(int i=0;i<baslikCount;i++){
MenuDoldurAS kontrol = new MenuDoldurAS(firma_id,sonuc[2][i]);
mRunningTasks.add(kontrol);
}
for (AsyncTask task : mRunningTasks) {
task.execute();
}
}
// AsyncTask2
protected void onPostExecute(...) {
boolean allComplete = true;
for (AsyncTask task : mRunningTasks) {
if (!task.getStatus().equals(AsyncTask.Status.FINISHED)) {
allComplete = false;
break;
}
}
if (allComplete) {
//do whatever
mRunningTasks.clear();
}
}

Resources