[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