Caucho Technology
documentation
examples
changes

overview
quick start
installation
command-line
configuration
admin
amber
clustering
caching
database
deployment
ejb 3.0
embedding
filters
hessian
hmtp
ioc
jsp
logging
messaging
performance
quercus/php
remoting
scheduled tasks
security
server push
servlets
third-party
troubleshooting
virtual hosting
watchdog
webapp
xml and xslt

scheduled task


Resin's <scheduled-task> capability lets you schedule events using a flexible cron-style trigger. The task can be any Runnable bean, a method specified by EL, or a URL.

<scheduled-task>

<scheduled-task> schedules a job to be executed at specific times or after specific delays. The times can be specified by a cron syntax or by a simple delay parameter. The job can be either a Runnable bean, a method specified by an EL expression, or a URL.

When specified as an IoC bean, the bean task has full IoC capabilities, including injection, @TransactionAttribute aspects, interception and @Observes.

<scheduled-task> Attributes
ATTRIBUTEDESCRIPTION
classthe classname of the singleton bean to create
crona cron-style scheduling description
delaya simple delay-based execution
initIoC initialization for the bean
mbean-nameoptional MBean name for JMX registration
methodEL expression for a method to be invoked as the task
nameoptional IoC name for registering the task
periodhow often the task should be invoked in simple mode
taskalternate task assignment for predefined beans
<scheduled-task> schema
element scheduled-task {
  class?
  & cron?
  & delay?
  & init?
  & mbean-name?
  & method?
  & name?
  & period?
  & task?
}

bean-style job configuration

The most common and flexible job configuration uses standard IoC bean-style configuration. The bean must implement Runnable. Like the <bean> tag, the class attribute specifies the Runnable class, and any init section configures the bean using Resin IoC configuration.

Example: 5min cron bean task
<web-app xmlns="http://caucho.com/ns/resin">

  <scheduled-task class="qa.MyTask">
    <cron>*/5</cron>
  </scheduled-task>

</web-app>

task reference job configuration

The task bean can also be passed to the <scheduled-task> using a Resin-IoC EL reference. The name of the task bean would be defined previously, either in a <bean> or <component> or picked up by classpath scanning. Like the bean-style job configuration, the reference bean must implement Runnable.

Example: midnight cron bean task
<web-app xmlns="http://caucho.com/ns/resin">

  <scheduled-task task="#{taskBean}">
    <cron>0 0 *</cron>
  </scheduled-task>

</web-app>

method reference job configuration

<scheduled-task> can execute a method on a defined bean as the scheduler's task. The method is specified using EL reference syntax. At each trigger time, <scheduled-task> will invoke the EL method expression.

In the following example, the task invokes myMethod() on the myBean singleton every 1 hour.

Example: 1h period method task
<web-app xmlns="http://caucho.com/ns/resin">

  <bean name="myBean" class="qa.MyBean"/>

  <scheduled-task method="#{myBean.myMethod}">
    <delay>10m</delay>
    <period>1h</period>
  </scheduled-task>

</web-app>

url job configuration

In a <web-app>, the <scheduled-task> can invoke a servlet URL at the trigger times. The task uses the servlet RequestDispatcher and forwards to the specified URL. The URL is relative to the <web-app> which contains the <scheduled-task.

Example: sunday cron url task
<web-app xmlns="http://caucho.com/ns/resin">

  <scheduled-task url="/cron.php">
    <cron>0 15 * * 0</cron>
  </scheduled-task>

</web-app>

cron trigger syntax

Some ascii art from the wikipedia cron entry

cron fields
# +---------------- minute (0 - 59)
# |  +------------- hour (0 - 23)
# |  |  +---------- day of month (1 - 31)
# |  |  |  +------- month (1 - 12)
# |  |  |  |  +---- day of week (0 - 6) (Sunday=0 or 7)
# |  |  |  |  |
  *  *  *  *  *
cron patterns
PATTERNDESCRIPTION
*matches all time periods
15matches the specific time, e.g. 15 for minutes
15,45matches a list of times, e.g. every :15 and :45
*/5matches every n times, e.g. every 5 minutes
1-5matches a range of times, e.g. mon, tue, wed, thu, fri (1-5)

Each field specifies a range of times to be executed. The patterns allowed are:

example ranges
RANGEEXPLANATION (USING MINUTES AS EXAMPLE)
*run every minute
*/5run every 5 minutes
0,5,50run at :00, :05, :50 every hour
0-4run at :00, :01, :02, :03, :04
0-30/2run every 2 minutes for the first half hour

The minutes field is always required, and the hours, days, and months fields are optional.

example times
RANGEEXPLANATION
0 */3run every 3 hours
15 2 *run every day at 0215 local time
0 0 */3run every third day at midnight
15 0 * * 6run every Saturday at 0015

com.caucho.resources.CronResource

Often, applications need to run a task at specific times. The CronResource provides a standard way of doing that. Applications need only create a standard java.lang.Runnable task and configure the CronResource. Resin configure's the work task with bean-style configuration.

Task running every 15 minutes
<web-app xmlns="http://caucho.com/ns/resin">
  <resource type="com.caucho.resources.CronResource">
    <init>
      <cron>*/15</cron>
      <work resin:type="example.PeriodicWork">
        <foo>Custom Config</foo>
      </work>
    </init>
  </resource>
</web-app>
cronSpecifies the times the task should be runrequired
workSpecifies application's work beanrequired

The cron specification follows the Unix crontab format. The cron is composed of 5 fields: minutes, hours, day of month, month, and day of week.

Each field specifies a range of times to be executed. The patterns allowed are:

example ranges
RANGEEXPLANATION (USING MINUTES AS EXAMPLE)
*run every minute
*/5run every 5 minutes
0,5,50run at :00, :05, :50 every hour
0-4run at :00, :01, :02, :03, :04
0-30/2run every 2 minutes for the first half hour

The minutes field is always required, and the hours, days, and months fields are optional.

example times
RANGEEXPLANATION
0 */3run every 3 hours
15 2 *run every day at 0215 local time
0 0 */3run every third day at midnight
15 0 * * 6run every Saturday at 0015

Hello, World example

Example: WEB-INF/web.xml
<web-app xmlns="http://caucho.com/ns/resin">

  <scheduled-task class="example.PeriodicWork">
    <!-- every 5 minutes -->
    <cron>*/5</cron>
  </resource>

</web-app>
Example: WEB-INF/classes/example/PeriodicWork.java

package example;

import java.util.logging.Level;
import java.util.logging.Logger;

public class PeriodicWork implements Runnable {
  static protected final Logger log = 
    Logger.getLogger(PeriodicWork.class.getName());

  public PeriodicWork()
  {
    log.info("PeriodicWork: constructor");
  }

  /**
   * Required implementation of java.lang.Runnable.run()
   */
  public void run()
  {
    log.info("PeriodicWork: run() Hello, World");
  }
}
[13:04:27.429] PeriodicWork: constructor
[13:05:00.095] PeriodicWork: run() Hello, World
[13:10:00.182] PeriodicWork: run() Hello, World

Example: bean-style configuration example

WEB-INF/web.xml
<web-app xmlns="http://caucho.com/ns/resin">

  <scheduled-task class="example.PeriodicWork">
    <!-- every minute -->
    <cron>*</cron>

    <init>
      <message>Goodybye, World</message>
    </init>
  </scheduled-task>

</web-app>
Example: WEB-INF/classes/example/PeriodicWork.java
package example;

import java.util.logging.Level;
import java.util.logging.Logger;
import javax.resource.spi.work.Work;

public class PeriodicWork implements Work {
  static protected final Logger log = 
    Logger.getLogger(PeriodicWork.class.getName());

  String _message;

  public PeriodicWork()
  {
    log.info("PeriodicWork: constructor");
  }

  /**
   * Optional, called in response to presence of <message>
   * configuration tag. 
   */
  public void setMessage(String message)
  {
    log.info("PeriodicWork: setMessage");
    _message = message;
  }

  /**
   * Optional, called after bean is created and any setters
   * from configuration are called.
   */
  @PostConstruct
  public void init()
    throws Exception
  {
    log.info("PeriodicWork: init()");

    if (_message == null)
      throw new Exception("`message' is required");
  }

  /**
   * Required implementation of java.lang.Runnable.run()
   */
  public void run()
  {
    log.info("PeriodicWork: run() " + _message);
  }

  /**
   * Implementation of javax.resource.spi.work.Work.release()
   */
  public void release()
  {
    log.info("PeriodicWork: release()");
  }
}
[13:04:27.429] PeriodicWork: constructor
[13:04:27.429] PeriodicWork: setMessage
[13:04:27.429] PeriodicWork: init()
[13:05:00.095] PeriodicWork: run() Goodbye, World
[13:06:00.182] PeriodicWork: run() Goodbye, World
(close Resin)
[13:06:00.345] PeriodicWork: release()

RMI Resource

The goal of RMI is to provide services to remote clients. A remote client obtains and uses a proxy object that implements an interface. The interface is the contract for the service, it is the definition of the methods that the service provides.

Because the client is using a proxy object, the actual execution of code occurs on the server. A proxy object is placeholder that the client uses to cause execution of code on a server.

Registry

The RMI registry is used to store a list of available services. A client uses the registry to make it's proxy object, and the Registry is responsible for giving appropriate information to the client so that it can hook up with the server that implements the service.

In many scenarios, the Registry and the server for the services are in the same JVM. It is possible, however, for the Registry to run in a different JVM or even on a different machine than the server or servers that implement the services.

A registry has a TCP port that it uses to listen to incoming requests, typically this is port 1099. The RMI registry is a global resource, each JVM can have only one Registry on a particular port. This has important ramifications for the naming of services.

The Hessian alternative

If you are considering RMI as a mechanism for publishing services, you may want to consider using Hessian instead. Hessian offers the following advantages:

  • it does not have a global namespace, separate web-app's can provide services with the same name without conflict
  • it supports the use of clients written in languages other than Java
  • it does not require the manual generation of stubs
  • it does not require a security-manager

More information is available in the Hessian section of the documentation.

Requirement: security-manager

The JDK requires that a security manager be in place for the use of RMI. This is true for both clients and servers. A security manager is enabled in Resin using the security-manager configuration:

enabling security-manager in resin.conf
<resin xmlns="http://caucho.com/ns/resin"
       xmlns:resin="http://caucho.com/ns/resin/core">

  <security-manager/>

  ...

For clients that are applets, the developer does not need to enable the security manager; the browser provides the security manager.

More information is available in the Security section of the documentation.

com.caucho.resources.rmi.RmiRegistry

Resin provides the resource class com.caucho.resources.rmi.RmiRegistry to define an RMI Registry and register services with it. If the Registry is on the `localhost' server, then Resin will also start the RMI Registry if needed.

Example use of RMI Registry
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>localhost</server>  
      <port>1099</port>           

      <rmi-service service-name="HelloWorld" 
                      service-class="example.HelloWorldImpl"/>
      <rmi-service .... >
    </init>
  </resource>
</web-app>

com.caucho.resources.rmi.RmiRegistry

serverthe ip address of the server with the Registrylocalhost
portthe port of the Registry1099
rmi-servicean rmi service (see below) 

RmiRegistry is used to define the location of an RMI Registry to use. If server is `localhost', then the Registry will be started on the specified port, if it has not already been started.

If server is something other than `localhost', then it is assumed that the Registry has been started by some other JVM , and is treated as remote Registry to register any services defined with rmi-server.

rmi-service

child of com.caucho.resources.rmi.RmiRegistry

Each RmiRegistry can have rmi-service children, which causes the service to be instantiated and registered with the RMI Registry defined by the containing RmiRegistry.

service-namethe name of the service, used for registration in the Registry and also used by clients to locate the service.required
server-classthe name of the implementation class for the servicerequired

Implementing a service

Interface and Implementaion

An RMI service requires the developer to create two classes - an interface and an implementation. The interface defines the contract for the service, it is given to the client and it is the client view of the service. The implementation class implements the functionality; it implements the interface and is used on on the server.

The following is a simple hello world example.

interface - WEB-INF/classes/example/HelloWorld.java
package example;

import java.rmi.Remote;
import java.rmi.RemoteException;


public interface HelloWorld extends Remote
{
  public String sayHello()
    throws RemoteException;
}
implementation - WEB-INF/classes/example/HelloWorldImpl.java
package example;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;


public class HelloWorldImpl extends UnicastRemoteObject implements HelloWorld
{
  public HelloWorldImpl()
    throws RemoteException
  {
    super();
  }

  public String sayHello()
    throws RemoteException
  {
    return "Hello, World";
  }
}

Making Stubs

When the client uses a service, it uses a proxy object. The proxy object is a placeholder, it implements the interface defined for the service, and call's through to the server so that the code is executed on the server.

RMI calls proxy objects Stubs, and the stubs must be manually generated. The generation of stubs is done using the rmic tool.

using rmic to generate Stubs
rmic -v1.2 -d WEB-INF/classes/  example.HelloWorldImpl 

This call to rmic will use the file WEB-INF/classes/example/HelloWorldImpl.class to generate the class file WEB-INF/classes/example/HelloWorldImpl_Stub.class.

It is tedious to perform this step manually, an ant build script (as shown in a later section) can be used to expediate the process.

Deploying the service with Resin

Once the work of making an interface, an implementation, and generating a stub is complete, it is a simple process to deploy the service in Resin.

Deploying the service with Resin
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>localhost</server>  
      <port>1099</port>           

      <rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
    </init>
  </resource>
</web-app>

More than once service is easily deployed with the use of multiple rmi-service tags:

Deploying more than once service with Resin
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>localhost</server>  
      <port>1099</port>           

      <rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
      <rmi-service service-name="HelloAgainWorld" service-class="example.HelloAgainWorldImpl"/>
    </init>
  </resource>
</web-app>

Choosing a name

By convention, the name chosen for the service often matches the name of the interface class. For example, if the interface name is "example.HelloWorld" then service-name is "HelloWorld" or even "example.HelloWorld" to match.

The RMI Registry has a global namespace. If two different web-app's try to publish the same service, with the same name, there will be conflicts.

An example build file

An ant build file is useful for completing the rmic step, and for preparing a jar for use by the client. The client jar contains the interfaces and the stubs.

The following build file, placed in /WEB-INF/build, creates the jar file /rmiclient.jar.

WEB-INF/build.xml
<project name="rmiexample" default="dist" basedir=".">

<property file="local.properties"/>
<property file="build.properties"/>
<property environment="env"/> 

<property name="build.compiler.emacs" value="true"/> 
<property name="resin.home" value="${'${'}env.RESIN_HOME}"/> 

<property name="rmiclient.jar" value="../rmiclient.jar"/>

<!-- NOTE: new RMI interfaces must have corresponding entries addeed
   -       in the rmiclient.jar taget
   -->

<path id="compile.classpath">
  <fileset dir="${'${'}resin.home}/lib">
    <include name="**/*.jar" />
  </fileset>
</path>

<target name="init">
  <tstamp/>
</target>

<target name="compile" depends="init">
  <mkdir dir="classes"/>
  <javac classpathref="compile.classpath"
         destdir="classes"
         debug="true">
    <src path="classes"/>
  </javac>
</target>

<target name="rmic" depends="init,compile">
  <rmic base="classes"
        classpathref="compile.classpath"
        includes="**/*Impl.class"/>
</target>


<target name="rmiclient.jar" depends="init,rmic">
  <jar destfile="${'${'}rmiclient.jar}">
    <fileset dir="classes">
      <patternset>
        <include name="**/HelloWorld.class"/>
        <include name="**/*_Stub.class"/>
      </patternset>
    </fileset>
  </jar>
</target>

<target name="dist" depends="rmiclient.jar"/>

</project>

Implementing a client

The client is usually on a different machine, or at least in a different JVM, than the server. That is the point of RMI, it enables the execution of code on a remote machine.

In order to use the RMI service, the client needs the interface classes and the Stubs. The easiest way to provide these to the client is to provide a jar; the ant build file above provides an example of using ant to automate the creation of the jar file for the client.

Once the jar file is available to the client, using the RMI service id fairly simple.

An RMI client
  String server = "//server-with-registry.com:1099/";
  HelloWorld remote = (HelloWorld) Naming.lookup(server + "HelloWorld");

  System.out.println(remote.sayHello());

Scenarios

A Resin server that provides the Registry and the service

In the most common scenario, the Resin server provides both the RMI Registry and the RMI services. When the registry server is defined as `localhost', Resin will start the rmi registry if has not been started already.

This provides a simple method of using RMI, you don't have to worry about the (somewhat tricky) process of starting the rmi registry yourself.

Scenario: a Resin server that provides the Registry and the service
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>localhost</server>
      <port>1099</port>

      <rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
      <rmi-service .... >
    </init>
  </resource>
</web-app>

When the Resin server starts, it will start the rmi registry on port 1099 and register the `HelloWorld' service with it.

A Registry on a different server

In this scenario, the rmi registry is located on the machine services.hogwarts.com. The registry is started with a custom (not Resin) server implemented by Hogwarts.

The requirement is for the HelloWorld service, implemented within a Resin server, to be registered with the remote Registry.

In this scenario, the Resin resource RmiRegistry is used to attach to the existing RMI registry running on services.hogwarts.com.

A Registry on a different server
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>services.hogwarts.com</server>
      <port>1099</port>

      <rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
      <rmi-service .... >
    </init>
  </resource>
</web-app>

When the Resin server starts, it will register the `HelloWorld' service with the RMI Registry on services.hogwarts.com. Since the server is on a remote machine, Resin will not create a registry on the local machine. When the REsin server shuts down, or is restarted, the `HelloWorld' service will be removed from the remote registry.

A Registry in a different JVM

In this scenario, the rmi registry is located on the same machine as the Resin server, but is started with a custom (not Resin) server implemented by Hogwarts.

This is essentially the same scenario as having a Registry on a different szerver. The server name cannot be provided as `localhost', however, because Resin will try to create the RMI registry.

The solution is to use an IP address of `127.0.0.1' as the address of the server. Because the server name is not `localhost', the RMI registry will not be created.

A Registry in a different JVM
<web-app>
  <resource type="com.caucho.resources.rmi.RmiRegistry">
    <init>
      <server>127.0.0.1</server>
      <port>1099</port>

      <rmi-service service-name="HelloWorld" service-class="example.HelloWorldImpl"/>
      <rmi-service .... >
    </init>
  </resource>
</web-app>

When the Resin server starts, it will register the `HelloWorld' service with the RMI Registry on the local machine. Since the server is not `localhost', Resin will not create a registry on the local machine. When the Resin server shuts down, or is restarted, the `HelloWorld' service will be removed from the remote registry.


Copyright © 1998-2008 Caucho Technology, Inc. All rights reserved.
Resin ® is a registered trademark, and Quercustm, Ambertm, and Hessiantm are trademarks of Caucho Technology.