Package org.objectweb.joram.mom.proxies

Source Code of org.objectweb.joram.mom.proxies.ClientSubscription

/*
* JORAM: Java(TM) Open Reliable Asynchronous Messaging
* Copyright (C) 2003 - 2010 ScalAgent Distributed Technologies
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or any later version.
*
* This library 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 for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
* USA.
*
* Initial developer(s): Frederic Maistre (INRIA)
* Contributor(s): ScalAgent Distributed Technologies
*/
package org.objectweb.joram.mom.proxies;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;

import javax.management.openmbean.CompositeData;
import javax.management.openmbean.TabularData;

import org.objectweb.joram.mom.dest.Queue;
import org.objectweb.joram.mom.messages.Message;
import org.objectweb.joram.mom.messages.MessageJMXWrapper;
import org.objectweb.joram.mom.util.DMQManager;
import org.objectweb.joram.shared.MessageErrorConstants;
import org.objectweb.joram.shared.client.ConsumerMessages;
import org.objectweb.joram.shared.selectors.Selector;
import org.objectweb.util.monolog.api.BasicLevel;
import org.objectweb.util.monolog.api.Logger;

import fr.dyade.aaa.agent.AgentId;
import fr.dyade.aaa.common.Debug;

/**
* The <code>ClientSubscription</code> class holds the data of a client
* subscription, and the methods managing the delivery and acknowledgement
* of the messages.
*/
class ClientSubscription implements ClientSubscriptionMBean, Serializable {
  /** define serialVersionUID for interoperability */
  private static final long serialVersionUID = 1L;
 
  public static Logger logger = Debug.getLogger(ClientSubscription.class.getName());
 
  /** The proxy's agent identifier. */
  private AgentId proxyId;
  /** <code>true</code> if the subscription is durable. */
  private boolean durable;
  /** The topic identifier. */
  private AgentId topicId;
  /** The subscription name. */
  private String name;
  /** The selector for filtering messages. */
  private String selector;
  /**
   * Identifier of the subscriber's dead message queue, <code>null</code> for
   * DMQ not set.
   */
  private AgentId dmqId;
  /**
   * Threshold above which messages are considered as undeliverable because
   * constantly denied.
   * 0 stands for no threshold, -1 for value not set (use servers' default value).
   */
  private int threshold = -1;

  /** Max number of Message stored in the queue (-1 no limit). */
  protected int nbMaxMsg = -1;

  /** Vector of identifiers of the messages to deliver. */
  private List messageIds;
  /** Table of delivered messages identifiers. */
  private Map deliveredIds;
  /** Table keeping the denied messages identifiers. */
  private Map deniedMsgs;

  /** Identifier of the subscription context. */
  private transient int contextId;
  /** Identifier of the subscription request. */
  private transient int subRequestId;
  /**
   * <code>true</code> if the subscriber does not wish to consume
   * messages published in the same context.
   */
  private transient boolean noLocal;
  /**
   * <code>true</code> if the subscription does not filter messages
   * in any way.
   */
  private transient boolean noFiltering;

  /** <code>true</code> if the subscription is active. */
  private transient boolean active;
  /**
   * Identifier of the request requesting messages, either the listener's
   * request, or a "receive" request.
   */
  private transient int requestId;
  /** <code>true</code> if the messages are destinated to a listener. */
  private transient boolean toListener;
  /** Expiration time of the "receive" request, if any. */
  private transient long requestExpTime;

  /**
   * Proxy messages table. Be careful: currently this table is shared between
   * all subscription.
   */
  private transient Map messagesTable;

  private transient ProxyAgentItf proxy;
 
  /** the number of erroneous messages forwarded to the DMQ */
  protected long nbMsgsSentToDMQSinceCreation = 0;
 
  /** the number of delivered messages */
  protected long nbMsgsDeliveredSinceCreation = 0;

  /**
   * Constructs a <code>ClientSubscription</code> instance.
   *
   * @param proxyId  Proxy's identifier.
   * @param contextId  Context identifier.
   * @param reqId  Request identifier.
   * @param durable  <code>true</code> for a durable subscription.
   * @param topicId  Topic identifier.
   * @param name  Subscription's name.
   * @param selector  Selector for filtering messages.
   * @param noLocal  <code>true</code> for not consuming messages published
   *          within the same proxy's context.
   * @param dmqId  Identifier of the proxy's dead message queue, if any.
   * @param threshold  Proxy's threshold value, if any.
   * @param messagesTable  Proxy's messages table.
   */
  ClientSubscription(AgentId proxyId,
                     int contextId,
                     int reqId,
                     boolean durable,
                     AgentId topicId,
                     String name,
                     String selector,
                     boolean noLocal,
                     AgentId dmqId,
                     int threshold,
                     int nbMaxMsg,
                     Map messagesTable) {
    this.proxyId = proxyId;
    this.contextId = contextId;
    this.subRequestId = reqId;
    this.durable = durable;
    this.topicId = topicId;
    this.name = name;
    this.selector = selector;
    this.noLocal = noLocal;
    this.dmqId = dmqId;
    this.threshold = threshold;
    this.nbMaxMsg = nbMaxMsg;
    this.messagesTable = messagesTable;

    messageIds = new Vector();
    deliveredIds = new Hashtable();
    deniedMsgs = new Hashtable();

    noFiltering = (! noLocal) && (selector == null || selector.equals(""));

    active = true;
    requestId = -1;
    toListener = false;

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": created.");
  }

//    public String dump() {
//      StringBuffer buff = new StringBuffer();
//      buff.append("ClientSubscription (proxyId=");
//      buff.append(proxyId);
//      buff.append(",topicId=");
//      buff.append(topicId);
//      buff.append(",messageIds=");
//      buff.append(messageIds);
//      buff.append(",contextId=");
//      buff.append(contextId);
//      buff.append(",subRequestId=");
//      buff.append(subRequestId);
//      buff.append(",noLocal=");
//      buff.append(noLocal);
//      buff.append(",active=");
//      buff.append(active);
//      buff.append(",requestId=");
//      buff.append(requestId);
//      buff.append(",toListener=");
//      buff.append(toListener);
//      buff.append(",messagesTable=");
//      buff.append(messagesTable);
//      buff.append(")");
//      return buff.toString();
//    }

  public String toString() {
    return "ClientSubscription" + proxyId + name;
  }

  /** Returns the subscription's context identifier. */
  public int getContextId() {
    return contextId;
  }

  /** Returns the identifier of the subscribing request. */
  public int getSubRequestId() {
    return subRequestId;
  }

  /** Returns the name of the subscription. */
  public String getName() {
    return name;
  }

  /** Returns the identifier of the subscription topic. */
  public AgentId getTopicId() {
    return topicId;
  }
 
  /** Returns the identifier of the subscription topic. */
  public String getTopicIdAsString() {
    return topicId.toString();
  }

  /** Returns the selector. */
  public String getSelector() {
    return selector;
  }

  /** Returns <code>true</code> if the subscription is durable. */
  public boolean getDurable() {
    return durable;
  }

  /** Returns <code>true</code> if the subscription is active. */
  public boolean getActive() {
    return active;
  }

  /**
   * Returns the maximum number of message for the subscription.
   * If the limit is unset the method returns -1.
   *
   * @return the maximum number of message for subscription if set;
   *       -1 otherwise.
   */
  public int getNbMaxMsg() {
    return nbMaxMsg;
  }

  /**
   * Sets the maximum number of message for the subscription.
   *
   * @param nbMaxMsg the maximum number of message for subscription (-1 set
   *         no limit).
   */
  public void setNbMaxMsg(int nbMaxMsg) {
    this.nbMaxMsg = nbMaxMsg;
  }

  /**
   * Returns the number of pending messages for the subscription.
   *
   * @return The number of pending message for the subscription.
   */
  public int getPendingMessageCount() {
    return messageIds.size();
  }

  /**
   * Returns the list of message's identifiers for the subscription.
   *
   * @return the list of message's identifiers for the subscription.
   */
  public String[] getMessageIds() {
    String[] res = new String[messageIds.size()];
    messageIds.toArray(res);
    return res;
  }
 
  void setProxyAgent(ProxyAgentItf px) {
    proxy = px;
  }
 
  /**
   * Re-initializes the client subscription.
   *
   * @param messagesTable  Proxy's table where storing the messages.
   * @param persistedMessages  Proxy's persisted messages.
   * @param denyDeliveredMessages Denies already delivered messages.
   */
  void reinitialize(Map messagesTable, List persistedMessages, boolean denyDeliveredMessages) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription[" + this + "].reinitialize()");
   
    this.messagesTable = messagesTable;

    // Browsing the persisted messages.
    Message message;
    String msgId;
    for (Iterator e = persistedMessages.iterator(); e.hasNext();) {
      message = (Message) e.next();
      msgId = message.getIdentifier();

      if (messageIds.contains(msgId) || deliveredIds.containsKey(msgId)) {
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, " -> contains message " + msgId);
        message.acksCounter++;
        message.durableAcksCounter++;
       
        if (message.acksCounter == 1) {
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, " -> messagesTable.put(" + msgId + ')');
          messagesTable.put(msgId, message);
        }
//          if (message.durableAcksCounter == 1) {
        // if (logger.isLoggable(BasicLevel.DEBUG))
        // logger.log(
        //                BasicLevel.DEBUG,
//                " -> save message " + message);
// it's alredy save.
//          message.save(proxyStringId);         
//        }
      }
    }

    if (denyDeliveredMessages) {
      // Denying all previously delivered messages:
      deny(deliveredIds.keySet().iterator(), false);
      deliveredIds.clear();
    }
  }

  /**
   * Reactivates the subscription.
   *
   * @param context  Re-activation context.
   * @param reqId  Re-activation request identifier.
   * @param topicId  Topic identifier.
   * @param selector  Selector for filtering messages.
   * @param noLocal  <code>true</code> for not consuming messages published
   *          within the same proxy's context.
   */
  void reactivate(int contextId,
                  int reqId,
                  AgentId topicId,
                  String selector,
                  boolean noLocal) {
    this.contextId = contextId;
    this.subRequestId = reqId;
    this.topicId = topicId;
    this.selector = selector;
    this.noLocal = noLocal;

    noFiltering = (! noLocal) && (selector == null || selector.equals(""));

    active = true;
    requestId = -1;
    toListener = false;
   
    // Some updated attributes are persistent
    save();

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": reactivated.");
  }

  /** De-activates the subscription, denies the non acknowledged messages. */ 
  void deactivate() {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription.deactivate()");

    unsetListener();
    unsetReceiver();
    active = false;
  
    // Denying all delivered messages:
    deny(deliveredIds.keySet().iterator(), false);
    deliveredIds.clear();
   
    // deliveredIds is persistent
    save();

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": deactivated.");
  }

  void setActive(boolean active) {
    this.active = active;
  }

  /**
   * Sets a listener.
   *
   * @param requestId  Identifier of the listener request.
   */  
  void setListener(int requestId) {
    this.requestId = requestId;
    toListener = true;

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": listener set.");
  }

  /** Unsets the listener. */
  void unsetListener() {
    requestId = -1;
    toListener = false;

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": listener unset.");
  }

  /**
   * Sets a receiver request.
   *
   * @param requestId
   *            Identifier of the "receive" request.
   * @param timeToLive
   *            Request's time to live value.
   */
  void setReceiver(int requestId, long timeToLive) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ".setReceiver(" + requestId + "," + timeToLive + ")");

    this.requestId = requestId;
    toListener = false;

    if (timeToLive > 0)
      requestExpTime = System.currentTimeMillis() + timeToLive;
    else
      requestExpTime = 0;
  }

  /** Unsets a receiver request. */
  void unsetReceiver() {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ".unsetReceiver()");
    requestId = -1;
    requestExpTime = 0;
  }

  /** Sets the subscription's dead message queue identifier. */
  void setDMQId(AgentId dmqId) {
    this.dmqId = dmqId;
    save();
  }

  /** Sets the subscription's threshold value. */
  void setThreshold(int threshold) {
    this.threshold = threshold;
    save();
  }

 
  /**
   * Browses messages and keeps those which will have to be delivered
   * to the subscriber.
   */
  // AF: TODO we should parse each message for each subscription
  // see UserAgent.doFwd
  void browseNewMessages(List newMessages) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ".browseNewMessages(" + newMessages + ')');
    // Browsing the messages one by one.
    Message message;
    String msgId;
    DMQManager dmqManager = null;
    for (Iterator e = newMessages.iterator(); e.hasNext();) {
      message = (Message) e.next();
      msgId = message.getIdentifier();

      // test nbMaxMsg
      if (nbMaxMsg > 0 && nbMaxMsg <= messageIds.size()) {
        if (dmqManager == null)
          dmqManager = new DMQManager(dmqId, null);
        nbMsgsSentToDMQSinceCreation++;
        dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.QUEUE_FULL);
        continue;
      }

      // Keeping the message if filtering is successful.
      if (noFiltering ||
          (Selector.matches(message.getHeaderMessage(), selector) &&
           (! noLocal || ! msgId.startsWith(proxyId.toString().substring(1) + "c" + contextId + "m", 3)))) {

        // It's the first delivery, adds the message to the proxy's table
        if (message.acksCounter == 0)
          messagesTable.put(msgId, message);
       
        message.acksCounter++;
        if (durable)
          message.durableAcksCounter++;

        messageIds.add(msgId);
        save();

        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, this + ": added msg " + msgId + " for delivery.");
      }
    }
    if (dmqManager != null) {
      dmqManager.sendToDMQ();
    }
  }

  /**
   * Launches a delivery sequence, either for a listener, or for a receiver.
   */
  ConsumerMessages deliver() {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription[" + proxyId + ',' + topicId + ',' + name
          + "].deliver()");

    // Returning null if no request exists:
    if (requestId == -1)
      return null;

     // Returning null if a "receive" request has expired:
    if (!toListener && requestExpTime > 0 && System.currentTimeMillis() >= requestExpTime) {
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, this + ": receive request " + requestId
            + " expired.");
      requestId = -1;
      requestExpTime = 0;
      return null;
    }

    String id;
    Message message;
    Integer deliveryAttempts = null;
    int lastPrior = -1;
    int insertionIndex = -1;
    int prior;
    Vector deliverables = new Vector();
    DMQManager dmqManager = null;

    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, " -> messageIds.size() = " + messageIds.size());
   
    // Delivering to a listener.
    if (toListener) {
      // Browsing the identifiers of the messages to deliver.
      while (! messageIds.isEmpty()) {
        id = (String) messageIds.remove(0);
        save();
        message = (Message) messagesTable.get(id);

        // Message still exists.
        if (message != null) {
          // Delivering it if valid.
          if (message.isValid(System.currentTimeMillis())) {
            deliveredIds.put(id, id);

            // Setting the message's deliveryCount and denied fields.
            deliveryAttempts = (Integer) deniedMsgs.get(id);
            if (deliveryAttempts == null)
              message.setDeliveryCount(1);
            else {
              message.setDeliveryCount(deliveryAttempts.intValue() +1);
              message.setRedelivered();
            }

            // Inserting it according to its priority.
            if (lastPrior == -1 || message.getPriority() == lastPrior)
              insertionIndex++;
            else {
              insertionIndex = 0;
              while (insertionIndex < deliverables.size()) {
                prior =
                  ((Message) deliverables.get(insertionIndex)).getPriority();
                if (prior >= message.getPriority())
                  insertionIndex++;
                else
                  break;
              }
            }
            lastPrior = message.getPriority();
            deliverables.add(insertionIndex, message.getFullMessage().clone());

            if (logger.isLoggable(BasicLevel.DEBUG))
              logger.log(BasicLevel.DEBUG, this + ": message " + id + " added for delivery.");
          } else {
            // Invalid message: removing and adding it to the vector of dead
            // messages.
            messagesTable.remove(id);
            // Deleting the message, if needed.
            if (durable)
              message.delete();

            // Setting the message's deliveryCount, denied and expired fields.
            deliveryAttempts = (Integer) deniedMsgs.remove(id);
            if (deliveryAttempts != null) {
              message.setDeliveryCount(deliveryAttempts.intValue() +1);
              message.setRedelivered();
            }
            if (dmqManager == null) {
              dmqManager = new DMQManager(dmqId, null);
            }
            nbMsgsSentToDMQSinceCreation++;
            dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.EXPIRED);
          }
        } else {
          // Message has already been deleted.
          deniedMsgs.remove(id);
        }
      }
    } else {
      // Delivering to a receiver: getting the highest priority message.
      int highestP = -1;
      Message keptMsg = null;
      // Browsing the non delivered messages.
      int i = 0;
      while (i < messageIds.size()) {
        id = (String) messageIds.get(i);
        message = (Message) messagesTable.get(id);
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, " -> message = " + message);

        // Message still exists.
        if (message != null) {
          // Checking valid message.
          if (message.isValid(System.currentTimeMillis())) {
            if (logger.isLoggable(BasicLevel.DEBUG))
              logger.log(BasicLevel.DEBUG, " -> valid message");
            // Higher priority: keeping the message.
            if (message.getPriority() > highestP) {
              highestP = message.getPriority();
              keptMsg = message;
            }

            // get next message
            i++;
          } else {
            // Invalid message: removing and adding it to the vector of dead
            // messages.
            if (logger.isLoggable(BasicLevel.DEBUG))
              logger.log(BasicLevel.DEBUG, " -> invalid message");
            messageIds.remove(id);
            save();
            messagesTable.remove(id);
            // Deleting the message, if needed.
            if (durable)
              message.delete();

            // Setting the message's deliveryCount, denied and expired fields.
            deliveryAttempts = (Integer) deniedMsgs.remove(id);
            if (deliveryAttempts != null) {
              message.setDeliveryCount(deliveryAttempts.intValue());
              message.setRedelivered();
            }
           
            if (dmqManager == null) {
              dmqManager = new DMQManager(dmqId, null);
            }
            nbMsgsSentToDMQSinceCreation++;
            dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.EXPIRED);
          }
        } else {
          // Message has already been deleted.
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, " -> deleted message");

          messageIds.remove(id);
          deniedMsgs.remove(id);
          save();
        }
      }

      // Putting the kept message in the vector.
      if (keptMsg != null) {
        messageIds.remove(keptMsg.getIdentifier());
        deliveredIds.put(keptMsg.getIdentifier(), keptMsg.getIdentifier());
        save();

        // Setting the message's deliveryCount and denied fields.
        deliveryAttempts = (Integer) deniedMsgs.get(keptMsg.getIdentifier());
        if (deliveryAttempts == null)
          keptMsg.setDeliveryCount(1);
        else {
          keptMsg.setDeliveryCount(deliveryAttempts.intValue() +1);
          keptMsg.setRedelivered();
        }
        deliverables.add(keptMsg.getFullMessage().clone());

        if (logger.isLoggable(BasicLevel.DEBUG))
          logger
              .log(BasicLevel.DEBUG, this + ": message " + keptMsg.getIdentifier() + " added for delivery.");
      } else {
        i++;
      }
    }
  
    // Sending the dead messages to the DMQ, if any:
    if (dmqManager != null)
      dmqManager.sendToDMQ();

    // Finally, returning the reply or null:
    if (! deliverables.isEmpty()) {
      nbMsgsDeliveredSinceCreation += deliverables.size();
      ConsumerMessages consM = new ConsumerMessages(requestId,
                                                    deliverables,
                                                    name,
                                                    false);
      if (! toListener) requestId = -1;

      return consM;
    }
    return null;
  }

  /**
   * Acknowledges messages.
   */
  void acknowledge(Iterator acks) {
    while (acks.hasNext()) {
      String id = (String) acks.next();
      acknowledge(id);
    }
  }

  void acknowledge(String id) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ": acknowledges message: " + id);
   
    deliveredIds.remove(id);
    deniedMsgs.remove(id);
    save();
    Message msg = (Message) messagesTable.get(id);
   
    // Message may be null if it is not valid anymore
    if (msg != null) {
      decrAckCounters(id, msg);
    }
  }

  /**
   * Denies messages.
   */
  void deny(Iterator denies) {
    deny(denies, true);
  }

  /**
   * Denies the messages.
   *
   * @param denies all ids of the messages to deny
   * @param remove true to remove messages from deliveredIds map. Must be false
   *          when denies iterates over deliveredIds map keys, to avoid a
   *          ConcurrentModificationException.
   */
  private void deny(Iterator denies, boolean remove) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, this + ".deny(" + denies + ')');
    String id;
    Message message;
    int deliveryAttempts = 1;
    int i;
    String currentId;
    long currentO;
    DMQManager dmqManager = null;

    denyLoop: while (denies.hasNext()) {
      id = (String) denies.next();

      if (remove) {
        String deliveredMsgId = (String) deliveredIds.remove(id);
        if (deliveredMsgId == null) {
          if (logger.isLoggable(BasicLevel.DEBUG))
            logger.log(BasicLevel.DEBUG, this + ": cannot deny message: " + id);
          continue denyLoop;
        }
      }
      save();
     
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, this + ": deny message: " + id);
     
      message = (Message) messagesTable.get(id);
     
      // Message may be null if it is not valid anymore
      if (message == null) continue denyLoop;
     
      Integer value = (Integer) deniedMsgs.get(id);
      if (value != null)
        deliveryAttempts = value.intValue() + 1;
     
      // If maximum delivery attempts is reached, the message is no more
      // deliverable to this subscriber.
      if (isUndeliverable(deliveryAttempts)) {
        deniedMsgs.remove(id);
        message.setDeliveryCount(deliveryAttempts);
        if (dmqManager == null)
          dmqManager = new DMQManager(dmqId, null);
        nbMsgsSentToDMQSinceCreation++;
        dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.UNDELIVERABLE);
        decrAckCounters(id, message);
      } else {
        // Else, putting it back to the deliverables vector according to its
        // original delivery order, and adding a new entry for it in the
        // denied messages table.
        if (logger.isLoggable(BasicLevel.DEBUG))
          logger.log(BasicLevel.DEBUG, " -> put back to the messages to deliver");
       
        i = 0;
        insertLoop:
        while (i < messageIds.size()) {
          currentId = (String) messageIds.get(i);
          Message currentMessage = (Message) messagesTable.get(currentId);
           
          // Message may be null if it is not valid anymore
          if (currentMessage != null) {
            currentO = currentMessage.order;
            if (currentO > message.order) break insertLoop;
            i++;
          } else {
            // Remove the invalid message
            messageIds.remove(i);
          }
        }
       
        messageIds.add(i, id);
        deniedMsgs.put(id, new Integer(deliveryAttempts));
      }
    }

    // Sending dead messages to the DMQ, if needed:
    if (dmqManager != null)
      dmqManager.sendToDMQ();

  }

  /**
   * Decreases the subscription's messages acknowledgement expectations,
   * deletes those not to be consumed anymore.
   */
  void delete() {
    messageIds.addAll(deliveredIds.keySet());

    for (Iterator allMessageIds = messageIds.iterator(); allMessageIds.hasNext();) {
      removeMessage((String) allMessageIds.next());
    }
  }
 
  /**
   * Returns <code>true</code> if a given message is considered as  undeliverable,
   * because its delivery count matches the subscription's threshold, if any, or the
   * server's default threshold value (if any).
   */
  private boolean isUndeliverable(int deliveryAttempts) {
    if (threshold == 0) return false;
   
    if (threshold > 0)
      return (deliveryAttempts >= threshold);
    else if (Queue.getDefaultThreshold() > 0)
      return (deliveryAttempts >= Queue.getDefaultThreshold());
    return false;
  }
 
  public long getNbMsgsSentToDMQSinceCreation() {
    return nbMsgsSentToDMQSinceCreation;
  }

  public long getNbMsgsDeliveredSinceCreation() {
    return nbMsgsDeliveredSinceCreation;
  }

  Message getSubscriptionMessage(String msgId) {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription.getSubscriptionMessage(" + msgId + ')');
   
    int index = messageIds.indexOf(msgId);
    if (index < 0) {
      // The message has been delivered
      if (logger.isLoggable(BasicLevel.DEBUG))
        logger.log(BasicLevel.DEBUG, " -> message not found");
     
      return null;
    }
    return (Message) messagesTable.get(msgId);
  }
 
  /**
   * Returns the description of a particular pending message. The message is
   * pointed out through its unique identifier.
   *
   * @param msgId The unique message's identifier.
   * @return the description of the message.
   *
   * @see org.objectweb.joram.mom.messages.MessageJMXWrapper
   */
  public CompositeData getMessage(String msgId) throws Exception {
    Message msg = getSubscriptionMessage(msgId);
    if (msg == null) return null;
   
    return MessageJMXWrapper.createCompositeDataSupport(msg);
  }

  /**
   * Returns the description of all pending messages.
   *
   * @return the description of the message.
   *
   * @see org.objectweb.joram.mom.messages.MessageJMXWrapper
   */
  public TabularData getMessages() throws Exception {
    return MessageJMXWrapper.createTabularDataSupport(messagesTable, messageIds);
  }

  public List getMessagesView() {
    List messages = new ArrayList();
    for (int i = 0; i < messageIds.size(); i++) {
      messages.add(messagesTable.get(messageIds.get(i)));
    }
    return messages;
  }

  public void deleteMessage(String msgId) {
    messageIds.remove(msgId);
    Message message = removeMessage(msgId);
    save();
    if (message != null) {
      DMQManager dmqManager = new DMQManager(dmqId, null);
      nbMsgsSentToDMQSinceCreation++;
      dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.ADMIN_DELETED);
      dmqManager.sendToDMQ();
    }
  }

  public void clear() {
    DMQManager dmqManager = null;
    for (int i = 0; i < messageIds.size(); i++) {
      String msgId = (String) messageIds.get(i);
      Message message = removeMessage(msgId);
      if (message != null) {
        if (dmqManager == null)
          dmqManager = new DMQManager(dmqId, null);
        nbMsgsSentToDMQSinceCreation++;
        dmqManager.addDeadMessage(message.getFullMessage(), MessageErrorConstants.ADMIN_DELETED);
      }
    }
    if (dmqManager != null)
      dmqManager.sendToDMQ();
    messageIds.clear();
    save();
  }

  /**
   * Removes a particular pending message in the subscription.
   * The message is pointed out through its unique identifier.
   *
   * @param msgId    The unique message's identifier.
   */
  Message removeMessage(String msgId) {
    Message message = (Message) messagesTable.get(msgId);
    if (message != null) {
      decrAckCounters(msgId, message);
    }
    return message;
  }

  private void decrAckCounters(String msgId, Message message) {
    message.acksCounter--;
    if (message.acksCounter == 0)
      messagesTable.remove(msgId);
    if (durable) {
      message.durableAcksCounter--;
      if (message.durableAcksCounter == 0)
        message.delete();
    }
  }
 
  private void save() {
    if (durable) proxy.setSave();
  }

  public void readBag(ObjectInputStream in) throws IOException, ClassNotFoundException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription[" + proxyId + "].readbag()");

    contextId = in.readInt();
    subRequestId = in.readInt();
    noLocal = in.readBoolean();
    noFiltering = in.readBoolean();
    active = in.readBoolean();
    requestId = in.readInt();
    toListener = in.readBoolean();
    requestExpTime = in.readLong();
  }

  public void writeBag(ObjectOutputStream out)
    throws IOException {
    if (logger.isLoggable(BasicLevel.DEBUG))
      logger.log(BasicLevel.DEBUG, "ClientSubscription[" + proxyId + "].writeBag()");

    out.writeInt(contextId);
    out.writeInt(subRequestId);
    out.writeBoolean(noLocal);
    out.writeBoolean(noFiltering);
    out.writeBoolean(active);
    out.writeInt(requestId);
    out.writeBoolean(toListener);
    out.writeLong(requestExpTime);
  }

  void cleanMessageIds() {
    messageIds.retainAll(messagesTable.keySet());
  }

}
TOP

Related Classes of org.objectweb.joram.mom.proxies.ClientSubscription

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.