Package nexj.core.rpc.timer

Source Code of nexj.core.rpc.timer.PersistentTimer

// Copyright 2010 NexJ Systems Inc. This software is licensed under the terms of the Eclipse Public License 1.0
package nexj.core.rpc.timer;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import nexj.core.integration.Sender;
import nexj.core.meta.Component;
import nexj.core.meta.Metaclass;
import nexj.core.meta.Metadata;
import nexj.core.meta.Repository;
import nexj.core.persistence.DeadlockException;
import nexj.core.persistence.Query;
import nexj.core.rpc.TransferObject;
import nexj.core.rpc.jms.JMSSender;
import nexj.core.runtime.Initializable;
import nexj.core.runtime.Instance;
import nexj.core.runtime.InstanceList;
import nexj.core.runtime.InvocationContext;
import nexj.core.runtime.ThreadContextHolder;
import nexj.core.scripting.Pair;
import nexj.core.scripting.Symbol;
import nexj.core.util.Binary;
import nexj.core.util.J2EEUtil;
import nexj.core.util.Lifecycle;
import nexj.core.util.Logger;

/**
* Generic persistent timer implementation.
* Must be instantiated as a singleton component per node.
*/
public class PersistentTimer implements TimerListener, Initializable, Lifecycle
{
   // attributes

   /**
    * The default user name.
    */
   protected String m_sDefaultUser;

   /**
    * The polling interval in ms.
    */
   protected long m_lInterval = 60000;

   /**
    * The SysTimer batch count.
    */
   protected int m_nBatchCount = 8;

   /**
    * The next timeout in ms since 1970-01-01 00:00:00 UTC.
    */
   protected long m_lTimeout;

   /**
    * The last deferral time.
    */
   protected long m_lDeferralTime;

   /**
    * True if the timer is operating in a cluster.
    */
   protected boolean m_bDistributed;

   /**
    * Flag to stop the timer.
    */
   protected boolean m_bStop;

   /**
    * True if the timer has been stopped (i.e. finished stopping).
    */
   protected boolean m_bStopped = true;

   // associations

   /**
    * The invocation context component.
    */
   protected Component m_contextComponent;

   /**
    * This component.
    */
   protected Component m_thisComponent;

   /**
    * The asynchronous message queue client.
    */
   protected Sender m_queueClient;

   /**
    * The asynchronous message broadcast client.
    */
   protected Sender m_topicClient;

   /**
    * The class logger.
    */
   protected final static Logger s_logger = Logger.getLogger(PersistentTimer.class);

   // operations

   /**
    * Sets the distributed system flag.
    * @param bDistributed True if the system is operating in a cluster.
    */
   public void setDistributed(boolean bDistributed)
   {
      m_bDistributed = bDistributed;
   }

   /**
    * Sets the default user account.
    * @param sUser The user account name.
    */
   public void setDefaultUser(String sUser)
   {
      m_sDefaultUser = sUser;
   }

   /**
    * Sets the maximum polling interval.
    * @param lInterval The maximum polling interval in ms.
    */
   public void setInterval(long lInterval)
   {
      m_lInterval = lInterval;
   }

   /**
    * Sets the timer batch count (maximum number of timers processed in one transaction).
    * @param nCount The batch count.
    */
   public void setBatchCount(int nCount)
   {
      m_nBatchCount = nCount;
   }

   /**
    * Sets the asynchronous message queue client.
    * @param queueClient The asynchronous message queue client to set.
    */
   public void setQueueClient(Sender queueClient)
   {
      m_queueClient = queueClient;
   }

   /**
    * Sets the asynchronous message broadcast client.
    * @param topicClient The asynchronous message broadcast client to set.
    */
   public void setTopicClient(Sender topicClient)
   {
      m_topicClient = topicClient;
   }

   /**
    * Sets the invocation context component.
    * @param contextComponent The invocation context component to set.
    */
   public void setContextComponent(Component contextComponent)
   {
      m_contextComponent = contextComponent;
   }

   /**
    * Sets this component.
    * @param component The component to set.
    */
   public void setThisComponent(Component component)
   {
      m_thisComponent = component;
   }

   /**
    * @see nexj.core.runtime.Initializable#initialize()
    */
   public void initialize() throws Exception
   {
      if (m_contextComponent == null)
      {
         m_contextComponent = Repository.getMetadata().getComponent("System.InvocationContext");
      }
   }

   /**
    * @see nexj.core.util.Lifecycle#startup()
    */
   public void startup() throws Exception
   {
      synchronized (this)
      {
         m_bStop = false;
      }
   }

   /**
    * @see nexj.core.util.Lifecycle#shutdown()
    */
   public void shutdown()
   {
      if (s_logger.isInfoEnabled())
      {
         s_logger.info("Stopping the persistent timer \"" + m_thisComponent.getName() + "\"");
      }

      // Request stop
      synchronized (this)
      {
         m_bStop = true;
         notifyAll();
      }

      // Wait for stopped
      synchronized (this)
      {
         while (!m_bStopped)
         {
            try
            {
               wait();
            }
            catch (InterruptedException ex)
            {
            }
         }
      }
   }

   /**
    * @see nexj.core.util.Suspendable#suspend()
    */
   public void suspend() throws Exception
   {
      shutdown();
   }

   /**
    * @see nexj.core.util.Suspendable#resume()
    */
   public void resume() throws Exception
   {
      startup();
   }

   /**
    * Resets the timer next timeout.
    * @param lTimeout The timeout in ms since 1970-01-01.
    */
   protected void reset(long lTimeout, boolean bNotify)
   {
      synchronized (this)
      {
         if (m_lTimeout == 0 || lTimeout < m_lTimeout)
         {
            m_lTimeout = lTimeout;

            if (bNotify)
            {
               notifyAll();
            }
         }
      }
   }

   /**
    * Notifies the timer about a new timeout.
    * @param lTimeout The timeout in ms since 1970-01-01.
    */
   public void notify(long lTimeout)
   {
      reset(lTimeout, true);
   }

   /**
    * Defers the polling by another interval.
    */
   public void defer()
   {
      synchronized (this)
      {
         m_lDeferralTime = System.currentTimeMillis();

         long lTimeout = m_lDeferralTime + m_lInterval;

         if (lTimeout > m_lTimeout)
         {
            m_lTimeout = lTimeout;
         }
      }
   }

   /**
    * @see nexj.core.rpc.timer.TimerListener#onTimeout()
    */
   public long onTimeout()
   {
      if (s_logger.isDebugEnabled())
      {
         s_logger.debug("Starting the persistent timer \"" + m_thisComponent.getName() + "\"");
      }

      synchronized (this)
      {
         m_bStopped = false;
      }

      try
      {
         if (m_lTimeout != 0)
         {
            synchronized (this)
            {
               long lDelay = m_lTimeout - System.currentTimeMillis();

               if (m_bStop)
               {
                  return Long.MAX_VALUE;
               }

               if (lDelay > 0)
               {
                  wait(lDelay);
               }

               if (m_bStop)
               {
                  return Long.MAX_VALUE;
               }

               if (System.currentTimeMillis() < m_lTimeout)
               {
                  return Long.MIN_VALUE;
               }

               m_lTimeout = 0;
            }
         }

         if (s_logger.isDebugEnabled())
         {
            s_logger.debug("Polling timer \"" +  m_thisComponent.getName() + "\"");
         }

         InvocationContext context = (InvocationContext)m_contextComponent.getInstance(null);
         int nCookie = -1;

         try
         {
            context.initialize(null);
            context.setSecure(false);
            context.setPartitioned(false);
            nCookie = Logger.pushContext(context.getPrincipal().getName());

            Metadata metadata = context.getMetadata();
            Metaclass timerClass = metadata.getMetaclass("SysTimer");
            Pair attributes =
               new Pair(Symbol.define("next"),
               new Pair(Symbol.define("start"),
               new Pair(Symbol.define("period"),
               new Pair(Symbol.define("principal"),
               new Pair(Symbol.define("data"))))));
            Pair orderBy = new Pair(new Pair(Symbol.define("next"), Boolean.TRUE));
            List sendList = null;

         loop:
            for (;;)
            {
               context.beginTransaction();
               context.getUnitOfWork().setRaw(true);

               long lCurrentTime = System.currentTimeMillis();
               InstanceList list = Query.createRead(timerClass, attributes,
                  (m_bDistributed) ? Pair.attribute("next").le(new Timestamp(lCurrentTime + m_lInterval)) : null,
                  orderBy, m_nBatchCount, 0, true, Query.SEC_NONE, context).read();

               for (int i = 0; i < list.size(); ++i)
               {
                  Instance timer = list.getInstance(i);
                  long lNextTimeout = ((Timestamp)timer.getValue("next")).getTime();

                  if (lNextTimeout <= lCurrentTime)
                  {
                     String sId = timer.getOID().getValue(0).toString();
                     TransferObject properties = new TransferObject(3);

                     properties.setValue("timer", sId);
                     properties.setValue(JMSSender.PROTECTED, Boolean.FALSE);

                     Object user = timer.getValue("principal");

                     if (user == null)
                     {
                        user = m_sDefaultUser;
                     }

                     if (user != null)
                     {
                        properties.setValue(JMSSender.USER, user);
                     }

                     if (s_logger.isDebugEnabled())
                     {
                        s_logger.debug("Timeout in timer \"" +  m_thisComponent.getName() +
                           "\", id=" + sId + "; forwarding to the message queue");
                     }

                     TransferObject tobj = new TransferObject(2);

                     tobj.setValue(JMSSender.BODY, ((Binary)timer.getValue("data")).toObject());
                     tobj.setValue(JMSSender.PROPERTIES, properties);

                     if (sendList == null)
                     {
                        sendList = new ArrayList(m_nBatchCount);
                     }

                     sendList.add(tobj);

                     long lPeriod = ((Number)timer.getValue("period")).longValue();

                     if (lPeriod <= 0)
                     {
                        timer.invoke("delete");
                     }
                     else
                     {
                        Timestamp tmStart = (Timestamp)timer.getValue("start");

                        lNextTimeout = tmStart.getTime() + lPeriod * ((lCurrentTime - tmStart.getTime()) / lPeriod + 1);
                        timer.setValue("next", new Timestamp(lNextTimeout));
                        reset(lNextTimeout, false);
                     }
                  }
                  else
                  {
                     reset(lNextTimeout, false);

                     break loop;
                  }
               }

               if (list.size() < m_nBatchCount)
               {
                  break;
               }

               if (sendList != null)
               {
                  m_queueClient.send(sendList);
                  sendList = null;
               }

               context.complete(true);
               context.initUnitOfWork();
            }

            if (sendList != null)
            {
               m_queueClient.send(sendList);
               sendList = null;
            }

            context.complete(true);

            // Defer the timeout in the other nodes

            if (m_bDistributed)
            {
               synchronized (this)
               {
                  long lCurrentTime = System.currentTimeMillis();

                  if (m_lTimeout - lCurrentTime >= (m_lInterval >> 1) ||
                     lCurrentTime - m_lDeferralTime >= (m_lInterval >> 1))
                  {
                     m_lDeferralTime = lCurrentTime;

                     TransferObject properties = new TransferObject(1);

                     properties.setValue(JMSSender.NODE, J2EEUtil.NODE_ID);

                     TransferObject tobj = new TransferObject(2);

                     tobj.setValue(JMSSender.BODY, new DeferTimeoutCommand(m_thisComponent.getName()));
                     tobj.setValue(JMSSender.PROPERTIES, properties);

                     m_topicClient.send(tobj);
                  }
               }
            }
         }
         catch (Throwable t)
         {
            context.complete(false);

            throw t;
         }
         finally
         {
            if (nCookie != -1)
            {
               Logger.resetContext(nCookie);
            }

            ThreadContextHolder.setContext(null);
            reset((m_bDistributed) ? System.currentTimeMillis() + m_lInterval : Long.MAX_VALUE, false);
         }
      }
      catch (InterruptedException e)
      {
         if (s_logger.isInfoEnabled())
         {
            s_logger.info("Timer \"" + m_thisComponent.getName() + "\" interrupted");
         }
      }
      catch (Throwable e)
      {
         s_logger.log((e instanceof DeadlockException) ? Logger.DEBUG : Logger.ERROR, "Unexpected timer exception", e);
         reset(System.currentTimeMillis() + ((m_lInterval + 7) >> 3), false);
      }
      finally
      {
         synchronized (this)
         {
            m_bStopped = true;
            notifyAll();
         }
      }

      return Long.MIN_VALUE;
   }
}
TOP

Related Classes of nexj.core.rpc.timer.PersistentTimer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.