Package org.xmlBlaster.engine

Source Code of org.xmlBlaster.engine.MsgErrorHandler

/*------------------------------------------------------------------------------
Name:      MsgErrorHandler.java
Project:   xmlBlaster.org
Copyright: xmlBlaster.org, see xmlBlaster-LICENSE file
------------------------------------------------------------------------------*/
package org.xmlBlaster.engine;

import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.xmlBlaster.authentication.SessionInfo;
import org.xmlBlaster.client.qos.DisconnectQos;
import org.xmlBlaster.util.MsgUnitRaw;
import org.xmlBlaster.util.SessionName;
import org.xmlBlaster.util.XmlBlasterException;
import org.xmlBlaster.util.def.Constants;
import org.xmlBlaster.util.def.ErrorCode;
import org.xmlBlaster.util.dispatch.DispatchManager;
import org.xmlBlaster.util.error.I_MsgErrorHandler;
import org.xmlBlaster.util.error.I_MsgErrorInfo;
import org.xmlBlaster.util.qos.MsgQosData;
import org.xmlBlaster.util.qos.storage.QueuePropertyBase;
import org.xmlBlaster.util.queue.I_Entry;
import org.xmlBlaster.util.queue.I_Queue;
import org.xmlBlaster.util.queuemsg.MsgQueueEntry;

/**
* The default error recovery implementation for messages which are lost
* in time and universe.
* @author xmlBlaster@marcelruff.info
* @author michele@laghi.eu
*/
public final class MsgErrorHandler implements I_MsgErrorHandler
{
   //private final String ME;
   private final long MAX_BYTES = 1000000L; // to avoid out of mem, max 1 MB during error handling
  
   private final ServerScope glob;
   private static Logger log = Logger.getLogger(MsgErrorHandler.class.getName());
   private /*final -> shutdown*/ SessionInfo sessionInfo;

   /**
    * @param sessionInfo Can be null (e.g. for Subject errors)
    */
   public MsgErrorHandler(ServerScope glob, SessionInfo sessionInfo) {
      //this.ME = "MsgErrorHandler-" + ((sessionInfo==null) ? "" : sessionInfo.getId());
      this.glob = glob;

      this.sessionInfo = sessionInfo;
   }

   /**
    * The final recovery, all informations necessary are transported in msgErrorInfo
    * We expect three error types:
    * <table border="1">
    *   <tr><th>ErrorCode</th><th>Action</th></tr>
    *   <tr>
    *     <td>ErrorCode.USER*</td>
    *     <td> User errors are thrown from remote clients update() method,
    *       we handle only the bounced back messages.
    *       We take care to remove them from the queue</td>
    *   </tr>
    *   <tr>
    *     <td>ErrorCode.COMMUNICATION*</td>
    *     <td>The connection went to state==DEAD, we recover all
    *       messages from the queue</td>
    *   </tr>
    *   <tr>
    *     <td>Other exceptions from mime access plugin</td>
    *     <td>Those messages have not entered the queue yet, they have no impact on
    *         the dispatcher framework (no queue flushing etc.)</td>
    </tr>
    *   <tr>
    *     <td>Other exceptions from dispatcher framework</td>
    *     <td>They are handled as internal problems,
    *       we do the same as with COMMUNICATION* exception</td>
    </tr>
    * </table>
    */
   public void handleError(I_MsgErrorInfo msgErrorInfo) {
      if (msgErrorInfo == null) return;

      String message = "";
      long size = 0;
     
      try {
         XmlBlasterException xmlBlasterException = msgErrorInfo.getXmlBlasterException();
         ErrorCode errorCode = xmlBlasterException.getErrorCode();
         message = xmlBlasterException.getMessage();
         MsgQueueEntry[] msgQueueEntries = msgErrorInfo.getMsgQueueEntries();
         DispatchManager dispatchManager = (this.sessionInfo == null) ? null : this.sessionInfo.getDispatchManager();
         I_Queue msgQueue = msgErrorInfo.getQueue()// is null if entry is not yet in queue
  
         if (log.isLoggable(Level.FINER)) log.finer("Error handling started: " + msgErrorInfo.toString());
  
         if (msgQueueEntries != null && msgQueueEntries.length > 0) {
            // 1. Generate dead letters from passed messages
            glob.getRequestBroker().deadMessage(msgQueueEntries, msgQueue, message);
  
            if (msgQueue != null) {
               // Remove the above published dead message from the queue
               try {
                  if (log.isLoggable(Level.FINE)) log.fine("Removing " + msgQueueEntries.length + " dead messages from queue");
                  long removed = 0L;
                  boolean tmp[] = msgQueue.removeRandom(msgQueueEntries);
                  for (int i=0; i < tmp.length; i++) if (tmp[i]) removed++;
                  if (removed != msgQueueEntries.length) {
                     log.warning("Expected to remove " + msgQueueEntries.length + " messages from queue but where only " + removed + ": " + message);
                  }
               }
               catch (XmlBlasterException e) {
                  log.warning("Can't remove " + msgQueueEntries.length + " messages from queue: " + e.getMessage() + ". Original cause was: " + message);
               }
            }
         }
  
         if (xmlBlasterException.isUser()) {
            // The update() method from the client has thrown a ErrorCode.USER* error
            if (log.isLoggable(Level.FINE)) log.fine("Error handlig for exception " + errorCode.toString() + " done");
            return;
         }
  
         // 2a. Generate dead letters if there are some entries in the queue
         size = (msgQueue == null) ? 0 : msgQueue.getNumOfEntries();
         if (log.isLoggable(Level.FINE)) log.fine("Flushing " + size + " remaining message from queue");
         if (size > 0) {
            try {
               QueuePropertyBase queueProperty = (QueuePropertyBase)msgQueue.getProperties();
               if (queueProperty == null || queueProperty.onFailureDeadMessage()) {
                  while (msgQueue.getNumOfEntries() > 0L) {
                     List<I_Entry> list = msgQueue.peek(-1, MAX_BYTES);
                     MsgQueueEntry[] msgArr = (MsgQueueEntry[])list.toArray(new MsgQueueEntry[list.size()]);
                     if (msgArr.length > 0) {
                        glob.getRequestBroker().deadMessage(msgArr, (I_Queue)null, message);
                     }
                     if (msgArr.length > 0) {
                        msgQueue.removeRandom(msgArr);
                     }
                  }
               }
               else {
                  log.severe("PANIC: Only onFailure='" + Constants.ONOVERFLOW_DEADMESSAGE +
                        "' is implemented, " + msgQueue.getNumOfEntries() + " messages are lost: " + message);
               }
            }
            catch(Throwable e) {
               e.printStackTrace();
               log.severe("PANIC: givingUpDelivery failed, " + size +
                              " messages are lost: " + message + ": " + e.toString());
            }
         }

         // 2b. Generate dead letters if there is a raw message which we could not parse
         if (msgErrorInfo.getMsgUnitRaw() != null) {
             MsgUnitRaw msgUnitRaw = msgErrorInfo.getMsgUnitRaw();
             glob.getRequestBroker().publishDeadMessageRaw(msgErrorInfo.getSessionName(), msgUnitRaw, message, null);
         }

         // We do a auto logout if the callback is down
         if (dispatchManager == null || dispatchManager.isDead()) {
            if (log.isLoggable(Level.FINE)) log.fine("Doing error handling for dead connection state ...");
  
            if (dispatchManager!=null) dispatchManager.shutdown();
  
            // 3. Kill login session
            if (this.sessionInfo != null && // if callback has been configured (async)
                sessionInfo.getConnectQos().getSessionCbQueueProperty().getCallbackAddresses().length > 0) {
              
                     log.warning("Callback server is lost, killing login session of client " +
                                   ((msgQueue == null) ? "unknown" : msgQueue.getStorageId().toString()) +
                                   ": " + message);
                     try {
                        DisconnectQos disconnectQos = new DisconnectQos(glob);
                        disconnectQos.deleteSubjectQueue(false);
                        glob.getAuthenticate().disconnect(this.sessionInfo.getAddressServer(),
                                            this.sessionInfo.getSecretSessionId(), disconnectQos.toXml());
                     }
                     catch (XmlBlasterException e) {
                        if (e.isErrorCode(ErrorCode.USER_SECURITY_AUTHENTICATION_ACCESSDENIED) ||
                              e.isErrorCode(ErrorCode.USER_NOT_CONNECTED))
                           log.fine("disconnect after error handling handling failed, session is destroyed already: " + e.getMessage());
                        else
                           log.warning("disconnect after error handling handling failed, session is destroyed already: " + e.getMessage());
                     }
                     catch (Throwable e) {
                        log.severe("PANIC: disconnect during error handling failed " + e.toString());
                     }
            }
         }
      }
      catch(Throwable e) {
         e.printStackTrace();
         log.severe("PANIC: handle error failed, " + size + " messages are lost: " + message);
      }
   }

   /**
    * This should never happen on server side, so we just call handleError(I_MsgErrorInfo).
    * @exception XmlBlasterException is thrown if we are in sync mode and we have no COMMUNICATION problem,
    * the client shall handle it himself
    */
   public String handleErrorSync(I_MsgErrorInfo msgErrorInfo) throws XmlBlasterException {
      if (msgErrorInfo.getMsgUnit() != null) {
         // No queue involved, a XmlBlasterImpl.publish() has failed
         // (a publish call from a client inside xmlBlaster server)
         // We end up here if the client does not want to get exception back:
         // ConnectQosServer.allowExcpetionsThrownToClient=false
         SessionName sessionName = msgErrorInfo.getSessionName();
         org.xmlBlaster.util.MsgUnit msgUnit = msgErrorInfo.getMsgUnit();
         XmlBlasterException e = msgErrorInfo.getXmlBlasterException();
         try {
            if (msgUnit.getQosData().getRcvTimestamp() == null)
               msgUnit.getQosData().touchRcvTimestamp();
            String clientPropertyKey = "__isErrorHandled" + msgUnit.getLogId();
            SessionName receiver = null; // !!!!! msgUnit.getQosData().getDestination();
            if (msgUnit.getQosData() instanceof MsgQosData) {
               MsgQosData mq = (MsgQosData)msgUnit.getQosData();
               if (mq.getDestinationArr().length > 0) {
                  receiver = mq.getDestinationArr()[0].getDestination();
               }
            }
            String txt = (e == null) ? "" : e.toString();
            if (e != null) {
              Throwable cause = e.getCause();
              if (cause != null) {
                txt += " cause=" + cause.getMessage();
                //cause.getStackTrace();
              }
              else {
                //e.printStackTrace();
              }
                txt += " " + e.createStackTrace(); // includes cause stack trace
            }
            String name = (sessionName == null) ? "" : sessionName.getAbsoluteName();
            log.severe("Generating dead message '" + msgUnit.getLogId() + "'" +
               " from publisher=" + name +
               " because delivery failed and connectQosServer.allowExcpetionsThrownToClient=false: " + txt);
            return glob.getRequestBroker().publishDeadMessage(msgUnit, txt, clientPropertyKey, receiver);
     }
         catch (XmlBlasterException ex) {
             ex.printStackTrace();
             log.severe(ex.toString());
             return null;
         }
      }
    
      if (log.isLoggable(Level.FINE)) log.fine("Unexpected sync error handling invocation, we try our best");
      //Thread.currentThread().dumpStack();
      handleError(msgErrorInfo);
      return null;
   }

   public void shutdown() {
      //this.sessionInfo = null;
   }
}
TOP

Related Classes of org.xmlBlaster.engine.MsgErrorHandler

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.