i'm writing a Sftp-client program using Jsch. I am using JProgressBars to display the Progress of Uploads and Downloads. My GUI contains Buttons according to the files in my Working Directory. When i delete a file i updated my gui to give the user a feedback:
void update() {
panel.removeAll();
addToPanel(ls(channelsftp, sftpWorkingDir));
validate();
}
This is using my ls-function to return all Files in the current workingDir. addToPanel will process the lsEntries to output Buttons on the panel. This works great for deleting files. However, i want the same update-function to be called after an upload is completed. Since upload is giving me graphical feedback in the form of a JProgressBar its functionality was moved to a thread:
final JFileChooser uploadChooser = new JFileChooser();
ulo.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
int returnVal = uploadChooser.showOpenDialog(Navigator.this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
String pathToUpload = uploadChooser.getSelectedFile().getAbsolutePath();
Runnable uploadIt = new UploadUtil(pathToUpload, chacha);
new Thread(uploadIt).start();
}
}
});
So after the user clicks on OK in this JFileChooser the upload starts. Here is UploadUtil:
public class UploadUtil implements Runnable{
String paTU;
ChannelSftp csftp;
public UploadUtil(String pathToUl, ChannelSftp chaSftp) {
paTU = pathToUl;
csftp = chaSftp;
}
@Override
public void run() {
try {
csftp.put(paTU, LoginAndFunctions.sftpWorkingDir, new SystemOutProgressMonitor());
} catch (SftpException e) {
Error errorUploading = new Error(e.toString()+"\nUploadpipe closed unexpectedly");
errorUploading.setVisible(true);
}
}
}
SystemOutProgressMonitor is a class that processes datatransfer in Jsch. I tried using Thread.join() and CountDownLatch. Both prevented my JProgressBar from updating. Is there a different solution for it?
A solution is to use a call back method or mechanism. Since this is a Swing problem, use a SwingWorker, not a Runnable in a thread which gives you two potential ways
done()
method is called within the worker on the Swing event thread when it has completed its job.SwingWorker.StateValue.DONE
. Note that you can also add a PropertyChangeListener to the worker thread and listen to its progress property. Within the worker as it's updating your data, update this property by calling setProgress(int value)
with a value from 0 to 100. Then in the listener, update your JProgressBar with this value.
For example (Note that code not tested yet, so sorry if there are errors):
public class UploadUtil extends SwingWorker<Void, Void> {
String paTU;
ChannelSftp csftp;
public UploadUtil(String pathToUl, ChannelSftp chaSftp) {
paTU = pathToUl;
csftp = chaSftp;
}
@Override
public void doInBackground() throws Exception {
try {
csftp.put(paTU, LoginAndFunctions.sftpWorkingDir, new SystemOutProgressMonitor());
} catch (SftpException e) {
Error errorUploading = new Error(e.toString()+"\nUploadpipe closed unexpectedly");
errorUploading.setVisible(true);
}
}
}
A listener for your worker:
class UploadUtilListener implements PropertyChangeListener {
public void propertyChanged(PropertyChangeEvent e) {
if (e.getNewValue() == SwingWorker.StateValue.DONE) {
// do your code here that you want called when worker done
}
}
}
then to use it:
public void actionPerformed(ActionEvent arg0) {
int returnVal = uploadChooser.showOpenDialog(Navigator.this);
if(returnVal == JFileChooser.APPROVE_OPTION) {
String pathToUpload = uploadChooser.getSelectedFile().getAbsolutePath();
UploadUtil uploadIt = new UploadUtil(pathToUpload, chacha);
uploadIt.addPropertyChangeListener(new UploadUtilListener());
uploadIt.execute();
}
}