Timer Components
Fabric3 provides a custom component type for implementing timers. Timers are useful for application functionality that is triggered at a certain time or interval. For example, a timer may be used to periodically delete records from a database or send events to a channel.
See the Feature Applications samples for examples of timer components.
Implementing Timer Components
Implementing timer components is similar to implementing Java-based components. References, properties, and resources may be injected and used when the timer is fired. However, since timer components are not invoked by clients, they do not implement a service interface. Rather, they implement java.lang.Runnable. When the timer is fired, its run() method will be invoked. The following is an example of a timer implementation that uses a JPA EntityManager to retrieve a list of records and invoke a notification service:
@Component @Timer(type = TimerType.FIXED_RATE, fixedRate=10000) public class TimedComponent implements Runnable { @PersistenceContext(name="notifyEM") protected EntityManager entityManager; @Reference @Target("NotificationService") protected NotificationService service; public void run() { // use the entityManager to retrieve records ... List<Record> records = (List<Record>) entityManager.createQuery("").getResultList(); // iterate through the records and fire notifications for (Recordsrecord :records) { service.notify; } } }
Timer components can also configured in XML using the implementation.timer element:
<composite xmlns:f3="urn:fabric3.org"...> <component name="TimerComponent"> <f3:implementation.timer class="org.foo.timer.TimedComponent" repeatInterval="10"/> <reference name="service" target="NotificationService"/> </component> </composite>
Error Handling
If an unchecked exception is thrown from the timer component's run()
method, it will not be rescheduled. Timer components must therefore implement error handling for cases where unchecked exceptions should not result in termination of the timer scheduling.
Triggers
Timer components can be configured to trigger in the following ways.
Fixed rate
Fixed rate fires a timer at the specified rate in milliseconds. A best effort will be made to maintain the rate over time. For example, the following timer will fire on average every 10 seconds:
In Java: Â @Timer(type = TimerType.FIXED_RATE, fixedRate=10000) Â Or in XML: Â <f3:implementation.timer class="org.foo.timer.TimedComponent" fixedRate="10000"/>
Repeat interval
Repeat interval fires a timer according to the specified interval in milliseconds. For example, the following configures a timer to be triggered in intervals of 10 seconds:
In Java: @Timer(type = TimerType.INTERVAL, repeatInterval=10000) Or in XML: <f3:implementation.timer class="org.foo.timer.TimedComponent" repeatInterval="10000"/>
Interval Method
A timer implementation class can implement nextInterval(), which will be called by Fabric3 to dynamically determine when the next time a trigger should be fired. This method will be called when a timer is initially scheduled and after each time the run method completes. If the method returns -1, the timer will not be fired again.
To create a create a dynamic firing timer, implement the nextInterval() method according to the following:
@Timer(type = TimerType.RECURRING) public class TransactionalTimedIntervalComponent implements Runnable { public void run() { } public long nextInterval() { return 100; } }
The above component can be configured in XML as follows:
<component name="TrxTimerIntervalComponent"> <f3:implementation.timer class="org.fabric3.tests.timer.TransactionalTimedIntervalComponent"/> </component>
Interval Class
 Note the interval class trigger is deprecated. Use an interval method instead.
A class can be specified which returns when to fire a timer. This can be used to implement a custom trigger algorithm. The trigger class must implement a public method, nextInterval(), which returns a long value indicating the delay in milliseconds before the timer is to be fired next. The trigger class is specified using the intervalClass attribute:
<f3:implementation.timer class="org.foo.timer.TimedComponent" intervalClass="org.fabric3.samples.TestInterval"/>
Fire Once
Fire once triggers the timer once at the specified time in milliseconds. This method of configuring a timer is likely to have limited use. The following shows how to configure a timer to fire once:
In Java: Â @Timer(type = TimerType.ONCE, fireOnce=10000) Â In XML: Â <f3:implementation.timer class="org.foo.timer.TimedComponent" fireOnce="...."/>
Configuring Policy
Timer components may be configured with policy. One common policy used with timer components is transactions. If a managed transaction is configured on a timer component, it will be triggered in the context of a transaction. This is useful if the timer must perform transacted work, such as persisting to a database or enqueing messages. Policy is configured in the same was as any other component via the @ManagedTransaction annotation:
@ManagedTransaction public class TransactionalTimedComponent implements Runnable { public void run() { //.... } }
When the timer run method finishes executing, the transaction will be commited or rolled back.
Timer Pools
Timers are scheduled against a timer pool. This allows fine-grained workload management by varying the number of available threads to execute a group of timers. If no timer pool is specified, the default pool is used.
Timer pools are configured similar to datasources in a composite. The poolName attribute is used to associate a timer component with a pool:
<composite xmlns:f3="urn:fabric3.org"> <f3:timer.pool name="TestPool" size="5"/> <component name="TimerComponent"> <f3:implementation.timer class="org.foo.timer.TimedComponent" poolName="TestPool" repeatInterval="10000"/> </component> </composite>
The above example creates a timer pool with 5 threads.
The number of threads in the default pool can be configured in systemConfig.xml using the <timers> element:
<timers default.pool.Size="5"/>
If not specified, the default pool size is 2.
Timer Scopes
Timer components can specified as stateless, composite, or domain scoped using the SCA @Scope annotation on the timer component class. A new timer component instance will be created when a trigger is fired for a stateless scoped implementation. In contrast, the same instance will be used when a trigger is fired for a composite scoped implementation. Domain scoped timers are described in the next section.
Clustered Singletons and Domain Scoped Timers
Some applications require highly available singleton timers. If a timer component is domain scoped, one and only one instance will exist in a cluster (termed a "zone") at a given time. In other words, a domain scoped timer component is guaranteed to have one instance per cluster (zone) and if the hosting runtime fails, the timer instance will be migrated to another runtime in the cluster (zone). This is different than a composite scoped timer component in that one timer instance per runtime will be created for the latter. Consequently, when deployed to a cluster, a composite scoped timer instance will be active on each runtime.
Implementing domain scoped components is straightforward. The only metadata needed is the SCA @Scope annotation; otherwise, the component configuration is the same. Below is an example of a domain scoped timer component:
@Scope("DOMAIN") public class TimedComponent implements Runnable { public void run() { //... } }
Fabric3 will transparently manage deployment and fail-over migration for domain scoped timers. The activation and failover algorithm relies on the underlying Fabric3 clustering service. When a domain scoped component is deployed to a cluster, its singleton instance will be activated on the cluster leader (each cluster as a dynamically elected leader which is determined by the clustering service; see Chapter 20, "Distributed Domains" for details). If the cluster leader fails, the timer will be migrated to the newly-elected leader and activated.
Using the Scheduled Executor Service
Applications can access the low-level runtime scheduler service to schedule tasks dynamically. The runtime scheduler service implements java.util.concurrent.ScheduledExecutorService. A reference to the runtime scheduler is obtained by using Fabric3 resource injection, as shown in the following code fragment:
import org.fabric3.api.annotations.Resource; import java.util.concurrent.TimeUnit; import java.util.concurrent.ScheduledExecutorService; public class SomeComponent implements SomeService { @Resource protected ScheduledExecutorService executor; public void invoke() { Runnable runnable = //... executor.schedule(runnable, 10000, TimeUnit.MILLISECONDS); } }
Dynamically scheduled tasks are executed using the default timer pool. If scheduling against a different timer pool is required, an application will need to use the Fabric3 SPI timer service interface org.fabric3.timer.spi.TimerService instead of java.util.concurrent.ScheduledExecutorService.