[SNMP4J] (WIP) SNMP4J CommonJ/Spring integration contrib, round 5
Brice Fines
bfines at sermepa.es
Tue Mar 16 19:12:14 CET 2010
Hi again,
I made a TimerFactory based on CommonJ TimerManager.
I include my current code in this post.
It was interesting: SNMP4J relies on java.util.TimerTask, and
java,util.Timer handle all the black magic for TimerTask (change state,
handle stack of TimerTask, ...).
CommonJ API is an alternative to java.util.Timer for J2EE environment (but
it is designed to handle Runnable kind of objects, not TimerTask)
TimerTask is a Runnable ("run"), but it also offers "cancel" and
"scheduledExecutionTime" methods, and no "set...." methods.
Known limitation of current implementation: this CommonJ TimerFactory will
work while the "scheduledExecutionTime" method is not used (i.e. while it
is not called from "run" method in TimerTask as it is supposed to be),
since I have not found a way to integrate this.
(Frank, please can you confirm that scheduledExecutionTime is not used/
not going to be used?)
Since SNMP4J 1.x is based on Java 1.4.1, I guess TimerFactory interface
cannot be modified to use java.util.concurrent.Future instead of
java.util.TimerTask (FutureTask is a java 5 feature, and it is an
interface) (Can we hope to see it in SNMP4J 2.x?).
I tested my code with SNMP4J 1.10.2 and Spring 2.5.6-SEC01 (EAR deployed
in a WAS 6.1).
Everything runs fine so far.
This is a CommonJ TimerFactory, not a Spring/CommonJ TimerFactory: I would
have used the TaskScheduler, but it is available in Spring 3.x, not in
2.5.x, so I use CommonJ.
(you can have a look at
http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/scheduling.html#scheduling-task-scheduler)
This implementation delegate scheduling to a CommonJ TimerManager.
It keeps track of scheduled TimerTasks (to prevent rescheduling). It also
keeps track of generated CommonJ Timers to cancel them when cancelling the
CommonTimer.
Rough equivalents used:
java.util.TimerTask -> java.util.TimerTask -> CommonJ Timer + CommonJ
TimerListener
java.util.Timer -> CommonTimer -> CommonJ
TimerManager
TimerTask that are just executed once are removed from tracking after
execution.
TimerTask that are going to repeat are discarded when CommonTimer is
cancelled.
Comments and suggestion are welcome.
(maybe someone could help me with the synchronized statement, I am not
sure that the double lock is needed, maybe lock on TimerTask Set could be
sufficient)
What do you think of it Frank?
Regards
package org.snmp4j.util.commonj;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.util.CommonTimer;
import org.snmp4j.util.TimerFactory;
import commonj.timers.TimerManager;
/**
* TimerFactory implementation based on CommonJ TimerManager.
*/
public class TimerManagerTimerFactoryImpl implements TimerFactory {
/**
* Logger.
*/
final static LogAdapter LOGGER = LogFactory
.getLogger(TimerManagerTimerFactoryImpl.class);
/**
* TimerManager.
*/
private final TimerManager timerManager;
/**
* Constructor.
*
* @param timerManager
* a TimerManager.
*/
public TimerManagerTimerFactoryImpl(TimerManager timerManager) {
if (timerManager == null) {
throw new NullPointerException("TimerManager is
required");
}
this.timerManager = timerManager;
}
/*
* (without Javadoc)
*
* @see org.snmp4j.util.TimerFactory#createTimer()
*/
public CommonTimer createTimer() {
LOGGER.debug("Creating Timer");
return new TimerManagerCommonTimerImpl(timerManager);
}
}
package org.snmp4j.util.commonj;
import java.util.Collections;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.TimerTask;
import org.snmp4j.log.LogAdapter;
import org.snmp4j.log.LogFactory;
import org.snmp4j.util.CommonTimer;
import commonj.timers.Timer;
import commonj.timers.TimerListener;
import commonj.timers.TimerManager;
/**
* CommonTimer implementation based on CommonJ TimerManager.
*/
public class TimerManagerCommonTimerImpl implements CommonTimer {
/**
* Logger.
*/
final static LogAdapter LOGGER = LogFactory
.getLogger(TimerManagerCommonTimerImpl.class);
/**
* TimerTasks. Set for tracking purpose.
*/
private final Set<TimerTask> timerTasks;
/**
* CommonJ TimerTask wrappers. Set for tracking purpose.
*/
private final Set<CommonJTimerTaskWrapper>
commonJTimerTaskWrappers;
/**
* TimerManager.
*/
private final TimerManager timerManager;
/**
* True if cancelled, false otherwise.
*/
private boolean cancelled;
/**
* Constructor.
*
* @param TimerManager
* a TimerManager.
*/
public TimerManagerCommonTimerImpl(TimerManager timerManager) {
this.timerManager = timerManager;
cancelled = false;
timerTasks = Collections.synchronizedSet(new
HashSet<TimerTask>());
commonJTimerTaskWrappers = Collections
.synchronizedSet(new
HashSet<CommonJTimerTaskWrapper>());
}
/*
* (without Javadoc)
*
* @see org.snmp4j.util.CommonTimer#cancel()
*/
public void cancel() {
synchronized (timerTasks) {
LOGGER.debug("CommonTimer cancel " + this + " with
"
+ commonJTimerTaskWrappers.size()
+ " CommonJ TimerTask wrapper(s)"
);
// we cancel the CommonTimer
cancelled = true;
// we cancel each registered task
synchronized (commonJTimerTaskWrappers) {
for (CommonJTimerTaskWrapper
commonJTimerTaskWrapper : commonJTimerTaskWrappers) {
try {
LOGGER.error("Cancelling
CommonJTimerTaskWrapper "
+
commonJTimerTaskWrapper);
commonJTimerTaskWrapper.
timerTask.cancel();
} catch (RuntimeException
runtimeException) {
runtimeException.printStackTrace(System.err);
LOGGER.error("Error
cancelling TimerTask",
runtimeException);
}
commonJTimerTaskWrapper.timer
.cancel();
}
// we clear resources
timerTasks.clear();
commonJTimerTaskWrappers.clear();
}
}
}
/*
* (without Javadoc)
*
* @see org.snmp4j.util.CommonTimer#schedule(java.util.TimerTask,
long)
*/
public void schedule(TimerTask task, long delay) {
if ((delay < 0l) || (delay + System.currentTimeMillis() <
0l)) {
throw new IllegalArgumentException("Negative
delay.");
}
synchronized (timerTasks) {
registerTimerTask(task);
LOGGER.debug("CommonTimer schedule task: " +
task.toString()
+ " delay: " + delay);
final CommonJTimerTaskWrapper
commonJTimerTaskWrapper = new CommonJTimerTaskWrapper(
task);
commonJTimerTaskWrapper.setTimer(timerManager
.schedule(
commonJTimerTaskWrapper, delay));
synchronized (commonJTimerTaskWrappers) {
commonJTimerTaskWrappers
.add(commonJTimerTaskWrapper);
}
}
}
/*
* (without Javadoc)
*
* @see org.snmp4j.util.CommonTimer#schedule(java.util.TimerTask,
* java.util.Date, long)
*/
public void schedule(TimerTask task, Date firstTime, long period)
{
if (firstTime.getTime() < 0l) {
throw new IllegalArgumentException("Negative
firstTime.");
}
synchronized (timerTasks) {
registerTimerTask(task);
LOGGER.debug("CommonTimer schedule task: " +
task.toString()
+ " firstTime: " +
firstTime.toString() + " period: "
+ period);
final CommonJTimerTaskWrapper
commonJTimerTaskWrapper = new CommonJTimerTaskWrapper(
task);
commonJTimerTaskWrapper.setTimer(timerManager
.schedule(
commonJTimerTaskWrapper,
firstTime, period));
synchronized (commonJTimerTaskWrappers) {
commonJTimerTaskWrappers
.add(commonJTimerTaskWrapper);
}
}
}
/*
* (without Javadoc)
*
* @see org.snmp4j.util.CommonTimer#schedule(java.util.TimerTask,
long,
* long)
*/
public void schedule(TimerTask task, long delay, long period) {
if ((delay < 0l) || (delay + System.currentTimeMillis() <
0l)) {
throw new IllegalArgumentException("Negative
delay.");
}
synchronized (timerTasks) {
registerTimerTask(task);
LOGGER.debug("CommonTimer schedule task: " +
task.toString()
+ " delay: " + delay + " period: "
+ period);
final CommonJTimerTaskWrapper
commonJTimerTaskWrapper = new CommonJTimerTaskWrapper(
task);
commonJTimerTaskWrapper.setTimer(timerManager
.schedule(
commonJTimerTaskWrapper, delay,
period));
synchronized (commonJTimerTaskWrappers) {
commonJTimerTaskWrappers
.add(commonJTimerTaskWrapper);
}
}
}
/**
* Registers a TimerTask.
*
* @param timerTask
* a TimerTask.
* @throws IllegalStateException
* if CommonTimer is cancelled, or TimerTask is
scheduled or
* executed.
*/
private void registerTimerTask(TimerTask timerTask)
throws IllegalStateException {
if (cancelled) {
final String message = "CommonTimer is cancelled,
task "
+ timerTask.toString() + " is not
scheduled.";
LOGGER.debug(message);
throw new IllegalStateException(message);
}
if (!timerTasks.add(timerTask)) {
final String message = "Task " +
timerTask.toString()
+ " is already scheduled or
executed.";
LOGGER.debug(message);
throw new IllegalStateException(message);
}
}
/**
* TimerTask wrapper based on CommonJ.
*/
private class CommonJTimerTaskWrapper implements TimerListener {
/**
* TimerTask.
*/
private TimerTask timerTask;
/**
* Timer.
*/
private Timer timer;
/**
* Constructor.
*
* @param timerTask
* a TimerTask.
*/
public CommonJTimerTaskWrapper(TimerTask timerTask) {
if (timerTask == null) {
throw new NullPointerException("timerTask
is required");
}
this.timerTask = timerTask;
}
/**
* Set Timer.
*
* @param timer
* a Timer.
*/
public void setTimer(Timer timer) {
this.timer = timer;
}
/*
* (without Javadoc)
*
* @see
commonj.timers.TimerListener#timerExpired(commonj.timers.Timer)
*/
public void timerExpired(Timer timer) {
LOGGER.debug("Run wrapped TimerTask: " + timerTask
.toString());
try {
timerTask.run();
} finally {
// should we remove task?
if (timer.getPeriod() == 0l) {
// task is not going to repeat
LOGGER.debug("Removing wrapped
TimerTask: "
+ timerTask
.toString());
// we cancel task
try {
timerTask.cancel();
} finally {
timer.cancel();
// we remove task
synchronized (timerTasks)
{
timerTasks.remove(
this.timerTask);
synchronized (
commonJTimerTaskWrappers) {
commonJTimerTaskWrappers.remove(this);
}
}
}
}
}
}
}
}
Extract from applicacion context:
<jee:jndi-lookup id="timerManager" jndi-name=
"yourTimerManagerReferenceName" />
<bean id="snmp4jTimerFactory"
class=
"org.snmp4j.util.commonj.TimerManagerTimerFactoryImpl">
<constructor-arg ref="timerManager" />
</bean>
More information about the SNMP4J
mailing list