I'm using a recursive method which implements the use of the SwingWorker class to do a research in one folder and all its subfolders - in the local hard drive.
Basically works fine but I'm stuck when I want to stop the SwingWorker method: when the user change the 'source folder' (I'm using a JTree - JAVAFX - to show all the folders in the local hard drive), I want to stop the current 'SwingWorker research' in that folder and start a new one, with the newest 'source path' results choosed from the user.
All the results of the research are stored in a private ObservableList - and updated everytime in the done() method, just by filling one TableView - JavaFX: so, when the user change the 'source path' I have to clean the results of the previous research.
Start method:
private static ObservableList<msg> data = FXCollections.observableArrayList();
private static SwingWorker<Void, Void> worker;
private static String currentFolder;
#Override
public void start(Stage primaryStage) throws Exception {
// TODO Auto-generated method stub
stage = primaryStage;
primaryStage.setScene(new Scene(createContent()));
styleControls();
primaryStage.initStyle(StageStyle.UNDECORATED);
primaryStage.setMaximized(true);
primaryStage.setFullScreen(false);
primaryStage.show();
msgp = new MsgParser();
}
createContent() method- recursive function its called here:
public Parent createContent() {
tree.getSelectionModel().selectedItemProperty().addListener( new ChangeListener<Object>() {
#Override
public void changed(ObservableValue observable, Object oldValue,
Object newValue) {
TreeItem<File> selectedItem = (TreeItem<File>) newValue;
currentFolder = selectedItem.getValue().getAbsolutePath();
// I want to stop here the previous SwingWorker call : the tree
// ChangeListener event is called when the user change the
// source folder of the research, by selecting one TreeItem on it.
if(worker!= null)
worker.cancel(true);
//Here I clean previous results
data.clear();
TV.setItems(data);
//And I call again the method with the new source Folder
ListMail(new File(currentFolder));
}
});
}
ListMail() method: [recursive SwingWorker]
private void ListMail(File dir) {
worker = new SwingWorker<Void, Void>() {
#Override
protected Void doInBackground() throws Exception {
File[] directoryListing = dir.listFiles();
if (directoryListing != null) {
for (File child : directoryListing) {
if(!worker.isCancelled()) {
if(child != null){
if(!child.isDirectory()) {
if(child.getAbsolutePath().substring(child.getAbsolutePath().lastIndexOf('.')+1).equals("msg")) {
Message message = msgp.parseMsg(child.getPath());
String percorsoMail = child.getAbsolutePath().toUpperCase();
if(message != null) {
String fromEmail = message.getFromEmail();
String fromName = message.getFromName();
String subject = message.getSubject();
String received = message.getDate().toString();
String name;
if(fromEmail != null)
name = fromName + "(" + fromEmail + ")";
else name = fromName;
msg Message = new msg(name, subject, received);
if(!data.contains(Message))
data.add(Message);
//I use the Platform.runLater to
// take count of the number of results found
//It updates the GUI - works fine
Platform.runLater(new Runnable() {
#Override public void run() {
if(data != null && data.size() > 0)
setStatusLabel(data.size());
else
setStatusLabel(0);
}
});
}
}
} else {
/**
* Recursive call here : I do the research
* for the subfolders
*/
ListMail(child);
}
} else {
}
}
}
}
return null;
}
// Update GUI Here
protected void done() {
// I refresh here the TableView: works fine on-the-fly added results
TableView.setItems(data);
TableView.refresh();
}
};
//This doesn't do anything
if(!worker.isCancelled())
worker.execute();
}
Basically, the issue is that the SwingWorker thread never stop, I'm thinking because of the recursive calls which creates new pid process at every run or something ?
Also by using a dedicated external button, which I prefer to avoid, gives no results:
refreshBtn.setOnAction(e -> {
//Handle clicks on refreshBtn button
worker.cancel(true);
});
After I click on TreeItem to change source-folder, it just delete all the ObservableList elements created at that moment, but the previous research don't stop.
Everything works fine instead if I wait the research its finished - but this can works only when I'm in a deep-level folder, while I can't obviously wait when the research start with the "C:\" folder.
Ok so that's here how I managed this by using javafx.concurrent.
Just to point my experience with this, it seems using a recursive background Task for potentially long computations, such as scanning the Whole local drive like in my example, it's very memory consuming - also because I stored some results of this background computation in static local variables to access them faster: the result was a data-structure (ObservableList) with over 5000+ instances of a custom class to represent that specific data computed and then the OutOfMemoryError message or the background thread just going like in 'stand-by' without any advice after running for long time (waiting for garbage collection?).
Anyway here's the code that sum up how I solved: the threads are correctly closed. By the way, sometimes, there's a little 'GUI delay' due to cleaning the GUI on the isCancelled() method check: the GUI swing between clear/not clear, because in my opinion it keeps get filled by the results of the previous tasks in the recursion.
private static BackgroundTask backgroundTask;
private static Thread thread;
tree.getSelectionModel().selectedItemProperty().addListener(new ChangeListener<Object>() {
#Override
public void changed(final ObservableValue observable, final Object oldValue, final Object newValue) {
//I close previous running background tasks if there's any
if (backgroundTask != null) {
while (backgroundTask.isRunning()) {
backgroundTask.cancel(true);
// reset GUI nodes here used to show results of the previous thread
}
}
backgroundTask = new BackGoundTask();
thread= new Thread(backgroundTask);
thread.setDaemon(true);
thread.start();
//This will be called only when latest recursion is finished, not at every run
backgroundTask.setOnSucceeded(e -> {});
}
});
BackgroundTask class:
public static class BackgroundTask extends Task<Object> {
// .. variables used by the task here
//constructor: initialize variables at every run of the Task
public BackgroundTask() {
}
#Override
protected Object call() throws Exception {
if (!isCancelled()) {
// ... Do all background work here
Platform.runLater(new Runnable() {
#Override
public void run() {
// GUI progress can goes here
}
});
//recursion here
if(something) {
//...
} else {
call();
}
} else {
//user want to cancel task: clean GUI nodes
}
return null;
}
}
I am trying to provide capability to upload file in my vaadin application
protected Upload questionImageUpload = new Upload("Upload question", questionReceiver);
questionImageUpload.addFinishedListener(new Upload.FinishedListener() {
#Override
public void uploadFinished(Upload.FinishedEvent event) {
boolean hasLock = VaadinSession.getCurrent().hasLock();
button.setEnabled(false);
}
});
But, in my FinishListener.uploadFinished(), if I modify some UI element (in above, I disable a button), the modification does not get applied.
I assumed that this method may be invoked in a non UI thread so I checked whether VaadinSession is available by putting a breakpoint in uploadFinished above. But, VaadinSession.getCurrent() didn't return null. Also hasLock is also true.
What could be the reason?
I am running this vaadin application on Google App Engine (still running locally inside IntelliJ IDEA). Could that be the reason behind this?
File upload is done as a POST request to the server, containing the file data. When the upload is complete, Upload.FinishedListeners are called at the end of that POST request. While all thread locals are set up correctly, this is not a UI update request (or UIDL request) and the response which is sent to the browser only contains a text that informs the browser that the upload finished. Any UI updates done will be queued until another request asks for them.
Because of this, you need to either use #Push, so that the UI changes are pushed immediately to the client through the push channel, or enable polling at the latest when starting the upload, so that the poll request will pick up the UI changes.
I actually accomplish what you want to do with a SuccedListener. I have a code which updates an embedded component with picture uploaded. You can look at the code and take a cue from it. It can also disable the button. You can correct it not optimised but it works
public class PicUploader extends Upload implements SucceededListener, Receiver {
private static final long serialVersionUID = 1L;
File file;
public String fileName;
final String LOCATION = "/home/###";
Embedded image = new Embedded();
TextField field;
public PicUploader(Embedded image, String caption) {
this.image = image;
this.addSucceededListener(this);
this.setReceiver(this);
this.setCaption(caption);
this.setIcon(FontAwesome.UPLOAD);;
}
public PicUploader(Embedded image, TextField field) {
this.image = image;
this.addSucceededListener(this);
this.setReceiver(this);
this.field = field;
this.setButtonCaption(""+FontAwesome.UPLOAD);
this.setIcon(FontAwesome.UPLOAD);;
}
#Override
public OutputStream receiveUpload(String filename, String mimeType) {
// TODO Auto-generated method stub
FileOutputStream stream = null;
try {
file = new File(LOCATION
+ "/Pictures/"
+ System.currentTimeMillis()
+ filename.substring(filename.length() - 4,
filename.length()));
fileName = file.getName();
System.out.println("This is the file name " + filename);
stream = new FileOutputStream(file);
} catch (FileNotFoundException ex) {
ex.printStackTrace();
}
return stream;
}
#Override
public void uploadSucceeded(SucceededEvent event) {
// TODO Auto-generated method stub
image.setSource(new FileResource(file));
image.setVisible(true);
image.setWidth("150");
image.setHeight("200");
// float ratio = image.getHeight()/image.getWidth();
// image.setWidth(""+image.getWidth());
// image.setHeight(""+image.getHeight());
// field.setValue(getFileName());
this.setEnabled(false);
}
public String getFileName() {
return fileName;
}
}
This has been driving me up the wall for 2 days now. I can't seem to make the JProgressBar update properly and I'm not sure why. I've read through the concurrency and SwingWorker tutorials, I've tested outside of my program and been able to get it working fine using simple code. I understand the concepts, and am just not seeing why it's not working as the code has been re-written in many different ways and still I can't make it work. I'll post a SSCE using as much code from my program as I can, and explain things best I can here as well.
Basically I have a auto-updater/Installer program to update and/or install World of Warcraft addons to our customers computers. The first thing the 'updater' does is check the server to see if it's version is the same as the version of the updater installer on the server. This works swimmingly! In fact the entire updater works great except for this one issue. If the updater sees that the server has a different version number than the updater it downloads the installer (in a zip) from the server, unzips it, starts the installation from the exe that was contained in the zip and closes the currently open updater so the install can complete properly.
So I have a progress bar on the card (using cardLayout to show the three different cards for the updater, SelfUpdate, Login, and AddonList) for the self updater. as well as a JTextArea below the JProgressBar and that's all that shows on the particular SelfUpdate card. It query's the server for the version number on the server and it compares that version to the version number on the updater currently installed. If the version is the same it changes to the Login card and moves along to the next bits of the program, which work great so far, if the versions are different it sends a query to the server and the server sends the zip file back to the updater for processing.
This is where it's not working. The zip is a decent size and of course I want to show the progress of this download. The zip file download works fine (it runs in the SwingWorker doInBackground() method), the updater then unzips the installer.exe file to a folder on the customers system drive, fires off using Runtime and starts the installer.exe which pops up the installer screen, then closes the current updater using System.exit(0). again, all this works but the JProgressBar itself will NOT update. it sits at 0% for the whole thing. Also, the taskOutput JTextArea doesn't get updated either, but the main problem is the progressbar not updating. I can live with that getting fixed and the JTextArea not updating. (tried a JLabel in place of a textarea as well and no joy)
So I know I'm doing something wrong, but I can't imagine what. Here's the bits where the magic happens. :) Forgive me if I explain as I go along here.
So Controller.java handles all the flow of the program here's the parts you need to see.
// Imports packages needed for use in this file.
import javax.swing.*;
import java.awt.CardLayout;
import java.io.*;
// class instantiation
public class Controller {
// Variables needed for use in this class
private static Updater util;
private static int loginReturnState = 0;
/**
* Single contructor for this class
*/
public Controller() {
}
/**
* Main method which runes everything in the updater program
*
*/
public static void main(String[] args) {
util = new Updater();
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// setup a new GUI
util.createGUI();
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");
rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);
SelfUpdater selfUp = new SelfUpdater();
int needUpdate = selfUp.checkForUpdate(); if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();
}
}
});
}
}
OK Next file is the Updater class. To put code in here that will hopefully compile and show the issue I need to put this class in there or the utility methods I have in the uPdater class will not be around to be called and I use them extensively.
// Packages needed for use in this file
import javax.swing.*;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.*;
import java.awt.CardLayout;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseAdapter;
import java.io.*;
// Class instantiation
public class Updater {
// These final variables are set to the path for each of the images used
public static final String BG_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\background.png";
public static final String APP_ICON_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\app-icon.png";
public static final String CLOSE_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\close.png";
public static final String FINOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOff.png";
public static final String FINON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\FinishedOn.png";
public static final String ICON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon.png";
public static final String ICON64_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon64x64.png";
public static final String ICON256_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\icon256x256.png";
public static final String INSTOFF_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesOFF.png";
public static final String INSTON_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\InstallUpdatesON.png";
public static final String LOGO_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\logo.png";
public static final String MIN_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\minimize.png";
public static final String PROGBACK_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBackground.png";
public static final String PROGBAR_IMAGE_PATH = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\ProgressBar.png";
public static final String INSTRUCTIONS_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\instructions.png";
public static final String PURCHASE_BUTTON = "F:\\Java Programs\\Updater\\src\\com\\dynastyaddons\\updater\\images\\Purchase.png";
// These are various final variables for use throughout the updater
public static final String URL_CONNECT = "http://dynastyaddons.com/api/index2.php?";
public static final String TITLE_TEXT = "Dynasty Updater";
public static final int BUFFER_SIZE = 4096;
public static final double UPDATER_VERSION = 1.1;
// Public variables needed for this class workings
private static JFileChooser fc;
private static JFrame rootFrame;
// Private variables for this class use alone
private static String PHPSessionID;
private static boolean debugToggle = true;
private CardLayout cards;
private JPanel guiFrame;
private Point mouseDownCompCoords;
private Login login;
private Addons addons;
/**
* Sole contructor. (For invocation by subclass contructors, typically implicit.)
*/
public Updater() {
// First instantiate any variables needed
PHPSessionID = "notset";
guiFrame = new JPanel();
cards = new CardLayout();
guiFrame.setLayout(cards);
}
// Various methods follow
/**
* This will create the root gui for showing in the main method
*
*/
public void createGUI() {
rootFrame = new JFrame();
SpringLayout rootLayout = new SpringLayout();
// setup root frame
rootFrame.setUndecorated(true);
rootFrame.setBackground(new Color(0,0,0,0));
rootFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
rootFrame.setPreferredSize(new Dimension(800,600));
rootFrame.setTitle(TITLE_TEXT);
// add the background image
JLabel bg = new JLabel(new ImageIcon(BG_IMAGE_PATH));
bg.setLayout(rootLayout);
rootFrame.add(bg);
// setup title bar
mouseDownCompCoords = null;
mouseDownCompCoords = null;
rootFrame.addMouseListener(new MouseListener() {
public void mouseReleased(MouseEvent e){ mouseDownCompCoords = null; }
public void mousePressed(MouseEvent e){ mouseDownCompCoords = e.getPoint(); }
public void mouseExited(MouseEvent e){}
public void mouseEntered(MouseEvent e){ }
public void mouseClicked(MouseEvent e){ }
});
rootFrame.addMouseMotionListener(new MouseMotionListener() {
public void mouseMoved(MouseEvent e){}
public void mouseDragged(MouseEvent e){
Point currCoords = e.getLocationOnScreen();
rootFrame.setLocation(currCoords.x - mouseDownCompCoords.x, currCoords.y - mouseDownCompCoords.y);
}
});
// Display and place the logo
JLabel logo = new JLabel(new ImageIcon(LOGO_IMAGE_PATH));
bg.add(logo);
// add Close and Minimize buttons
JPanel cmButt = createCloseAndMinButtons();
bg.add(cmButt);
// create card frame
guiFrame.setPreferredSize(new Dimension(800, 435));
guiFrame.setOpaque(false);
bg.add(guiFrame);
// Constrain the parts of your BG
rootLayout.putConstraint(SpringLayout.WEST, logo, 30, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, logo, 70, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.NORTH, cmButt, 0, SpringLayout.NORTH, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, cmButt, 0, SpringLayout.EAST, rootFrame);
rootLayout.putConstraint(SpringLayout.SOUTH, guiFrame, 0, SpringLayout.SOUTH, rootFrame);
rootLayout.putConstraint(SpringLayout.WEST, guiFrame, 0, SpringLayout.WEST, rootFrame);
rootLayout.putConstraint(SpringLayout.EAST, guiFrame, 0, SpringLayout.EAST, rootFrame);
// Create self updater panel and get it ready to show
SelfUpdater selfUp = new SelfUpdater();
JPanel selfUpd = selfUp.getSelfUpdatePanel();
// get the cardlayout and add the three panels to it, then show the self-updater to begin.
guiFrame.add(selfUpd, "UpdateUpdaterPanel"); }
/**
* Creates a panel which has the close and minimize buttons on it.
*/
private static JPanel createCloseAndMinButtons() {
JPanel a = new JPanel();
SpringLayout closeLO = new SpringLayout();
a.setLayout(closeLO);
a.setPreferredSize(new Dimension(150,150));
a.setOpaque(false);
// Close Button
JButton cButt = new JButton(new ImageIcon(Updater.CLOSE_IMAGE_PATH));
cButt.setBorderPainted(false);
cButt.setContentAreaFilled(false);
cButt.setFocusPainted(false);
cButt.setOpaque(false);
cButt.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) { System.exit(0); }
});
a.add(cButt);
// Minimize Button
JButton mButt = new JButton(new ImageIcon(Updater.MIN_IMAGE_PATH));
mButt.setBorderPainted(false);
mButt.setContentAreaFilled(false);
mButt.setFocusPainted(false);
mButt.setOpaque(false);
mButt.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) { rootFrame.setState(JFrame.ICONIFIED); }
});
a.add(mButt);
// Constrain parts on panel
closeLO.putConstraint(SpringLayout.EAST, cButt, 12, SpringLayout.EAST, a);
closeLO.putConstraint(SpringLayout.NORTH, cButt, -2, SpringLayout.NORTH, a);
closeLO.putConstraint(SpringLayout.EAST, mButt, 30, SpringLayout.WEST, cButt);
closeLO.putConstraint(SpringLayout.NORTH, mButt, -2, SpringLayout.NORTH, a);
// Return the jframe
return a;
}
/**
* Returns the guiFrame which holds the cardlayout for the changing of the panels.
*
* #return JPanel Returns the frame which holds the cardlayout.
*/
public JPanel getCardLayoutFrame() {
return guiFrame;
}
/**
* This will return the cardlayout for use in the controller.
*
* #return CardLayout Returns the cardlayout for use in the Controller.
*/
public CardLayout getCardLayout() { return cards; }
/**
* Returns the current version of this updater as a double
*
* #return the version of this updater as a double.
*/
public double getUpdaterVersion() { return UPDATER_VERSION; }
/**
* Returns a string that tells if the customer is useing windows or mac
*
* #return the operating system in use by the customer
*/
public static String getOSType() {
String retString = "";
String oST = System.getProperty("os.name").toLowerCase();
int spacePos = oST.indexOf(" ");
if (spacePos > 0) {
retString = oST.substring(0, spacePos);
}
retString.trim();
return retString;
}
/**
* Returns the main root frame for display purposes in controller.java
*
* #return JFrame returns rootFrame.
*/
public JFrame getRootFrame() { return rootFrame; }
}
OK< Keep in mind this class really just does some utility stuff in the background and likely isn't involved in anything other than creatin the background gui and tellin the SelfUpdate card to show up.
Finally here's the SelfUpdater.java file which displays the SelfUpdater panel with the progress bar in it and does all the server querying which works fine again. Pay attention to the System.out.println debug statements I have in the downloadUpdate method (which is called in the controller file), as they work fine and print out in the console no problem. The selfUpdateProgress.setValue(int) and taskOutput.setText(txt) statements in that method do nothing .Also I've put a println in the doInBackground where it does setProgress(int) and that prints to the console fine but the bar doesn't update with setProgress. Here it is.
import java.awt.*;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import java.io.*;
import java.net.*;
import java.util.zip.*;
// Create and start class
public class SelfUpdater extends JPanel {
// These are the variables needed for use in this class
private JPanel selfUpdatePanel;
private JProgressBar selfUpdateProgress;
private Task task;
private JTextArea taskOutput;
private Updater util;
/**
* Default class constructor, instantiates variables and sets up GUI.
*
*
**/
public SelfUpdater() {
// Progress Bar
selfUpdateProgress = new JProgressBar(0, 100);
selfUpdateProgress.setValue(0);
selfUpdateProgress.setStringPainted(true);
selfUpdateProgress.setPreferredSize(new Dimension(470,29));
// Layout
SpringLayout selfUpdateLayout = new SpringLayout();
SpringLayout pBarLayout = new SpringLayout();
// Font and color setup for Task Output
Font myFont = new Font("Serif", Font.BOLD, 16);
Color myColor = new Color(255, 170, 0);
// Description of progress pane
taskOutput = new JTextArea("Checking server for updates to the updater.", 1, 39);
taskOutput.setFont(myFont);
taskOutput.setForeground(myColor);
taskOutput.setEditable(false);
taskOutput.setOpaque(false); // this is false after setting up the window.
//extras needed for the class workings
util = new Updater();
// Images for progress bar setup
JLabel pBar = new JLabel(new ImageIcon(util.PROGBACK_IMAGE_PATH));
pBar.setOpaque(false);
//pBar.setLayout(pBarLayout);
//pBar.add(taskOutput);
//pBar.add(selfUpdateProgress);
// Main panel
selfUpdatePanel = new JPanel();
selfUpdatePanel.setPreferredSize(new Dimension(800, 435));
selfUpdatePanel.setLayout(selfUpdateLayout);
selfUpdatePanel.setOpaque(false);
selfUpdatePanel.add(taskOutput);
selfUpdatePanel.add(selfUpdateProgress);
selfUpdatePanel.add(pBar);
// Constrain your bits.
// First constrain PBar to the self update panel
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, pBar, -40, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, pBar, 150, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, taskOutput, -50, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, taskOutput, 175, SpringLayout.WEST, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.SOUTH, selfUpdateProgress, -84, SpringLayout.SOUTH, selfUpdatePanel);
selfUpdateLayout.putConstraint(SpringLayout.WEST, selfUpdateProgress, 171, SpringLayout.WEST, selfUpdatePanel); }
/**
* Will return the jpanel that contains the gui elements for the Self-Update
*
* #return JPanel The Self-Update gui.
*
*/
public JPanel getSelfUpdatePanel() { return selfUpdatePanel; }
/**
* This will check the server to see if an update is available for the updater
*
* #return int Returns an int that will describe the answer from the server.
* Possible Return values:
* 0 = Problem checking for update: Server return NULL
* 1 = Second type of problem checking for update: Server returned empty string
* 2 = Update available.
* 3 = Update not needed.
*
*/
public int checkForUpdate() {
// ask the server what version it's updater is.
String serverUpdaterVersion = "";
try {
HttpConnect conn = new HttpConnect();
String urlRequest = util.URL_CONNECT + "action=get_updater&platform=" + util.getOSType();
serverUpdaterVersion = conn.textRequest(urlRequest, 1);
} catch (MalformedURLException e) {
JOptionPane.showMessageDialog(null, "We were unable to connect to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
} catch (IOException e) {
JOptionPane.showMessageDialog(null, "We were unable to open the connection to the server while checking for an updater update, please check your internet connection.", "Attention!",JOptionPane.ERROR_MESSAGE);
}
// check for errors and return proper int.
if (serverUpdaterVersion == null) { // There is an error if the server returns null, return 0
return 0;
} else if (serverUpdaterVersion.equals("")) { // if the server returns an empty string return 1 for an error
return 1;
} else { // not null or empty string. check versions
// Check version of this updater to servers version
double sVers = Double.parseDouble(serverUpdaterVersion);
if((double)sVers == util.getUpdaterVersion()) { // return 3 if no update needed here.
return 3;
} else { // otherwise return 2 so we can get the update
return 2;
}
}
}
/**
* This will download the update from the server.
*
* #return File The downloaded file from the server. A zip file usually.
*
*/
public void downloadUpdate() {
// turn on wait cursor
util.getRootFrame().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// start a new task which does the download
// Instances of javax.swing.SwingWorker are not reusuable, so
// we create new instances as needed.
task = new Task();
// add a property listener to the task so it knows to update the progress meter.
task.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
switch(evt.getPropertyName()) {
case "progress":// set the progress value and the task output value.
selfUpdateProgress.setIndeterminate(false);
int prg = (Integer) evt.getNewValue();
selfUpdateProgress.setValue(prg);
taskOutput.setText("Downloading and unzipping update. Progress = " + prg + "%");
System.out.println("Percent progress: " + prg + "%");
break;
}
}
});
// execute or start the task.
task.execute();
}
/**
* This method will unzip the downloaded file, and start the installation.
* Then it will close this program while the installation completes.
*
*
*/
public void unzipNewUpdater() {
// create strings for naming the zip file to open up and where to put it.
String zipFile = "NewUpdater.zip";
String outputFolder = System.getenv("SystemDrive") + "\\DynastyAddons";
// Try the unzipping and catch any errors.
try {
// Setup the input stream and get the entry ready to unzip.
ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile));
ZipEntry ze = zis.getNextEntry();
// loop to open each entry and output it to the output folder and file.
while(ze!=null) {
// Get the name
String entryName = ze.getName();
// get the file to unzip
File f = new File(outputFolder + File.separator + entryName);
// Create folders needed to hold the unzipped stuff
f.getParentFile().mkdirs();
// get output stream setup to copy the file to from the zip
FileOutputStream fos = new FileOutputStream(f);
// get length of file to make sure we get the whole thing moved
int len;
byte buffer[] = new byte[1024];
// loop thru the entry writing the whole file to the output directory
while((len = zis.read(buffer)) > 0){
fos.write(buffer, 0, len);
}
// close the output stream and get the next entry ready for the next iteration of the loop.
fos.close();
ze = zis.getNextEntry();
}
// close the entry and input stream since we're done.
zis.closeEntry();
zis.close();
} catch (FileNotFoundException ex) {
// throw error
} catch (IOException ex) {
// throw error
}
}
// This private class handles the download and unzip of the new updater if needed.
class Task extends SwingWorker<Void, Void> {
#Override
public Void doInBackground() {
try {
HttpConnect conn = new HttpConnect();
String downloadURL = util.URL_CONNECT + "action=download_updater&platform=" + util.getOSType();
conn.downloadFile(downloadURL);
InputStream inputStream = conn.getInputStream();
FileOutputStream outputStream = new FileOutputStream(System.getenv("SystemDrive") + "\\DynastyAddons\\NewUpdater.zip");
byte[] buffer = new byte[util.BUFFER_SIZE];
int bytesRead = -1;
long totalBytesRead = 0;
int percentCompleted = 0;
long fileSize = conn.getContentLength();
taskOutput.setText("Downloading Newest Updater.");
//setProgress(0);
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
totalBytesRead += bytesRead;
percentCompleted = (int) (totalBytesRead * 100 / fileSize);
setProgress(percentCompleted);
}
outputStream.close();
conn.disconnect();
} catch (IOException ex) {
JOptionPane.showMessageDialog(util.getRootFrame(), "Error downloading Updater file. Please send this entire error to support#dynastyaddons.com. \n Error: " + ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
setProgress(0);
}
return null;
}
#Override
public void done() {
// Create msg window saying update downloaded.
JOptionPane.showMessageDialog(util.getRootFrame(), "The new updater has been downloaded. \nClick OK to close this updater and start the installation of the new version.", "Attention", JOptionPane.PLAIN_MESSAGE);
// Unzip the updater
unzipNewUpdater();
// Tell the cursor we're not busy anymore
setProgress(100);
selfUpdateProgress.setValue(100);
taskOutput.setText("Done Downloading New Updater. Installing...");
util.getRootFrame().setCursor(null);
// Create command used to open the udpater installer.
String cmd = "cmd /c " + System.getenv("SystemDrive") + "\\DynastyAddons\\InstallDynastyAddonsUpdater.exe";
// Here is where we run the exe that will install the new updater.
try{
Runtime.getRuntime().exec(cmd);
}catch (FileNotFoundException ex){
// handle this error
}catch (IOException ex) {
// handle this error
}
// Now close this updater so the new one can install.
System.exit(0);
}
}
}
I'm hopin it'll all compile properly if you need to see it working. If not let me know and i'll update stuff to make it work on a compile. So I've tried a number of things, making the SelfUpdater class implement PropertyChangeListener and implementing PropertyChange(event), I've tried feeding the JProgressBar to a constructor in the Task class. I've tried outputting an Integer in the SwingWorker and using Publish and Process to update the taskOutput and JProgressBar, I've tried re-writing the whole damn class to fix up the code and make it eaiser to pick out issues. I've done tests using the tutorials and some of my code to make it work (they worked great) but every time I come back to my updater code here, the darn progressbar wont update. I'm really prayin someone can give me a bit of insight on this problem so I can wrap up this project and start on something new! This is the very last thing I have to fix (unless there's problems on the Mac) and I'm done it and can go back to testing and getting it distributed to our customers.
Feel more than free to comment on any part of the code as well (anything I maybe did stupid or something) I'm always lookin for critisim on my code especially if I'm not doing something according to good design and.. ugh brain froze forgot the word here. and definitely if more info is needed I can easily just paste in all the code in the 7 classes that make up this updater.
It all compiles, and works fine, except this jprogressbar wont display the progress, even tho I can get it to pring out in the console. Thank you in advance, sorry for the mini-novel here, Haven't had to ask a question yet, and have been able to figure everything eout from other peoples questions and answers, just stumped now and ready to be done with this! :D
I apologize in advance if I've screwed something up in the post here, remember it's my first post and while I've read hundreds of them, I'm sure my noobness has messed something up so feel free to castigate me for my mistakes so I can fix them. :)
EDIT Updated the selfUpdater.java to reflect using of only one spring layout.
What you have there are layout problems, not anything SwingWorker related. The JProgressBar is updating nicely, you just can't see it.
Check your main method. You are creating two instances of SelfUpdater, adding one to your GUI and updating the other.
public static void main(String[] args) {
util = new Updater(); // <-- don't do this, it calls Swing code from a thread that is not EDT
// Schedule a job for the event dispatch thread.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
// setup a new GUI
util.createGUI(); // <-- creates an instance of SelfUpdater and adds it to GUI
JFrame rFrame = util.getRootFrame();
JPanel gFrame = util.getCardLayoutFrame();
CardLayout cards = (CardLayout) gFrame.getLayout();
cards.show(gFrame, "UpdateUpdaterPanel");
rFrame.pack();
rFrame.setLocationRelativeTo(null);
rFrame.setVisible(true);
SelfUpdater selfUp = new SelfUpdater(); // <-- creates another instance
int needUpdate = selfUp.checkForUpdate();
if (needUpdate == 2) {// Update available for download. Download it.
selfUp.downloadUpdate();
}
}
});
}
You can't expect components to be shown, if they are not added anywhere. :)
Have you tried to put the updating code of the jProgressBar in an extra thread? http://docs.oracle.com/javase/7/docs/api/java/lang/Thread.html
I had a similar problem once, the progressbar didn't progress and when it was all done, the progressbar showed 100%.
I've met a problem and I've been struggling last 2 days. I have a compiled program that runs some simulations and visualizes the results in an SVG file. The file is replaced every 2 seconds with a new one, until the simulation is done.
Wanting to visualize the results, I made a java swing program which uses batik and JSVGCanvas to display the svg file and update it every 2 seconds.
The code I use is:
// In the main part of my code
svgCanvas = new JSVGCanvas();
oneLineInnerPanel.add("Center", svgCanvas);
(new Thread() {
#Override
public void run() {
try {
Thread.sleep(2000);
File f = new File("file_that_shows_simulation_still_running");
while (f.exists()) {
svgReloadButtonActionPerformed(null);
Thread.sleep(2000);
}
} catch (InterruptedException ex) {
Logger.getLogger(testUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
}).start();
// -----------------------------------------------
private void svgReloadButtonActionPerformed(java.awt.event.ActionEvent evt) {
try {
svgCanvas.loadSVGDocument(SVGFile.toURL().toString());
} catch (MalformedURLException ex) {
Logger.getLogger(testUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
It works fine with the exception that every 30-40 updates, it happens that the loadSVGDocument tries to read the document while it's being written by the simulator and thus I get a jdialog error:
XML document structures must start and end within the same entity.
org.apache.batik.dom.util.SAXIOException: XML document structures must start and end within the same entity.
at org.apache.batik.dom.util.SAXDocumentFactory.createDocument(SAXDocumentFactory.java:437)
at org.apache.batik.dom.util.SAXDocumentFactory.createDocument(SAXDocumentFactory.java:349)
at org.apache.batik.dom.svg.SAXSVGDocumentFactory.createDocument(SAXSVGDocumentFactory.java:200)
at org.apache.batik.dom.svg.SAXSVGDocumentFactory.createSVGDocument(SAXSVGDocumentFactory.java:124)
at org.apache.batik.bridge.DocumentLoader.loadDocument(DocumentLoader.java:106)
at org.apache.batik.swing.svg.SVGDocumentLoader.run(SVGDocumentLoader.java:84)
Caused by: org.xml.sax.SAXParseException; systemId: file:/tmp/tempDir9189341730639722289/svgOut.svg; lineNumber: 272; columnNumber: 2; XML document structures must start and end within the same entity.
at org.apache.xerces.parsers.AbstractSAXParser.parse(Unknown Source)
at org.apache.batik.dom.util.SAXDocumentFactory.createDocument(SAXDocumentFactory.java:431)
... 5 more
This doesn't affect the whole procedure, but it's ugly. I get 2-3 of these jdialogs throughout the simulation. I cannot lock the file I/O, because I don't have access to the simulator code. If I lock from java only, the simulator crashes saying it cannot access the file.
What I want is that if there is an error while loading the svg file to somehow catch it internally and not have a jdialog. I can accept missing an update every 30-40 times (60-80 secs).
Now, JSVGCanvas gives you the option to provide a useragent and overwrite the displayError() method to do as you like. I tried that but the dialogs still occur. The problem is that the dialogs are not produced by the svgUserAgent I provide but by an internal BridgeUserAgent:
public JSVGComponent(SVGUserAgent ua, boolean eventsEnabled,
boolean selectableText) {
super(eventsEnabled, selectableText);
svgUserAgent = ua;
userAgent = new BridgeUserAgentWrapper(createUserAgent());
addSVGDocumentLoaderListener((SVGListener)listener);
addGVTTreeBuilderListener((SVGListener)listener);
addSVGLoadEventDispatcherListener((SVGListener)listener);
if (updateOverlay != null)
getOverlays().add(updateOverlay);
}
public void loadSVGDocument(String url) {
String oldURI = null;
if (svgDocument != null) {
oldURI = svgDocument.getURL();
}
final ParsedURL newURI = new ParsedURL(oldURI, url);
stopThenRun(new Runnable() {
public void run() {
String url = newURI.toString();
fragmentIdentifier = newURI.getRef();
loader = new DocumentLoader(userAgent);
nextDocumentLoader = new SVGDocumentLoader(url, loader);
nextDocumentLoader.setPriority(Thread.MIN_PRIORITY);
Iterator it = svgDocumentLoaderListeners.iterator();
while (it.hasNext()) {
nextDocumentLoader.addSVGDocumentLoaderListener
((SVGDocumentLoaderListener)it.next());
}
startDocumentLoader();
}
});
}
Can anyone help me get out of this mess, please?
Thanks in advance!
Finally solved the issue. Extended JSVGCanvas, override the createUserAgent() method to provide a bridgeuseragent of my own. This useragent extends the JSVGCanvas user agent but overrides the displayError methods. Here's the code:
import org.apache.batik.bridge.UserAgent;
import org.apache.batik.swing.JSVGCanvas;
import org.apache.batik.util.XMLConstants;
/**
*
* #author
*/
public class myJSVGCanvas extends JSVGCanvas{
#Override
protected UserAgent createUserAgent() {
return new myCanvasUserAgent();
}
protected class myCanvasUserAgent extends CanvasUserAgent
implements XMLConstants {
/**
* Displays an error message in the User Agent interface.
*/
#Override
public void displayError(String message) {
if (svgUserAgent != null) {
super.displayError(message);
} else {
System.out.println(message);
// JOptionPane pane =
// new JOptionPane(message, JOptionPane.ERROR_MESSAGE);
// JDialog dialog =
// pane.createDialog(myJSVGCanvas.this, "ERROR");
// dialog.setModal(false);
// dialog.setVisible(true); // Safe to be called from any thread
}
}
/**
* Displays an error resulting from the specified Exception.
*/
#Override
public void displayError(Exception ex) {
if (svgUserAgent != null) {
super.displayError(ex);
} else {
ex.printStackTrace();
// JErrorPane pane =
// new JErrorPane(ex, JOptionPane.ERROR_MESSAGE);
// JDialog dialog = pane.createDialog(myJSVGCanvas.this, "ERROR");
// dialog.setModal(false);
// dialog.setVisible(true); // Safe to be called from any thread
}
}
}
}
Hope it helps somebody. If anyone has a better idea for dealing with the problem, than just hiding it, I'd be glad to hear it!
So, I'm trying to write code for a search JTextField that retrieves results from the web (kinda like Google's fancy search tips). The only problem is... probably my lack of programming experience. More specifically, I have it set up to whenever a user hits a key, it sends out a request with the JTextField input through an HttpURLConnection and retrieves the results then updates. But whenever the HttpURLConnection is running, it freezes the program for a couple seconds while it does its thing, which mainly prohibits entering in more characters into the search field if I didn't mind anything else. I've considered using separate threads, but I'm not sure how I would properly interrupt a thread and have it start over using new data while having everything else wait on it but still accepting new data. I've considered using
thread.stop();
to help manage the issue, but from what I've read, that seems taboo (am I right?). Anyways, here's pretty much what I'm using:
public class SearchField extends JTextField implements KeyListener {
public SearchField() {
addkeyListener(this);
}
public void updateData(ArrayList<String results) {
/*Pass the data off the the GUI*/
{
#Override
public void keyTyped(KeyEvent e) {
SearchQuery query = new SearchQuery(this.getText());
updateData(query.getResults());
}
}
public class SearchQuery {
ArrayList<String> results = new ArrayList<String>();
public SearchQuery(String search) {
String search_path = "http://www.whatever.com/" + search;
URL url = new URL(search_path);
conn = (HttpURLConnection) url.openConnection();
conn.addRequestProperty("User-Agent", "Mozilla/4.76");
in = new BufferedReader(
new InputStreamReader(conn.getInputStream()));
StringBuffer buf = new StringBuffer();
int i;
while((i = in.read()) != -1) {
buf.append((char) i);
}
String data = buf.toString();
parse(data);
}
public void parse(String data) {
/*Parse a bunch of JSON, return the results in an array*/
results.add(data);
}
public ArrayList<String> getResults() {
return results;
}
}
I'm just at a loss for how to synchronise recieving continuous input while getting new results for each key stroke and dismissing unfinished connections and their respective data processing when it becomes invalid at the next key stroke.
You are doing the download in the same thread that responds to user events. You need to do the download in another thread, perhaps by making SearchQuery extend Thread and doing the query in the run method. When it comes time to update the text field, you can use SwingUtilities.invokeLater to update the text field. Here’s an example:
public class SearchQuery extends Thread {
private SearchField f;
public SearchQuery(JTextField f) {
this.f = f;
}
public void run() {
// do the query here
SwingUtilities.invokeLater(new Runnable() {
public void run() {
f.updateData(results);
}
});
}
}
Then change the line:
SearchQuery query = new SearchQuery(this.getText());
to this:
SearchQuery query = new SearchQuery(this);