<document>
<localtoc/>

<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.

Attribute Description
class the classname of the singleton bean to create
cron a cron-style scheduling description
delay a simple delay-based execution
init IoC initialization for the bean
mbean-name optional MBean name for JMX registration
method EL expression for a method to be invoked as the task
name optional IoC name for registering the task
period how often the task should be invoked in simple mode
task alternate task assignment for predefined beans
element scheduled-task { class? & cron? & delay? & init? & mbean-name? & method? & name? & period? & task? }

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.

<web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task class="qa.MyTask"> <cron>*/5</cron> </scheduled-task> </web-app>

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`.

<web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task task="#{taskBean}"> <cron>0 0 *</cron> </scheduled-task> </web-app>

<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.

<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>

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.

<web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task url="/cron.php"> <cron>0 15 * * 0</cron> </scheduled-task> </web-app>

Some ascii art from the wikipedia cron entry

# +---------------- minute (0 - 59) # | +------------- hour (0 - 23) # | | +---------- day of month (1 - 31) # | | | +------- month (1 - 12) # | | | | +---- day of week (0 - 6) (Sunday=0 or 7) # | | | | | * * * * *
Pattern Description
* matches all time periods
15 matches the specific time, e.g. 15 for minutes
15,45 matches a list of times, e.g. every :15 and :45
*/5 matches every n times, e.g. every 5 minutes
1-5 matches 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:

range explanation (using minutes as example)
* run every minute
*/5 run every 5 minutes
0,5,50 run at :00, :05, :50 every hour
0-4 run at :00, :01, :02, :03, :04
0-30/2 run every 2 minutes for the first half hour

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

range explanation
0 */3 run every 3 hours
15 2 * run every day at 0215 local time
0 0 */3 run every third day at midnight
15 0 * * 6 run every Saturday at 0015

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.

<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:

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.

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
<web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task class="example.PeriodicWork"> <!-- every 5 minutes --> <cron>*/5</cron> </resource> </web-app> 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"); } } <results> [13:04:27.429] PeriodicWork: constructor [13:05:00.095] PeriodicWork: run() Hello, World [13:10:00.182] PeriodicWork: run() Hello, World </results> <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> 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()"); } } <results> [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() </results>

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.

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.

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.

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 configuration:

<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.

Resin provides the resource class 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.

<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>
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`.

<parents>com.caucho.resources.rmi.RmiRegistry</parents>

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

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.

package example; import java.rmi.Remote; import java.rmi.RemoteException; public interface HelloWorld extends Remote { public String sayHello() throws RemoteException; } 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"; } }

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.

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.

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.

<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:

<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>

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 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`.

<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>

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.

String server = "//server-with-registry.com:1099/"; HelloWorld remote = (HelloWorld) Naming.lookup(server + "HelloWorld"); System.out.println(remote.sayHello());

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.

<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.

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`.

<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.

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.

<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.

</document>