0

I'm tryng to write a simple Java program that runs class every 3 minutes. I am using Timer and TimerTask to call these classes.

import java.io.IOException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

public class Receiver
{
    public static void main(String[] args)
    {
        System.out.println("Time now is -> " + new Date());

        Timer timer = new Timer();
        TimerTask task = new ReceiverTask();
        timer.scheduleAtFixedRate(task, 0, 180000);
    }
}

class DPGReceiverTask extends TimerTask
{
    private ArrayList<TaskArgs> m_tasks = new ArrayList<TaskArgs>();

    public ReceiverTask()
    {
        m_tasks.add(new TaskArgs("com.comp.Receiver", new String[] { "ARG1", "ARG2"}));
    }

    public void run()
    {
        System.out.println("Receiver Started!");

        String classpath = "D:/Receiver;D:/Receiver/lib/*";
        int i = 0;

        ArrayList<Process> processes = new ArrayList<Process>();
        for (TaskArgs task: m_tasks)
        {
            try
            {
                List<String> command = new ArrayList<String>();
                command.add(System.getProperty("java.home") + "/bin/java");
                command.add("-classpath");
                command.add(classpath);
                command.add(task.Name);

                String[] args = task.Args;
                for (String arg : args)
                {
                    command.add(arg);
                }

                Process process = new ProcessBuilder(command).start();
                processes.add(process);

            }
            catch (IOException e)
            {
                e.printStackTrace();
            }
        }
        try
        {
            for (Process process : processes)
            {
                int exitCode = process.waitFor();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
    }
}
public class TaskArgs
{
    public TaskArgs(String name, String[] args)
    {
        Name = name;
        Args = args;
    }

    public String Name;
    public String[] Args;
}

I implemented winsw and created service that runs bat file, who in turn runs java:

receiverTask.bat

java -classpath ReceiverService.jar Receiver

receiverTask.xml

<service>
  <id>receiverTask</id>
  <name>receiverTask</name>
  <description>receiver Service</description>
  <executable>receiverTask.bat</executable>
  <logpath>D:\winsw\logs\Service</logpath>
 <log mode="roll-by-time">
  <pattern>yyyyMMdd</pattern>
    </log>
  <depend>Spooler</depend>
  <startargument>run</startargument>
  <stopargument>stop</stopargument>
</service>

I got two problems with this current implementation:

  1. Regardless of the current java finishing or not, the Timer will start another process in 3 minutes.
  2. Stopping the service (winsw - java service wrapper) doesn't stop the process running java.exe or cmd.exe

I tried adding destroy method to kill the process, but is this really the way to go?

        ...    
        try
        {
            for (Process process : processes)
            {
                Timer t = new Timer();
                TimerTask killer = new TimeoutProcessKiller(process);
                t.schedule(killer, 178000);
                int exitCode = process.waitFor();
                killer.cancel();
            }
        }

public class TimeoutProcessKiller extends TimerTask
{
    private Process p;
    public TimeoutProcessKiller(Process p)
    {
        this.p = p;
    }

    public void run()
    {
        p.destroy();
    }
}

Does anyone have any suggestions?

Angelina
  • 2,175
  • 10
  • 42
  • 82

2 Answers2

1

If you start the process from with in your Java application by calling ProcessBuilder.start() then you have a valid Process reference to it, and you can invoke the destroy() method in Process class to kill that particular process. So, you should traverse your list before creating new processes and destroy its if some exists.

class DPGReceiverTask extends TimerTask {
    private ArrayList<TaskArgs> m_tasks = new ArrayList<TaskArgs>();
    private LinkedList<Process> processes = new LinkedList<Process>();

    public ReceiverTask() {
        m_tasks.add(new TaskArgs("com.comp.Receiver", new String[] { "ARG1", "ARG2"}));
    }

    public void run() {
        // shutdown prev. created processes
        for (Process process: processes) {
            process.destroy();
        }
        processes.clear();

        System.out.println("Receiver Started!");

        String classpath = "D:/Receiver;D:/Receiver/lib/*";
        int i = 0;

        for (TaskArgs task: m_tasks) {
            try {
                List<String> command = new ArrayList<String>();
                command.add(System.getProperty("java.home") + "/bin/java");
                command.add("-classpath");
                command.add(classpath);
                command.add(task.Name);

                String[] args = task.Args;
                for (String arg : args)
                {
                    command.add(arg);
                }

                Process process = new ProcessBuilder(command).start();
                processes.add(process);

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
Daryl
  • 644
  • 1
  • 9
  • 20
  • I edited my question to include winsw config file. Maybe you can give me more detail on where to start :) – Angelina Apr 13 '16 at 18:36
  • 1
    If i understood you correctly you created ReceiverTask instance and scheduled it fixed rate. In your run method you create new processes. After 3 minutes your run method invoked by scheduler and it again creates new processes. But it is not expected because it can starts the same process even the old process is not finished. In order to it will work as expected you should memorize what processes you created and when run method starts call destroy method on it. – Daryl Apr 13 '16 at 19:01
1

Using a Timer is a comparatively crude way to do such scheduling.

The seventh paragraph of that class’ documentation even suggests using alternative described below.

Java 5.0 introduced the java.util.concurrent package … for repeatedly executing tasks at a given rate or delay … a more versatile replacement for the Timer/TimerTask …

Executor

Java 5 and later provides the Executor framework for handling such tasks.

Specifically you want the ScheduledExecutorService. This solves both your problems.

  • The service waits until a pending job completes before executing the next job.
  • The service gives you a ScheduledFuture object by which to check its status and to halt further work. The cancel method lets you specify whether you want to wait until the current job is finished or instead try to interrupt that current job.

Learning

Search Stack Overflow for many examples of using a ScheduledExecutorService.

You will need a little bit of time and effort to wrap your head around the moving parts. But time well spent as the resulting code in your app is a pleasantly simple and elegant way to handle the challenge of managing threaded background jobs.

I would not start your reading here, but later you may find this answer of mine to be helpful, with a fully-working example Vaadin app driven by a ScheduledExecutorService.

Caveat

Beware of the one critical trick: Surround the outside of your Callable/Runnable object's call/run method with a try-catch as any uncaught exception reaching the service causes the service to stop scheduling further jobs. This cease-work feature happens silently. This behavior is a feature, not a bug.

The workaround is simple to catch any Exception (and perhaps any Throwable) in your Callable/Runnable object -- which makes sense anyways, as any uncaught exception reaching that level means something went wrong that you would want to be aware of and edit your code so as to handle properly further down in your implementation.

For more info, see this Question:ScheduledExecutorService Exception handling

Community
  • 1
  • 1
Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154