Commits

Anonymous committed 24485fa

some cleanups prior for eclipse.scada migration

  • Participants
  • Parent commits 7f6f94f
  • Branches 1.2

Comments (0)

Files changed (8)

org.openscada.protocol.common/META-INF/MANIFEST.MF

 Bundle-SymbolicName: org.openscada.protocol.common
 Bundle-Version: 1.2.0.qualifier
 Bundle-Vendor: openSCADA.org
-Export-Package: org.openscada.protocol.common;version="1.2.0"
+Export-Package: org.openscada.protocol.common;version="1.2.0";uses:="org.apache.mina.core.session,org.apache.mina.core.filterchain,org.apache.mina.core.write",
+ org.openscada.protocol.common.io;version="1.2.0";uses:="org.apache.mina.core.session,org.apache.mina.core.filterchain,org.apache.mina.filter.codec"
 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
 Import-Package: org.apache.mina.core.filterchain;version="2.0.7",
+ org.apache.mina.core.service;version="2.0.7",
  org.apache.mina.core.session;version="2.0.7",
  org.apache.mina.core.write;version="2.0.7",
- org.eclipse.scada.core.info;version="0.1.0"
+ org.apache.mina.filter.codec;version="2.0.7",
+ org.eclipse.scada.core.info;version="0.1.0",
+ org.slf4j;version="1.7.2"

org.openscada.protocol.common/src/org/openscada/protocol/common/Checksum.java

+package org.openscada.protocol.common;
+
+public class Checksum
+{
+    private final static int[] table = { /* */
+    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, /* */
+    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, /* */
+    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, /* */
+    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, /* */
+    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, /* */
+    0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, /* */
+    0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, /* */
+    0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, /* */
+    0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, /* */
+    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, /* */
+    0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, /* */
+    0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, /* */
+    0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, /* */
+    0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, /* */
+    0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, /* */
+    0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, /* */
+    0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, /* */
+    0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, /* */
+    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, /* */
+    0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, /* */
+    0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, /* */
+    0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, /* */
+    0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, /* */
+    0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, /* */
+    0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, /* */
+    0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, /* */
+    0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, /* */
+    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, /* */
+    0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, /* */
+    0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, /* */
+    0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, /* */
+    0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, /* */
+    };
+
+    public static int crc16 ( final byte[] data )
+    {
+        return crc16 ( data, 0, data.length );
+    }
+
+    public static int crc16 ( final byte[] data, final int start, final int length )
+    {
+        int crc = 0xffff;
+        for ( int i = start; i < start + length; i++ )
+        {
+            crc = crc >>> 8 ^ table[ ( crc ^ data[i] ) & 0xff];
+        }
+        return crc;
+    }
+
+    public static byte lrc ( final byte[] data )
+    {
+        byte lrc = 0;
+        for ( final byte b : data )
+        {
+            lrc += b;
+        }
+        return (byte)-lrc;
+    }
+}

org.openscada.protocol.common/src/org/openscada/protocol/common/io/TimedEndDecoder.java

+/*
+ * This file is part of the openSCADA project
+ * 
+ * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
+ * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de)
+ *
+ * openSCADA is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * openSCADA is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with openSCADA. If not, see
+ * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
+ */
+
+package org.openscada.protocol.common.io;
+
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.mina.core.filterchain.IoFilter.NextFilter;
+import org.apache.mina.core.session.IoSession;
+import org.apache.mina.filter.codec.ProtocolDecoder;
+import org.apache.mina.filter.codec.ProtocolDecoderOutput;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * this implements a {@link ProtocolDecoder} which detects a frame end
+ * based on the absence of new data (timeout).
+ * <p>
+ * Some protocol use a timeout to detect a frame end. e.g. Modbus defines a
+ * <q>end-of-frame</q> as a timeout of x milliseconds in which no data is sent.
+ * This decoder helps to implement such protocols.
+ * <p>
+ * In order to use it you will need to derive your class from this one and
+ * implement {@link #timeout(IoSession, ProtocolDecoderOutput)} which will be
+ * called in the case of the timeout. The timeout detect will be started by
+ * calling {@link #tick(IoSession, ProtocolDecoderOutput)}. It will remember the
+ * output and pass it later on to the
+ * {@link #timeout(IoSession, ProtocolDecoderOutput)} call. So when you read
+ * data in your
+ * {@link ProtocolDecoder#decode(IoSession, org.apache.mina.common.ByteBuffer, ProtocolDecoderOutput)}
+ * you simply call {@link #tick(IoSession, ProtocolDecoderOutput)} and the
+ * timeout is started or pushed back if it was already started.
+ * <p>
+ * If you need to clear the timeout use {@link #clear(IoSession)}
+ * 
+ * @author Jens Reimann
+ */
+public abstract class TimedEndDecoder implements ProtocolDecoder
+{
+    private final static Logger logger = LoggerFactory.getLogger ( TimedEndDecoder.class );
+
+    private static final String CONTEXT = "timedEndContext";
+
+    private final Set<Context> contextSet = new CopyOnWriteArraySet<Context> ();
+
+    private final ScheduledExecutorService scheduler;
+
+    private ScheduledFuture<?> job;
+
+    private final long timeout;
+
+    protected NextFilter nextFilter;
+
+    /**
+     * Create a new instance
+     * 
+     * @param scheduler
+     *            the scheduler to use for cyclic jobs
+     * @param timeout
+     *            the timeout in milliseconds for the end-of-frame detection
+     */
+    public TimedEndDecoder ( final ScheduledExecutorService scheduler, final long timeout, final TimeUnit timeUnit )
+    {
+        this.scheduler = scheduler;
+        this.timeout = TimeUnit.MILLISECONDS.convert ( timeout, timeUnit );
+        logger.debug ( "Running with a timeout of {} ms", this.timeout );
+    }
+
+    private synchronized void addJob ()
+    {
+        logger.trace ( "Request add job" );
+        if ( this.job == null )
+        {
+            logger.trace ( "Added job" );
+            this.job = this.scheduler.scheduleAtFixedRate ( new Runnable () {
+
+                @Override
+                public void run ()
+                {
+                    tick ();
+                }
+            }, this.timeout, this.timeout, TimeUnit.MILLISECONDS );
+        }
+    }
+
+    private synchronized void removeJob ()
+    {
+        logger.trace ( "Request remove job" );
+        if ( this.job != null )
+        {
+            logger.trace ( "Removed job" );
+            this.job.cancel ( false );
+            this.job = null;
+        }
+    }
+
+    protected void tick ()
+    {
+        logger.trace ( "Checking contexts" );
+
+        int i = 0;
+
+        // check each registered context object
+        for ( final Context ctx : this.contextSet )
+        {
+            ctx.check ();
+            i++;
+        }
+
+        logger.trace ( "Checked {}", i );
+    }
+
+    @Override
+    public void dispose ( final IoSession session ) throws Exception
+    {
+        // remove the context from the session and kill the cyclic check
+        final Context ctx = (Context)session.getAttribute ( CONTEXT );
+        if ( ctx != null )
+        {
+            unregisterContext ( ctx );
+            ctx.dispose ();
+            session.removeAttribute ( CONTEXT );
+        }
+    }
+
+    /**
+     * Register a context and start the cyclic job if needed
+     * 
+     * @param ctx
+     *            the context to add
+     */
+    private synchronized void registerContext ( final Context ctx )
+    {
+        logger.trace ( "Register context: {}", ctx );
+
+        this.contextSet.add ( ctx );
+        if ( this.contextSet.size () == 1 )
+        {
+            // start thread
+            addJob ();
+        }
+    }
+
+    /**
+     * Unregister a context and stop the cyclic job if needed
+     * 
+     * @param ctx
+     *            the context to remove
+     */
+    private synchronized void unregisterContext ( final Context ctx )
+    {
+        logger.trace ( "Unregister context: {}", ctx );
+
+        this.contextSet.remove ( ctx );
+        if ( this.contextSet.isEmpty () )
+        {
+            // stop thread
+            removeJob ();
+        }
+    }
+
+    /**
+     * Handle the timeout situation
+     * 
+     * @param session
+     *            the session for which the timeout occurred
+     * @param out
+     *            the output
+     */
+    public abstract void timeout ( IoSession session, ProtocolDecoderOutput out ) throws Exception;
+
+    private void wrapTimeout ( final IoSession session, final ProtocolDecoderOutput out )
+    {
+        try
+        {
+            timeout ( session, out );
+        }
+        catch ( final Throwable e )
+        {
+            try
+            {
+                session.getHandler ().exceptionCaught ( session, e );
+            }
+            catch ( final Throwable ee )
+            {
+                logger.warn ( "Exception was thrown during handling Exception", ee );
+            }
+        }
+    }
+
+    /**
+     * Start or tick the end-of-frame detection for this session
+     * 
+     * @param session
+     *            the session
+     * @param out
+     *            the protocol decoder output to use for the finished frame
+     */
+    public void tick ( final IoSession session, final ProtocolDecoderOutput out )
+    {
+        logger.trace ( "Ticking for session: {}", session );
+        getTimedContext ( session, true ).tick ( out );
+    }
+
+    /**
+     * Clear the end-of-frame detection for this session
+     * 
+     * @param session
+     *            the session
+     */
+    public void clear ( final IoSession session )
+    {
+        logger.trace ( "Clear for session: {}", session );
+        final Context ctx = getTimedContext ( session, false );
+        if ( ctx != null )
+        {
+            ctx.clear ();
+            unregisterContext ( ctx );
+            session.removeAttribute ( CONTEXT );
+        }
+    }
+
+    /**
+     * Get the context for a session
+     * 
+     * @param session
+     *            the session
+     * @return the context
+     */
+    private Context getTimedContext ( final IoSession session, final boolean create )
+    {
+        Context ctx = (Context)session.getAttribute ( CONTEXT );
+        if ( ctx == null && create )
+        {
+            logger.trace ( "Creating contect for: {}", session );
+            ctx = new Context ( this, this.timeout, session );
+            registerContext ( ctx );
+            session.setAttribute ( CONTEXT, ctx );
+        }
+        return ctx;
+    }
+
+    private class Context
+    {
+        private Long lastData = null;
+
+        private final TimedEndDecoder decoder;
+
+        private final long timeout;
+
+        private final IoSession session;
+
+        private boolean disposed = false;
+
+        private ProtocolDecoderOutput out;
+
+        public Context ( final TimedEndDecoder decoder, final long timeout, final IoSession session )
+        {
+            this.decoder = decoder;
+            this.timeout = timeout;
+            this.session = session;
+        }
+
+        public synchronized void tick ( final ProtocolDecoderOutput out )
+        {
+            this.lastData = System.currentTimeMillis ();
+            this.out = out;
+        }
+
+        public synchronized void clear ()
+        {
+            this.lastData = null;
+            this.out = null;
+        }
+
+        public synchronized void check ()
+        {
+            if ( this.disposed )
+            {
+                return;
+            }
+
+            if ( this.lastData == null )
+            {
+                return;
+            }
+
+            if ( System.currentTimeMillis () - this.lastData > this.timeout )
+            {
+                final ProtocolDecoderOutput out = this.out;
+                TimedEndDecoder.this.clear ( this.session );
+                this.decoder.wrapTimeout ( this.session, out );
+            }
+        }
+
+        public synchronized void dispose ()
+        {
+            this.disposed = true;
+        }
+    }
+
+    public synchronized void setNextFilter ( final NextFilter nextFilter )
+    {
+        this.nextFilter = nextFilter;
+    }
+
+}

org.openscada.protocol.modbus/META-INF/MANIFEST.MF

  org.apache.mina.core.session;version="2.0.7",
  org.apache.mina.core.write;version="2.0.7",
  org.apache.mina.filter.codec;version="2.0.7",
+ org.openscada.protocol.common;version="1.2.0",
+ org.openscada.protocol.common.io;version="1.2.0",
  org.slf4j;version="1.7.2"

org.openscada.protocol.modbus/src/org/openscada/protocol/modbus/Checksum.java

-package org.openscada.protocol.modbus;
-
-public class Checksum
-{
-    final static int[] table = { /* */
-    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241, /* */
-    0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440, /* */
-    0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40, /* */
-    0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841, /* */
-    0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40, /* */
-    0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41, /* */
-    0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641, /* */
-    0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040, /* */
-    0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240, /* */
-    0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441, /* */
-    0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41, /* */
-    0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840, /* */
-    0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41, /* */
-    0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40, /* */
-    0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640, /* */
-    0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041, /* */
-    0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240, /* */
-    0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441, /* */
-    0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41, /* */
-    0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840, /* */
-    0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41, /* */
-    0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40, /* */
-    0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640, /* */
-    0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041, /* */
-    0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241, /* */
-    0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440, /* */
-    0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40, /* */
-    0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841, /* */
-    0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40, /* */
-    0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41, /* */
-    0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641, /* */
-    0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040, /* */
-    };
-
-    public static int crc16 ( final byte[] data )
-    {
-        return crc16 ( data, 0, data.length );
-    }
-
-    public static int crc16 ( final byte[] data, final int start, final int length )
-    {
-        int crc = 0xffff;
-        for ( int i = start; i < start + length; i++ )
-        {
-            crc = crc >>> 8 ^ table[ ( crc ^ data[i] ) & 0xff];
-        }
-        return crc;
-    }
-
-    public static byte lrc ( final byte[] data )
-    {
-        byte lrc = 0;
-        for ( final byte b : data )
-        {
-            lrc += b;
-        }
-        return (byte)-lrc;
-    }
-}

org.openscada.protocol.modbus/src/org/openscada/protocol/modbus/codec/ModbusRtuDecoder.java

 import org.apache.mina.core.filterchain.IoFilter.NextFilter;
 import org.apache.mina.core.session.IoSession;
 import org.apache.mina.filter.codec.ProtocolDecoderOutput;
-import org.openscada.protocol.modbus.Checksum;
+import org.openscada.protocol.common.Checksum;
+import org.openscada.protocol.common.io.TimedEndDecoder;
 import org.openscada.protocol.modbus.Constants;
 import org.openscada.protocol.modbus.io.ChecksumProtocolException;
 import org.openscada.protocol.modbus.io.ModbusProtocolError;
-import org.openscada.protocol.modbus.io.TimedEndDecoder;
 import org.openscada.protocol.modbus.message.Pdu;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

org.openscada.protocol.modbus/src/org/openscada/protocol/modbus/codec/ModbusRtuEncoder.java

 import org.apache.mina.core.session.IoSession;
 import org.apache.mina.filter.codec.ProtocolEncoderAdapter;
 import org.apache.mina.filter.codec.ProtocolEncoderOutput;
-import org.openscada.protocol.modbus.Checksum;
+import org.openscada.protocol.common.Checksum;
 import org.openscada.protocol.modbus.message.Pdu;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;

org.openscada.protocol.modbus/src/org/openscada/protocol/modbus/io/TimedEndDecoder.java

-/*
- * This file is part of the openSCADA project
- * 
- * Copyright (C) 2006-2010 TH4 SYSTEMS GmbH (http://th4-systems.com)
- * Copyright (C) 2013 Jens Reimann (ctron@dentrassi.de)
- *
- * openSCADA is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License version 3
- * only, as published by the Free Software Foundation.
- *
- * openSCADA is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU Lesser General Public License version 3 for more details
- * (a copy is included in the LICENSE file that accompanied this code).
- *
- * You should have received a copy of the GNU Lesser General Public License
- * version 3 along with openSCADA. If not, see
- * <http://opensource.org/licenses/lgpl-3.0.html> for a copy of the LGPLv3 License.
- */
-
-package org.openscada.protocol.modbus.io;
-
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-import java.util.concurrent.ScheduledExecutorService;
-import java.util.concurrent.ScheduledFuture;
-import java.util.concurrent.TimeUnit;
-
-import org.apache.mina.core.filterchain.IoFilter.NextFilter;
-import org.apache.mina.core.session.IoSession;
-import org.apache.mina.filter.codec.ProtocolDecoder;
-import org.apache.mina.filter.codec.ProtocolDecoderOutput;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * this implements a {@link ProtocolDecoder} which detects a frame end
- * based on the absence of new data (timeout).
- * <p>
- * Some protocol use a timeout to detect a frame end. e.g. Modbus defines a
- * <q>end-of-frame</q> as a timeout of x milliseconds in which no data is sent.
- * This decoder helps to implement such protocols.
- * <p>
- * In order to use it you will need to derive your class from this one and
- * implement {@link #timeout(IoSession, ProtocolDecoderOutput)} which will be
- * called in the case of the timeout. The timeout detect will be started by
- * calling {@link #tick(IoSession, ProtocolDecoderOutput)}. It will remember the
- * output and pass it later on to the
- * {@link #timeout(IoSession, ProtocolDecoderOutput)} call. So when you read
- * data in your
- * {@link ProtocolDecoder#decode(IoSession, org.apache.mina.common.ByteBuffer, ProtocolDecoderOutput)}
- * you simply call {@link #tick(IoSession, ProtocolDecoderOutput)} and the
- * timeout is started or pushed back if it was already started.
- * <p>
- * If you need to clear the timeout use {@link #clear(IoSession)}
- * 
- * @author Jens Reimann
- */
-public abstract class TimedEndDecoder implements ProtocolDecoder
-{
-    private final static Logger logger = LoggerFactory.getLogger ( TimedEndDecoder.class );
-
-    private static final String CONTEXT = "timedEndContext";
-
-    private final Set<Context> contextSet = new CopyOnWriteArraySet<Context> ();
-
-    private final ScheduledExecutorService scheduler;
-
-    private ScheduledFuture<?> job;
-
-    private final long timeout;
-
-    protected NextFilter nextFilter;
-
-    /**
-     * Create a new instance
-     * 
-     * @param scheduler
-     *            the scheduler to use for cyclic jobs
-     * @param timeout
-     *            the timeout in milliseconds for the end-of-frame detection
-     */
-    public TimedEndDecoder ( final ScheduledExecutorService scheduler, final long timeout, final TimeUnit timeUnit )
-    {
-        this.scheduler = scheduler;
-        this.timeout = TimeUnit.MILLISECONDS.convert ( timeout, timeUnit );
-        logger.debug ( "Running with a timeout of {} ms", this.timeout );
-    }
-
-    private synchronized void addJob ()
-    {
-        logger.trace ( "Request add job" );
-        if ( this.job == null )
-        {
-            logger.trace ( "Added job" );
-            this.job = this.scheduler.scheduleAtFixedRate ( new Runnable () {
-
-                @Override
-                public void run ()
-                {
-                    tick ();
-                }
-            }, this.timeout, this.timeout, TimeUnit.MILLISECONDS );
-        }
-    }
-
-    private synchronized void removeJob ()
-    {
-        logger.trace ( "Request remove job" );
-        if ( this.job != null )
-        {
-            logger.trace ( "Removed job" );
-            this.job.cancel ( false );
-            this.job = null;
-        }
-    }
-
-    protected void tick ()
-    {
-        logger.trace ( "Checking contexts" );
-
-        int i = 0;
-
-        // check each registered context object
-        for ( final Context ctx : this.contextSet )
-        {
-            ctx.check ();
-            i++;
-        }
-
-        logger.trace ( "Checked {}", i );
-    }
-
-    @Override
-    public void dispose ( final IoSession session ) throws Exception
-    {
-        // remove the context from the session and kill the cyclic check
-        final Context ctx = (Context)session.getAttribute ( CONTEXT );
-        if ( ctx != null )
-        {
-            unregisterContext ( ctx );
-            ctx.dispose ();
-            session.removeAttribute ( CONTEXT );
-        }
-    }
-
-    /**
-     * Register a context and start the cyclic job if needed
-     * 
-     * @param ctx
-     *            the context to add
-     */
-    private synchronized void registerContext ( final Context ctx )
-    {
-        logger.trace ( "Register context: {}", ctx );
-
-        this.contextSet.add ( ctx );
-        if ( this.contextSet.size () == 1 )
-        {
-            // start thread
-            addJob ();
-        }
-    }
-
-    /**
-     * Unregister a context and stop the cyclic job if needed
-     * 
-     * @param ctx
-     *            the context to remove
-     */
-    private synchronized void unregisterContext ( final Context ctx )
-    {
-        logger.trace ( "Unregister context: {}", ctx );
-
-        this.contextSet.remove ( ctx );
-        if ( this.contextSet.isEmpty () )
-        {
-            // stop thread
-            removeJob ();
-        }
-    }
-
-    /**
-     * Handle the timeout situation
-     * 
-     * @param session
-     *            the session for which the timeout occurred
-     * @param out
-     *            the output
-     */
-    public abstract void timeout ( IoSession session, ProtocolDecoderOutput out ) throws Exception;
-
-    private void wrapTimeout ( final IoSession session, final ProtocolDecoderOutput out )
-    {
-        try
-        {
-            timeout ( session, out );
-        }
-        catch ( final Throwable e )
-        {
-            try
-            {
-                session.getHandler ().exceptionCaught ( session, e );
-            }
-            catch ( final Throwable ee )
-            {
-                logger.warn ( "Exception was thrown during handling Exception", ee );
-            }
-        }
-    }
-
-    /**
-     * Start or tick the end-of-frame detection for this session
-     * 
-     * @param session
-     *            the session
-     * @param out
-     *            the protocol decoder output to use for the finished frame
-     */
-    public void tick ( final IoSession session, final ProtocolDecoderOutput out )
-    {
-        logger.trace ( "Ticking for session: {}", session );
-        getTimedContext ( session, true ).tick ( out );
-    }
-
-    /**
-     * Clear the end-of-frame detection for this session
-     * 
-     * @param session
-     *            the session
-     */
-    public void clear ( final IoSession session )
-    {
-        logger.trace ( "Clear for session: {}", session );
-        final Context ctx = getTimedContext ( session, false );
-        if ( ctx != null )
-        {
-            ctx.clear ();
-            unregisterContext ( ctx );
-            session.removeAttribute ( CONTEXT );
-        }
-    }
-
-    /**
-     * Get the context for a session
-     * 
-     * @param session
-     *            the session
-     * @return the context
-     */
-    private Context getTimedContext ( final IoSession session, final boolean create )
-    {
-        Context ctx = (Context)session.getAttribute ( CONTEXT );
-        if ( ctx == null && create )
-        {
-            logger.trace ( "Creating contect for: {}", session );
-            ctx = new Context ( this, this.timeout, session );
-            registerContext ( ctx );
-            session.setAttribute ( CONTEXT, ctx );
-        }
-        return ctx;
-    }
-
-    private class Context
-    {
-        private Long lastData = null;
-
-        private final TimedEndDecoder decoder;
-
-        private final long timeout;
-
-        private final IoSession session;
-
-        private boolean disposed = false;
-
-        private ProtocolDecoderOutput out;
-
-        public Context ( final TimedEndDecoder decoder, final long timeout, final IoSession session )
-        {
-            this.decoder = decoder;
-            this.timeout = timeout;
-            this.session = session;
-        }
-
-        public synchronized void tick ( final ProtocolDecoderOutput out )
-        {
-            this.lastData = System.currentTimeMillis ();
-            this.out = out;
-        }
-
-        public synchronized void clear ()
-        {
-            this.lastData = null;
-            this.out = null;
-        }
-
-        public synchronized void check ()
-        {
-            if ( this.disposed )
-            {
-                return;
-            }
-
-            if ( this.lastData == null )
-            {
-                return;
-            }
-
-            if ( System.currentTimeMillis () - this.lastData > this.timeout )
-            {
-                final ProtocolDecoderOutput out = this.out;
-                TimedEndDecoder.this.clear ( this.session );
-                this.decoder.wrapTimeout ( this.session, out );
-            }
-        }
-
-        public synchronized void dispose ()
-        {
-            this.disposed = true;
-        }
-    }
-
-    public synchronized void setNextFilter ( final NextFilter nextFilter )
-    {
-        this.nextFilter = nextFilter;
-    }
-
-}