Resin Documentationapp server |
scheduled task
Resin's <scheduled-task> capability lets you schedule
events using a flexible cron-style trigger. The task can be
any <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 When specified as an IoC bean, the bean task has full IoC capabilities, including injection, @TransactionAttribute aspects, interception and @Observes.
element scheduled-task { class? & cron? & delay? & init? & mbean-name? & method? & name? & period? & task? } bean-style job configurationThe most common and flexible job configuration uses standard IoC
bean-style configuration. The bean must implement <web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task class="qa.MyTask"> <cron>*/5</cron> </scheduled-task> </web-app> task reference job configurationThe 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 <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 <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 configurationIn a <web-app>, the <scheduled-task> can invoke a servlet URL
at the trigger times. The task uses the servlet <web-app xmlns="http://caucho.com/ns/resin"> <scheduled-task url="/cron.php"> <cron>0 15 * * 0</cron> </scheduled-task> </web-app> cron trigger syntaxSome 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) # | | | | | * * * * *
Each field specifies a range of times to be executed. The patterns allowed are:
The minutes field is always required, and the hours, days, and months fields are optional.
com.caucho.resources.CronResourceOften, applications need to run a task at specific times. The
<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>
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:
The minutes field is always required, and the hours, days, and months fields are optional.
Hello, World example<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"); } } [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-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()"); } } [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() The goal of RMI is to provide to remote clients. A remote client obtains and uses a that implements an . 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. RegistryThe 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 alternativeIf you are considering RMI as a mechanism for publishing services, you may want to consider using Hessian instead. Hessian offers the following advantages:
More information is available in the Hessian section of the documentation. Requirement: security-managerThe 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: <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.RmiRegistryResin 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> com.caucho.resources.rmi.RmiRegistry
If rmi-servicechild of com.caucho.resources.rmi.RmiRegistry
Each
Implementing a serviceInterface and ImplementaionAn 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"; } } Making StubsWhen the client uses a service, it uses a . 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 rmic -v1.2 -d WEB-INF/classes/ example.HelloWorldImpl
This call to 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 ResinOnce 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 <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 nameBy 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
The following build file, placed in <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 clientThe 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()); ScenariosA Resin server that provides the Registry and the serviceIn 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. A Registry on a different serverIn this scenario, the rmi registry is located on the machine
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 <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 A Registry in a different JVMIn 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.
|