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
The done() method is called within the worker on the Swing event thread when it has completed its job.
Or add a PropertyChangeListener to the SwingWorker, listen to the "state" property, and react when the new value is 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();
}
}
Related
so I have the following problem, I want to make a minigame on a text channel, the problem is, I want to create some sort of timeout so that people don't create multiple "listenerAdapter" instances that will just overload the bot.
the command I use to load my game event (ListenerAdapter is as follows).
#Override
public void handle(List<String> args, GuildMessageReceivedEvent event) {
// TODO Auto-generated method stub
TextChannel channel = event.getChannel();
channel.sendMessage("please type \"joingame\" to join! ").queue();
event.getJDA().addEventListener(new MinigameEvent(channel, event.getAuthor(), event));
}
then , the code I use for loading players in, is the following:
public class MinigameEvent extends ListenerAdapter {
private final long channelId, authorId;
private final int players=3;
private ArraySet<User> users;
private String textMsg;
private Message target;
private GuildMessageReceivedEvent outTimerEvent;
private boolean cancelEvent;
public MinigameEvent(MessageChannel channel, User author, GuildMessageReceivedEvent outTimerEvent) {
this.channelId = channel.getIdLong();
this.authorId = author.getIdLong();
this.outTimerEvent=outTimerEvent;
cancelEvent=false;
this.timeOut(channel);
users=new ArraySet<User>();
users.add(author);
textMsg=("registered users: "+author.getName());
channel.sendMessage(textMsg).queue((new Consumer<Message>()
{
#Override
public void accept(Message t)
{
target = t;
}
}));
}
#Override
public void onMessageReceived(MessageReceivedEvent event) {
if(event.getAuthor().isBot()) {
return;
}
//not respond on other channels
if (event.getChannel().getIdLong() != channelId) {
return;
}
MessageChannel channel = event.getChannel();
String content = event.getMessage().getContentRaw();
if(content.equalsIgnoreCase("joingame")) {
users.add(event.getAuthor());
textMsg=textMsg+", "+event.getAuthor().getName();
target.editMessage(textMsg).queue();
if(users.size()==players) {
event.getChannel().sendMessage("starting").queue();
event.getJDA().removeEventListener(this);
}
}
if(content.equalsIgnoreCase("cancel") && event.getAuthor().getIdLong()==authorId) {
cancelEvent=true;
event.getJDA().removeEventListener(this);
event.getChannel().sendMessage("this game has been canceled").queue();
}
}
private void timeOut(MessageChannel channel) {
Timer timer = new Timer();
TimerTask cooldown = new TimerTask() {
#Override
public void run() {
if(cancelEvent) {
return;
}
if(users.size()<players) {
outTimerEvent.getJDA().removeEventListener(this);
try {
destroyEvent();
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
channel.sendMessage("not enough players, the game has been cancelled").queue();
}else {
return;
}
}
};
timer.schedule(cooldown, 10000L);
}
private void destroyEvent() throws Throwable {
this.finalize();
}
}
when I get to 3 people, the Listener adapter stops working as intended, also when the author of the event (the one who used the !minigame command) types cancel. but when the timer goes off, it sends the message indicating the game has been cancelled, but the listener adapter is still running, if someone tries to join after, it will allow him to do so.
I currently solved the issue by using the finalize method, but I thought that you could just do something like event.getJDA().removeEventListener(this);
Your problem is that your this refers to the nearest class declaration. In this case this is the anonymous class created by your new TimeTask() { .... To refer to the outer class which actually is registered as the listener you have to use MinigameEvent.this instead.
Read More
I would highly recommend using a lambda expression instead which doesn't have this problem. Another thing to note is your use of timer which will result in thread leaks because they are never shutdown by your code (How to cleanup a timer). Even better would be to use a single ScheduledExecutorService which you should re-use to schedule everything you need rather than creating a new one for every task. This can then be shutdown once your process ends (like the onShutdown event in JDA which is fired when shutdown() is called).
I have made a basic chat application in java using eclipse. I am now starting to add extra features to it and am currently stuck on a feature that tells the user when the other person is typing, similar to whatsapp and facebook messenger.
currently i have an integer that records if the user is typing
public int typing = 0;
when it is 0 the user isn't typing when it is 1 they are (a boolean wouldn't work for some reason)
I have an action listener on the textbox that listens for a caret update and excecutes this code:
isTyping = 1;
String typing = ("t-");
client.send(typing.getBytes());
The server then relays this back to the other clients and when they recieve this message that gets sent if they are not typing it will make the someone is typing label appear.
What i would like is something to listen for when the caret is not updating to execute this code:
isTyping = 0;
String typing = ("n-");
client.send(typing.getBytes());
Is this possible or is there a way to make this work as i seem to need to listen for no carat update?
I suggest avoiding the listener and creating a thread:
The created thread checks the value of textbox and remembers the current value of the textbox in a loop. If the value hasn't changed since the last check, it means that the user is not typing. It is up to you to consider frequency of the check and maybe only a length of the value could be used for the check.
Make a single “expiration” Timer that waits a short delay, and then executes your “not typing” action. Whenever the text field’s document changes, restart the Timer, to ensure it only manages to execute when there is a lull in the user’s typing:
JTextField textField = /* ... */;
ActionListener idleSender = new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
isTyping = false;
client.send("n-".getBytes(StandardCharsets.UTF_8));
}
};
int delay = 2000; // 2 seconds
final Timer sendTimer = new Timer(delay, idleSender);
sendTimer.setRepeats(false);
sendTimer.start();
textField.getDocument().addDocumentListener(new DocumentListener() {
#Override
public void insertUpdate(DocumentEvent event) {
sendTimer.restart();
}
#Override
public void removeUpdate(DocumentEvent event) {
sendTimer.restart();
}
#Override
public void changedUpdate(DocumentEvent event) {
sendTimer.restart();
}
});
Some notes:
It is important to use javax.swing.Timer, not java.util.Timer. The latter uses its own thread, while the former always executes its task on the AWT Event Dispatch Thread. Calling (almost) any AWT or Swing method on any thread other than the EDT is not allowed, and while violating that rule may not generate an exception, things tend to break intermittently and unpredictably.
Using typing.getBytes() without passing an charset to getBytes() may cause data corruption on the other side. It will convert bytes using the underlying system’s default charset, which may not be the same as the server’s default charset. It is a good idea to use "n-".getBytes(StandardCharsets.UTF_8) instead.
I don’t know what “a boolean wouldn’t work for some reason” actually means, but booleans work perfectly in all circumstances. If you had a problem, you will be doing yourself a service by finding out what that problem is and fixing it, rather than writing peculiar code that sidesteps the issue, only to come back to it months later and wonder why you are using 0 and 1 in place of false and true.
Performing a command while an action isn't happening isn't really possible, because it doesn't answer one crucial question - how often should it happen? Always isn't really an answer - that would require an infinite loop constantly executing, which will throttle your application as a whole.
That said, you can set up a timed delay for sending a notification that the person has stopped editing. In my mind it would count down (via thread sleep) towards 0 and refresh to a set (positive) amount whenever a key is pressed, but it could be the opposite as well (as AJ suggests in the comments).
public class NotificationSender {
private boolean isEditing;
private final Object isEditingLock;
private DelayedTurnOffThread turnOffThread;
private static final long MS_TO_OFF_NOTIFICATION = 1000;
public NotificationSender() {
isEditing = false;
isEditingLock = new Object();
turnOffThread = null;
}
private void sendEditingNotification(String newContent) {
System.out.println("Editing, new content=" + newContent);
}
private void sendStopEditingNotification() {
System.out.println("Editing stopped");
}
public boolean isEditing() {
synchronized (isEditingLock) {
return isEditing;
}
}
public void doEdit(String newContent) {
synchronized (isEditingLock) {
isEditing = true;
sendEditingNotification(newContent);
if (turnOffThread != null) {
turnOffThread.interrupt();
}
turnOffThread = new DelayedTurnOffThread();
turnOffThread.start();
}
}
private class DelayedTurnOffThread extends Thread {
#Override
public void run() {
try {
Thread.sleep(MS_TO_OFF_NOTIFICATION);
synchronized (isEditingLock) {
isEditing = false;
sendStopEditingNotification();
}
} catch (InterruptedException e) {
//Do nothing - superceded by other turnoff thread
}
}
}
//
//DEMO CODE BELOW
//
private static class NotificationDemo extends JFrame {
private NotificationSender notificationSender;
public NotificationDemo() {
notificationSender = new NotificationSender();
JTextField textField = new JTextField();
getContentPane().add(textField, BorderLayout.CENTER);
textField.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
notificationSender.doEdit(((JTextField)e.getSource()).getText() + e.getKeyChar());
}
#Override public void keyPressed(KeyEvent e) {}
#Override public void keyReleased(KeyEvent e) {}
});
pack();
setLocationRelativeTo(null);
setVisible(true);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
}
public static void main(String[] args) {
new NotificationDemo();
}
}
I am new to java
I have a function runner inside PhotoPoster class (PhotoPoster class is a Jframe with lots of buttons and other GUI elements)
it contains a START button when I click on START it executes the runner function which runs an activity & thats takes a very long time
and other GUI components are not accessible unless the runner functions completes.
I want this to work in separate thread or any other solution to help me
what I currently do is
I have made a another class GuiWorker.java
public class GuiWorker extends SwingWorker<Integer, Integer>{
public GuiWorker() throws IOException {
}
protected Integer doInBackground() throws Exception {
PhotoPoster photoPoster = new PhotoPoster();
photoPoster.ruuner();
return 0;
}
protected void done() {
System.out.println("done");
}
}
PhotoPoster.java
on button click
private void jButton4ActionPerformed(java.awt.event.ActionEvent evt) {
// PhotoPoster photoPoster = new PhotoPoster();
//ruuner();
EventQueue.invokeLater( new Runnable() {
#Override
public void run() {
try {
new GuiWorker().execute();
} catch (IOException ex) {
Logger.getLogger(PhotoPoster.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
}
it gives system out done but not performing runner function activity
Please Help me to sort out this error or any other solution
It looks as though you are queuing your long running thread back onto the Event Dispatch Thread, which means that even though you are doing invokeLater it will still block the thread.
You need to use a SwingWorker.
I am sending a file over network using sockets. The file is received properly without any problem. But now I am using a JProgressBar to show percentage of file sent. My problem is that even when I update GUI in a separate thread, the progress bar is updated only when file is completely sent. I also tried adjusting the priority of main thread to Thread.MIN_PRIORITY but the problem still persisted.
The complete code is long so I am not posting it (I will post if someone asks). The short code for sending file and updating progress bar is
final double temp=(done/fileSize)*100; // percentage of file sent
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
jpb.setString("Completed : "+temp+" %");
jpb.setValue((int)temp); // this is executed only when 100%
}
catch(Exception e)
{
e.printStackTrace();
}
}
});
System.out.println(temp); // only this and
bos.write(b,0,read); // this statement is executed
Problem lies in below line:
final double temp=(done/fileSize)*100; // percentage of file sent
If done and fileSize are both not double then result of done/fileSize is 0.0.
Make them double (at least one of them) to keep the decimal part of the division.
Here is a implementation that I talked about.
It's not the best design as I did it quick and dirty but this way the file transfer code is not dependent on the UI.
public class FileTransfer implements Runnable
{
double transferPercent = 0.0;
double getTransferPercent(){ return transferPercent; }
#Override
public void run()
{
while(transferingFile)
{
// Write data
// Update transferPercent
}
}
}
public class UIClass extends TimerTask
{
private FileTransfer fileTransfer;
public void createUI()
{
TimerTask myClass = this;
JButton b = new JButton("Transfer");
b.addMouseListener(new MouseAdapter()
{
#Override
public void mouseClicked(MouseEvent e)
{
fileTransfer.start();
Timer t = new Timer();
t.scheduleAtFixedRate(myClass, 0.0, 20);
}
});
}
// Update the UI here!
#Override
public void run()
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
jpb.setValue(fileTransfer.getTransferPercent());
}
});
}
}
I'd probably design this differently. I'd make the network code independent of the UI and have it just update a variable on percentage sent.
The UI would then poll this number with a timer to update the progress bar.
But your code should work. Try adding an #Override on your run function. Maybe SwingUltities.invokeLater is calling Runnable's run function instead of your own.
I am developing a swing applciation. In that I have a workflow of jobs to be done.
I Am running these jobs in a for loop one after the other. The interesting thing is I have to update GUI status bar with the current job name running.
I can not use SwingUtilities.invokeAndWait as it can not run on the dispatch thread which will be the currently running thread.
I tried using SwingWorker since the jobs are running in a loop, the SwingWorker's doBackGrount() method will execute and will come out and gets the the next index to run the next job. In the done() of SwingWorker I have written code to update GUI with the status.
public class TestAction extends SwingWorker<Boolean, Void> {
boolean executeThread = false;
public TestAction() {
}
#Override
protected Boolean doInBackground() throws Exception {
executeThread = ExecuteWebServiceAction.webServiceExecution();
return executeThread;
}
#Override
protected void done() {
try {
boolean isOver = (boolean) get();
if (isOver) {
MainApplication.getInstance().getFrame().setStatus(LangUtil.getString("cdsExecuteFinehed")
+ " " + ((WebServiceTool) DrawingManager.getInstance().getCurrentTool()).getName());
FrameMain.jPanel6.repaint();
}
} catch (Interr`enter code here`uptedException ex) {
Logger.getLogger(TestAction.class.getName()).log(Level.SEVERE, null, ex);
} catch (ExecutionException ex) {
Logger.getLogger(TestAction.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
And this is where am calling TestAction:
if (!WorkFlow.isIsWorkflow()) {
SwingUtilities.invokeLater(
new Runnable() {
#Override
public void run() {
webServiceExecution();
}
});
} else {
new TestAction().execute();
}
running in a loop one after the other and notify UI when one is done
sounds like "big job with intermediate results". Intermediate results are supported via the publish/process methods:
implement doInBackground to loop through the jobs and call publish when one is terminated
implement process to do the ui update
you can use Thread.currentThread().sleep(5000); in SwingWorker's doInBackground method before currentthread finishes its execution and update your UI
You could add a Runnable to your constructor to be run when done() is over:
public class TestAction extends SwingWorker<Boolean, Void> {
boolean executeThread = false;
private final Runnable runWhenDone;
public TestAction(Runnable runWhenDone) {
this.runWhenDone = runWhenDone;
}
//...
#Override
protected void done() {
try {
boolean isOver = (boolean) get();
if (isOver) {
MainApplication.getInstance().getFrame().setStatus(LangUtil.getString("cdsExecuteFinehed")
+ " " + ((WebServiceTool) DrawingManager.getInstance().getCurrentTool()).getName());
//Run the Runnable here
runWhenDone.run();
//...
And in your GUI class
Runnable r = new Runnable() {public void run() {updateTheTitle();}};
(new TestAction(r)).execute();
private void updateTheTitle() { yourTitle.setText("I am done");}