Package org.apache.sandesha2.workers

Source Code of org.apache.sandesha2.workers.Invoker

/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/

package org.apache.sandesha2.workers;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.apache.axis2.context.ConfigurationContext;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.sandesha2.Sandesha2Constants;
import org.apache.sandesha2.SandeshaException;
import org.apache.sandesha2.context.ContextManager;
import org.apache.sandesha2.i18n.SandeshaMessageHelper;
import org.apache.sandesha2.i18n.SandeshaMessageKeys;
import org.apache.sandesha2.storage.StorageManager;
import org.apache.sandesha2.storage.Transaction;
import org.apache.sandesha2.storage.beanmanagers.InvokerBeanMgr;
import org.apache.sandesha2.storage.beanmanagers.RMDBeanMgr;
import org.apache.sandesha2.storage.beans.InvokerBean;
import org.apache.sandesha2.storage.beans.RMDBean;
import org.apache.sandesha2.util.Range;
import org.apache.sandesha2.util.RangeString;
import org.apache.sandesha2.util.SandeshaUtil;

/**
* This is used when InOrder invocation is required. This is a seperated Thread
* that keep running all the time. At each iteration it checks the InvokerTable
* to find weather there are any messages to me invoked.
*/

public class Invoker extends SandeshaThread {

  private static final Log log = LogFactory.getLog(Invoker.class);

  // If this invoker is working for several sequences, we use round-robin to
  // try and give them all a chance to invoke messages.
  int nextIndex = 0;
  boolean processedMessage = false;

  public Invoker() {
    super(Sandesha2Constants.INVOKER_SLEEP_TIME);
  }
 
  /**
   * Forces dispatch of queued messages to the application.
   * NOTE: may break ordering
   * @param ctx
   * @param sequenceID
   * @param allowLaterDeliveryOfMissingMessages if true, messages skipped over during this
   * action will be invoked if they arrive on the system at a later time.
   * Otherwise messages skipped over will be ignored
   * @throws SandeshaException
   */
  public synchronized void forceInvokeOfAllMessagesCurrentlyOnSequence(ConfigurationContext ctx,
      String sequenceID,
      boolean allowLaterDeliveryOfMissingMessages)throws SandeshaException{
    //first we block while we wait for the invoking thread to pause
    blockForPause();
    try{
      //get all invoker beans for the sequence
      InvokerBeanMgr storageMapMgr = storageManager
          .getInvokerBeanMgr();
      RMDBeanMgr rmdBeanMgr = storageManager.getRMDBeanMgr();
      RMDBean rMDBean = rmdBeanMgr.retrieve(sequenceID);
     
      if (rMDBean != null) {
       
        //The outOfOrder window is the set of known sequence messages (including those
        //that are missing) at the time the button is pressed.
        long firstMessageInOutOfOrderWindow = rMDBean.getNextMsgNoToProcess();
     
        InvokerBean selector = new InvokerBean();
        selector.setSequenceID(sequenceID);
        Iterator stMapIt = storageMapMgr.find(selector).iterator();
       
        long highestMsgNumberInvoked = 0;
        Transaction transaction = null;
       
        //invoke each bean in turn.
        //NOTE: here we are breaking ordering
        while(stMapIt.hasNext()){
          //invoke the app
          try{
            transaction = storageManager.getTransaction();
            InvokerBean invoker = (InvokerBean)stMapIt.next();
           
            // start a new worker thread and let it do the invocation.
            String workId = sequenceID + "::" + invoker.getMsgNo(); //creating a workId to uniquely identify the
             //piece of work that will be assigned to the Worker.
           
            String messageContextKey = invoker.getMessageContextRefKey();
            InvokerWorker worker = new InvokerWorker(context,
                messageContextKey,
                true); //want to ignore the enxt msg number
           
            worker.setLock(getWorkerLock());
            worker.setWorkId(workId);
           
            // Wrap the invoker worker with the correct context, if needed.
            Runnable work = worker;
            ContextManager contextMgr = SandeshaUtil.getContextManager(context);
            if(contextMgr != null) {
              work = contextMgr.wrapWithContext(work, invoker.getContext());
            }
           
            threadPool.execute(work);
         
            //adding the workId to the lock after assigning it to a thread makes sure
            //that all the workIds in the Lock are handled by threads.
            getWorkerLock().addWork(workId);

            long msgNumber = invoker.getMsgNo();
            //if necessary, update the "next message number" bean under this transaction
            if(msgNumber>highestMsgNumberInvoked){
              highestMsgNumberInvoked = invoker.getMsgNo();
              rMDBean.setNextMsgNoToProcess(highestMsgNumberInvoked+1);
             
              if(allowLaterDeliveryOfMissingMessages){
                //we also need to update the sequence OUT_OF_ORDER_RANGES property
                //so as to include our latest view of this outOfOrder range.
                //We do that here (rather than once at the end) so that we reamin
                //transactionally consistent
                Range r = new Range(firstMessageInOutOfOrderWindow,highestMsgNumberInvoked);
                   
                RangeString rangeString = null;
                if(rMDBean.getOutOfOrderRanges()==null){
                  //insert a new blank one one
                  rangeString = new RangeString();
                }
                else{
                  rangeString = rMDBean.getOutOfOrderRanges();
                }
                //update the range String with the new value
                rangeString.addRange(r);
                rMDBean.setOutOfOrderRanges(rangeString);
              }
             
              rmdBeanMgr.update(rMDBean);
            }
           
            if(transaction != null && transaction.isActive()) transaction.commit();
            transaction = null;
          }
          catch(Exception e){
            // Just log the error
            if(log.isDebugEnabled()) log.debug("Exception", e);
          } finally {
            if(transaction != null && transaction.isActive()) {
              transaction.rollback();
              transaction = null;
            }
          }
   
        }//end while
      }
    }
    finally{
      //restart the invoker
      finishPause();
    }
  }

  private void addOutOfOrderInvokerBeansToList(String sequenceID,
      StorageManager storageManager, List list)throws SandeshaException{
    if (log.isDebugEnabled())
      log.debug("Enter: InOrderInvoker::addOutOfOrderInvokerBeansToList " + sequenceID + ", " + list);
   
    RMDBean rmdBean = SandeshaUtil.getRMDBeanFromSequenceId(storageManager, sequenceID);
   
    if(rmdBean != null && rmdBean.getOutOfOrderRanges() != null){
      RangeString rangeString = rmdBean.getOutOfOrderRanges();
      //we now have the set of ranges that can be delivered out of order.
      //Look for any invokable message that lies in one of those ranges
      InvokerBean selector = new InvokerBean();
      selector.setSequenceID(sequenceID);
      Iterator invokerBeansIterator =
        storageManager.getInvokerBeanMgr().find(selector).iterator();
     
      while(invokerBeansIterator.hasNext()){
        InvokerBean invokerBean = (InvokerBean)invokerBeansIterator.next();
       
        if(rangeString.isMessageNumberInRanges(invokerBean.getMsgNo())){
          //an invoker bean that has not been deleted and lies in an out
          //or order range - we can add this to the list
          list.add(invokerBean);
        }
      }
     
    }
     
    if (log.isDebugEnabled())
      log.debug("Exit: InOrderInvoker::addOutOfOrderInvokerBeansToList");
  }
 
  protected boolean internalRun() {
    if (log.isDebugEnabled()) log.debug("Enter: Invoker::internalRun");
   
    boolean sleep = false;
    Transaction transaction = null;

    try {
      RMDBeanMgr nextMsgMgr = storageManager.getRMDBeanMgr();

      InvokerBeanMgr storageMapMgr = storageManager
          .getInvokerBeanMgr();

      transaction = storageManager.getTransaction();
     
      // Pick a sequence using a round-robin approach
      ArrayList allSequencesList = getSequences();
      int size = allSequencesList.size();
      log.debug("Choosing one from " + size + " sequences");
      if(nextIndex >= size) {
        nextIndex = 0;

        // We just looped over the set of sequences. If we didn't process any
        // messages on this loop then we sleep before the next one
        if(size == 0 || !processedMessage) {
          sleep = true;
        }
        processedMessage = false;
       
        if (log.isDebugEnabled()) log.debug("Exit: Invoker::internalRun, looped over all sequences, sleep " + sleep);
       
        if(transaction != null && transaction.isActive()) transaction.commit();
        transaction = null;
       
        return sleep;
      }

      SequenceEntry entry = (SequenceEntry) allSequencesList.get(nextIndex++);
      String sequenceId = entry.getSequenceId();
      log.debug("Chose sequence " + sequenceId);

      RMDBean nextMsgBean = nextMsgMgr.retrieve(sequenceId);
      if (nextMsgBean == null) {
        log.debug("Next message not set correctly. Removing invalid entry.");

        stopThreadForSequence(sequenceId, entry.isRmSource());
        allSequencesList = getSequences();
        if (allSequencesList.size() == 0)
          sleep = true;

        if (log.isDebugEnabled()) log.debug("Exit: Invoker::internalRun, sleep " + sleep);
       
        if(transaction != null && transaction.isActive()) transaction.commit();
        transaction = null;

        return sleep;
      }

      long nextMsgno = nextMsgBean.getNextMsgNoToProcess();
      if (nextMsgno <= 0) {
        // Make sure we sleep on the next loop, so that we don't spin in a tight loop
        sleep = true;
        if (log.isDebugEnabled())
          log.debug("Invalid Next Message Number " + nextMsgno);
        String message = SandeshaMessageHelper.getMessage(
            SandeshaMessageKeys.invalidMsgNumber, Long
                .toString(nextMsgno));
        throw new SandeshaException(message);
      }

      InvokerBean selector = new InvokerBean();
      selector.setSequenceID(sequenceId);
      selector.setMsgNo(nextMsgno);
      List invokerBeans = storageMapMgr.find(selector);
     
      //add any msgs that belong to out of order windows
      addOutOfOrderInvokerBeansToList(sequenceId,
          storageManager, invokerBeans);
     
      // If there aren't any beans to process then move on to the next sequence
      if (invokerBeans.size() == 0) {
        if (log.isDebugEnabled()) log.debug("Exit: Invoker::internalRun, no beans to invoke on sequence " + sequenceId + ", sleep " + sleep);
       
        if(transaction != null && transaction.isActive()) transaction.commit();
        transaction = null;

        return sleep;
      }
     
      Iterator stMapIt = invokerBeans.iterator();

      //TODO correct the locking mechanism to have one lock per sequence.
      //TODO should this be a while, not an if?
      if (stMapIt.hasNext()) { //some invokation work is present
       
        InvokerBean bean = (InvokerBean) stMapIt.next();
        //see if this is an out of order msg
        boolean beanIsOutOfOrderMsg = bean.getMsgNo()!=nextMsgno;
       
        String workId = sequenceId + "::" + bean.getMsgNo();
                                  //creating a workId to uniquely identify the
                                 //piece of work that will be assigned to the Worker.
                 
        //check whether the bean is already assigned to a worker.
        if (getWorkerLock().isWorkPresent(workId)) {
          // As there is already a worker assigned we are probably dispatching
          // messages too quickly, so we sleep before trying the next sequence.
          sleep = true;
          String message = SandeshaMessageHelper.getMessage(SandeshaMessageKeys.workAlreadyAssigned, workId);
          if (log.isDebugEnabled()) log.debug("Exit: Invoker::internalRun, " + message + ", sleep " + sleep);
         
          if(transaction != null) {
            transaction.commit();
            transaction = null;
          }
         
          return sleep;
        }

        String messageContextKey = bean.getMessageContextRefKey();
       
        if(transaction != null) {
          transaction.commit();
          transaction = null;
        }

        // start a new worker thread and let it do the invocation.
        InvokerWorker worker = new InvokerWorker(context,
            messageContextKey,
            beanIsOutOfOrderMsg); //only ignore nextMsgNumber if the bean is an
                                  //out of order message
       
        worker.setLock(getWorkerLock());
        worker.setWorkId(workId);
       
        // Wrap the invoker worker with the correct context, if needed.
        Runnable work = worker;
        ContextManager contextMgr = SandeshaUtil.getContextManager(context);
        if(contextMgr != null) {
          work = contextMgr.wrapWithContext(work, bean.getContext());
        }
        threadPool.execute(work);
       
        //adding the workId to the lock after assigning it to a thread makes sure
        //that all the workIds in the Lock are handled by threads.
        getWorkerLock().addWork(workId);
       
        processedMessage = true;
      }
     
      if(transaction != null && transaction.isActive()) transaction.commit();
      transaction = null;
    } catch (Exception e) {
      String message = SandeshaMessageHelper
          .getMessage(SandeshaMessageKeys.invokeMsgError);
      if(log.isDebugEnabled()) log.debug(message, e);
    } finally {
      if (transaction != null && transaction.isActive()) {
        try {
          transaction.rollback();
        } catch (Exception e) {
          String message = SandeshaMessageHelper.getMessage(
              SandeshaMessageKeys.rollbackError, e.toString());
          if(log.isDebugEnabled()) log.debug(message, e);
        }
      }
    }

    if (log.isDebugEnabled())
      log.debug("Exit: InOrderInvoker::internalRun");
    return sleep;
  }
 
}
TOP

Related Classes of org.apache.sandesha2.workers.Invoker

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.