1

I have a class that implements Actionlistener. Upon clicking the button from this class I want it do something at a precise time I specified, earlier, and elsewhere. So I have made these classes :

class DoSomething implements ActionListener{
     public void actionPerformed(ActionEvent a){
     while(true){
         String test = obtainTime.time;

    if(test.matches("(.*)17:29:30(.*)")){
         Class.doSomethingMethod();
    }
        //Rest of the code
     }
     }
}

class ObtainTime{
     static DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
     static Calendar cal = Calendar.getInstance();
     static String time = dateFormat.format(cal.getTime());
}

So the problem is, I only get the time from when I clicked it. The second thing is the button becomes unclickable since it is still running, is there a way to make it clickable again while the code still runs in the background? Thank you for any help.

andersoj
  • 22,406
  • 7
  • 62
  • 73
John Smith
  • 87
  • 10

4 Answers4

1

If you're doing something quick and/or something that requires you to update a UI when you reach the target time, you want to use a Java Swing Timer. More documentation on this is here: Oracle's Swing Timer tutorial. If you're doing a long-running task, you'll want to follow andersoj's suggestion and use a scheduled executor.

Sbodd
  • 11,279
  • 6
  • 41
  • 42
0

If you want to do several things paralely, you'd have to use threads.. You can do this by extending your class with Thread or implementing Runnable interface Nice tutorial for both ways can be found here.

To get the time & date in formate, that will match your pattern, check this document, where you can find patterns for foramtting date & time in java.

Martin Dvoracek
  • 1,714
  • 6
  • 27
  • 55
  • The format of date and time isn't the problem. The problem is I only get the time of when I have clicked the button, so If I say make it tell me the time within the while loop every second it will print the same time every time. – John Smith Mar 14 '13 at 17:54
  • Can you update your question to be a little more explicit about what "working correctly" looks like? I think you were asking two questions, one about "I only get the time when I clicked it" which I don't know how to interpret; and one about concurrency which @Dworza and I answered. – andersoj Mar 14 '13 at 17:57
  • Ok I see it may have been a little confusing. So what I meant was when I got the obtaintTime.time going at 18:40:45 it printed 18:40:45 every time, and as someone explained it happens because the initializer is static. – John Smith Mar 14 '13 at 19:42
0

Your first question: While it's confusing, I think you are asking why you only get the time as measured by the first time you push the button. The answer is because when you push the button for the first time, you load the ObtainTime class. When you load that class, once and only once, the static initializers run. The line:

static String time = dateFormat.format(cal.getTime());

Is a static initializer. What you probably mean to do is something more like this:

class DoSomething implements ActionListener{

  private final ScheduledExecutor scheduler = executors.newSingleThreadScheduledExecutor();
  private final long timeToRunAction;

   DoSomething(long timeToRunAction) {
     this.timeToRunAction = timeToRunAction;
   }

   public void actionPerformed(ActionEvent a)
   {
     long currentTime = System.currentTimeMillis();
     long delayTime = timeToRunAction - currentTime;
     scheduler.schedule(new Runnable() {
         public void run() { Class.doSomethingMethod(); }
     }, delayTime, TimeUnit.MILLISECONDS);
   }
}

If you use this class, then when you instantiate it you specify the target time in the constructor. You might want this to be more dynamic, that's fine, you just make the timeToRunAction field mutable and provide a way to set it. Times here are specified in milliseconds from the unix epoch. If you need to do some calendar magic to figure out your target time, I have left that out.

Your second question: Instead of rolling your own concurrency, I strongly recommend using ScheduledExecutor.schedule() to do this. You will need to use the calendar to compute the correct delay time, probably in milliseconds, to schedule the activity.

andersoj
  • 22,406
  • 7
  • 62
  • 73
0

Looks like you are using Swing as your GUI API. you should use javax.swing.SwingWorker to achieve what you are looking for. It can be done as follows:

import javax.swing.*;
import java.util.*;
import java.text.*;
import java.awt.*;

public class SWorker extends JFrame
{
    JLabel label ;
    DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
    Calendar cal = Calendar.getInstance();
    static SwingWorker worker;
    public void createAndShowGUI()
    {
        setTitle("SwingWorker Demo");
        label = new JLabel();
        getContentPane().add(label,BorderLayout.NORTH);
        setSize(300,200);
        worker = new SwingWorker<String, String>() 
        {
            @Override
            public String doInBackground() 
            {
               cal = Calendar.getInstance();
               String test = dateFormat.format(cal.getTime());
               while (!(test.matches("(.*)23:41:30(.*)")) && ! isCancelled())
               {
                   publish(new String[] {test});
                    Thread.sleep(5);//Added to sleep the thread for 5 ms so that load could be shed from processor..
                   cal = Calendar.getInstance();
                   test = dateFormat.format(cal.getTime());
               }
               return test;
            }

            @Override
            public void done() //This method is called when doInBackground() finishes. i.e when pattern is matched
            {
                try
                {
                    //Call here the method that you want to call after the time matches.
                    label.setText(get() + " Time's up ...!!");  
                }
                catch (Exception ex){}

            }
            @Override
            public void process(java.util.List<String> chunks)
            {
                label.setText(chunks.get(chunks.size()-1));
            }
        };
        String time = dateFormat.format(cal.getTime());
        label.setText(time);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setVisible(true);
    }
    public void start()
    {
        worker.execute();
    }
    public static void main(String st[])
    {
        SwingUtilities.invokeLater( new Runnable()
        {
            @Override
            public void run()
            {
                SWorker sWorker = new SWorker();
                sWorker.createAndShowGUI();
                sWorker.start();
            }
        });
    }
}

To know more about how to use SwingWorker look at this official tutorial on SwingWorker

Vishal K
  • 12,976
  • 2
  • 27
  • 38
  • you REALLY don't want to run that `while !test.matches()` loop, it'll just chew away at the processor for no good reason. – andersoj Mar 14 '13 at 18:15
  • @andersoj Yeah it will.. But if the user gives some flexibility in choosing the time like `(.*)23:42:(.*)`, then we could happily use `Thread.sleep()` within `doInBackground()` , that will shed the load from processor for sure. – Vishal K Mar 14 '13 at 18:23
  • Yes you could, but the JDK provides nice primitives that are a lot safer and more efficient. See either `Timer` or `ScheduledExecutor`. If you want "sloppy" matching of the time, you can just specify the earliest time you're happy with for either a timer or an executor. – andersoj Mar 14 '13 at 18:26
  • What I have got from Java tutorial (official one) that Whenever the API under consideration is `swing` and some long process has to be executed in `background` then `SwingWorker` is the best alternative for it. If you launch the program given in this tutorial page of oracle http://docs.oracle.com/javase/tutorial/uiswing/concurrency/interim.html , You will see that it too is chewing up the processor violently . So it means , these codes are misleading the programmers ?? . – Vishal K Mar 14 '13 at 18:33
  • SwingWorker is a good and simple choice for activities running in the background of a GUI, particularly if they need to communicate back progress to the GUI. However, it provides no scheduling infrastructure. Using an `Executor` (which SwingWorker uses behind the scenes) is perfectly acceptable as well. The only thing you lose with going to an `Executor` is that you have to be careful if you call back to the UI, you'll need to make sure you get back on the EDT, probably with `SwingUtilities.invokeLater()` – andersoj Mar 14 '13 at 18:37
  • See http://stackoverflow.com/questions/9909932/is-running-a-executorservice-inside-a-swingworker-a-good-practice and http://stackoverflow.com/questions/8779923/differences-between-swingworker-and-executor – andersoj Mar 14 '13 at 18:38
  • yeah..but this EDT is already being wrapped up by `SwingWorker`. It is designed for GUI updation in EDT by itself. We need not to worry about the concurrency issue in this case. If someone is using `Executor` then he/she must be careful to update UI in EDT , that is like extra burden on programmer. Moreover, correct me if I am wrong.. `ScheduledExecutor` internally also uses a kind of while(true) loop which is sent to sleep state for some amount of time i.es via `delayTime` parameter as you have mentioned in your code.Which I too will use in `doInBackground` if some flexibility in time match – Vishal K Mar 14 '13 at 18:44
  • Everything you say is correct. In my view, the burden of doing the EDT stuff manually is lower than the burden of getting `sleep()` conditionals and possible interrupts, etc... right. But that may be a matter of taste. Note also you could wrap the `ScheduledExecutor` inside the `SwingWorker` and get the best of both worlds. – andersoj Mar 14 '13 at 18:46
  • And on point of the original question, the author was NOT calling back in the GUI, he was scheduling some random static method from another class, so the EDT isn't a concern for this specific question. – andersoj Mar 14 '13 at 18:47
  • @andersoj Yeah.. Perhaps OP has nothing to do with GUI or perhaps he/she has to (because the functionality of `Class.doSomethingMethod();` isn't clear here.). If Nothing has to be done with GUI then I would surely go for `Executor` as you have mentioned in your post .. Else ... I think i still need to dig further more in understanding the `SwingWorker` utility in combination with `ScheduledExecutor` as you have mentioned in previous comment. – Vishal K Mar 14 '13 at 18:56