Defining a background Job to run periodically

One may need to run some task periodically in the background in Eclipse. This background task should run automatically and shouldn’t block user from regular usage of the application. My proposal for accomplishing this is using Eclipse Jobs API and org.eclipse.ui.startup extension-point.

  1. Adding org.eclipse.ui.startup extension:
    • Dependencies:
      • org.eclipse.ui
      • org.eclipse.core.runtime
    • Extension:
      <?xml version="1.0" encoding="UTF-8"?>
      <?eclipse version="3.2"?>
      <plugin>
         <extension point="org.eclipse.ui.startup">
            <startup class="tk.urbas.eclipse.urbanlife.Startup"></startup>
         </extension>
      </plugin>
  2. Implementing Job:
    package tk.urbas.eclipse.urbanlife;
    
    import org.eclipse.core.runtime.IProgressMonitor;
    import org.eclipse.core.runtime.IStatus;
    import org.eclipse.core.runtime.Status;
    import org.eclipse.core.runtime.jobs.Job;
    
    public class RefreshDataJob extends Job {
    
        public RefreshDataJob(String name) {
            super(name);
        }
    
        @Override
        protected IStatus run(IProgressMonitor monitor) {
            // Work to do in the background
            return Status.OK_STATUS;
        }
    }
  3. Scheduling Job to run periodically:
    package tk.urbas.eclipse.urbanlife;
    
    import org.eclipse.core.runtime.jobs.IJobChangeEvent;
    import org.eclipse.core.runtime.jobs.Job;
    import org.eclipse.core.runtime.jobs.JobChangeAdapter;
    import org.eclipse.ui.IStartup;
    
    public class Startup implements IStartup {
    
        private static final long STARTUP_DELAY = 5000; // 5 seconds delay for first run
        protected static final long JOB_INTERVAL = 60000; // Job should run every 60 seconds
    
        public void earlyStartup() {
            final Job updateJob = new RefreshDataJob("Refreshing data in the background");
            updateJob.schedule(STARTUP_DELAY);
            updateJob.addJobChangeListener(new JobChangeAdapter() {
                @Override
                public void done(IJobChangeEvent event) {
                    super.done(event);
                    updateJob.schedule(JOB_INTERVAL);
                }
            });
        }
    }

Let me know if you have any better solution for implementing this. All improvements, suggestions and comments will be greatly appreciated.

10 comments

  1. I would strongly discourage using (and at work, be discouraged to use) early startup for this purpose for four reasons: one, because it delays the IDE startup, a solution that does not scale when implemented by a large number if plug-ins; two, because you can’t stop the job when the IDE shuts down; and three, you can’t reference your plug-in or any other plug-in until it too has started

    Instead I would use the plug-in’s lifecycle methods start() and stop() to control when the job starts and stops. Make the job a private member of the plug-in and everything plays well together.

    Like

  2. Robert – that’s very good suggestion, thanks! The job lifecycle should be mantained by start()/stop() methods of plug-in’s Activator. But I still want this bundle to activate automatically (it is supposed to be independent, no one other bundle is supposed to reference it) – is there any better option than org.eclipse.ui.startup extension-point for this?

    Like

  3. @Radoslaw, @Antoine’s suggestion is a good one. One wrinkle to consider: if starting your plug-in incurs a long time cost either because it has to start other bundles (that take a long time to start) or because it loads a lot of information, consider splitting your job’s plug-in from the rest of your plug-in.

    My concern is that making your plug-in non-lazy per @Antoine’s suggestion also suffers from being not scalable. If you plan to publish your plug-in for other people to use, while you may think your plug-in is important enough to start early, your user may disagree.

    Like

  4. @Robert – basically I was looking for Eclipse way of running some background task periodically.
    All concerns regarding startup time are extremely important, but if the requirement is to do something at startup (or pretty close to startup) I don’t see the obvious effective solution for this. The suggestion regarding making the plug-in defining the job as tiny (including declared dependencies – direct ones and transitive) as possible to minimize the delay is good one. There is still one open question for me: what’s the advantage or disadvantage of using org.eclipse.ui.startup extension comparing to defining non-lazy bundle?

    Like

  5. To reschedule the job, it is possible to do that in the run-method.

    @Override
    protected IStatus run(IProgressMonitor monitor) {
    try{
    // Work to do in the background
    }
    finally{
    this.schedule(delay);
    }

    return Status.OK_STATUS;
    }

    So in my eyes, you don´t need the jobchangelistener…

    Like

  6. Implementing IStartup no longer slows down IDE startup. In fact, the workbench is visible before the IStartups begin to run. In fact, the IStartups are actually run within a Job. (Jobs do not run until after the workbench has been restored. The JobManager is suspended until that time)

    If you scheduled your job within your Plugin.start() method, it will only run when your plugin is activated. This can be far too late in some cases (depending on what your background job does.) That is why the IStartup extension point exists. One caveat on IStartup. Your IStartup extension is *not* guaranteed to run, because the user could have disabled it in the preferences. So you should take care that proper functioning of your plugin is not dependent on that IStartup extension.

    Last note. You do not need a IJobChangedListener for periodic rescheduling. You can just call “schedule()” from within your job. This will cause your job to automatically reschedule itself after it runs.

    However, if a user “cancels” the sleeping job, you will not get a chance to run (or to reschedule) yourself. In that case, you would still need a IJobChangedListener.

    Like

  7. Hi!

    I like the idea of using a bundle’s lifecycle methods start() and stop() for starting and stopping a “job” controlled by some other bundle.
    But a bundle can only be started once in a time. So if I have the use case that a job can be run any number of times this solution won’t work, because a bundle can only be started once until it is stopped, right?!

    Which possibilities would I have for such an use-case?!?

    Like

  8. I sent Min’s comment about IStartup to some colleagues and here was a reply:

    The point is that enabling your module to run it early during startup
    will also enable all its dependencies and it does not scale well. The
    push is to make everything turned on on demand. A couple of (3rd
    party) plugins with their IStartups may effectively ruin the effort to
    make the product lazily initialized.

    This comment does not bring any convincing argument. If UI is displayed but there is a couple of threads running on background it is likely that the application will be unresponsive (thread priorities do
    not help much).

    Like

Leave a comment