Package com.sun.messaging.jmq.jmsserver.core

Source Code of com.sun.messaging.jmq.jmsserver.core.Topic

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2000-2010 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

/*
* @(#)Topic.java  1.57 11/16/07
*/

package com.sun.messaging.jmq.jmsserver.core;

import java.util.*;
import java.io.*;
import com.sun.messaging.jmq.jmsserver.util.FeatureUnavailableException;
import com.sun.messaging.jmq.jmsserver.util.BrokerException;
import com.sun.messaging.jmq.jmsserver.Globals;
import com.sun.messaging.jmq.util.lists.*;
import com.sun.messaging.jmq.jmsserver.util.lists.*;
import com.sun.messaging.jmq.io.SysMessageID;
import com.sun.messaging.jmq.util.log.*;
import com.sun.messaging.jmq.util.selector.*;
import com.sun.messaging.jmq.jmsserver.service.ConnectionUID;
import com.sun.messaging.jmq.jmsserver.service.Connection;
import com.sun.messaging.jmq.io.DestMetricsCounters;
import com.sun.messaging.jmq.jmsserver.core.cluster.*;


/**
* This class represents a topic destination
*/
public class Topic extends Destination
{

    static final long serialVersionUID = -5748515630523651753L;

    private static boolean DEBUG = false;


    private transient Map selectorToInterest;
    private transient List selectors;

    private transient Map remoteConsumers;
    private static int TOPIC_DEFAULT_PREFETCH = Globals.getConfig().
                  getIntProperty(Globals.IMQ+
                 ".autocreate.topic.consumerFlowLimit", 1000);

    private boolean hasNoLocalConsumers = false;

    int maxSharedConsumers = 0;
    int sharedPrefetch = 0;

    public static final String MAX_SHARE_CONSUMERS = "max_shared_consumers";
    public static final String SHARED_PREFETCH = "sharedPrefetch";


    public static final int AUTO_MAX_SHARED_CONSUMER_LIMIT =
            Globals.getConfig().getIntProperty(
                Globals.IMQ + ".autocreate.topic.maxNumSharedConsumers",
                -1);

    public static final int AUTO_MAX_SHARED_FLOW_LIMIT =
            Globals.getConfig().getIntProperty(
                Globals.IMQ + ".autocreate.topic.sharedConsumerFlowLimit",
                5);
    public static final int ADMIN_MAX_SHARED_CONSUMER_LIMIT =
            Globals.getConfig().getIntProperty(
                Globals.IMQ + ".admincreate.topic.maxNumSharedConsumers",
                -1);

    public static final int ADMIN_MAX_SHARED_FLOW_LIMIT =
            Globals.getConfig().getIntProperty(
                Globals.IMQ + ".admincreate.topic.sharedConsumerFlowLimit",
                5);

    public Hashtable getDebugState() {
        Hashtable ht = super.getDebugState();
        Hashtable sel = new Hashtable();
        synchronized(selectorToInterest) {
            Iterator itr = selectorToInterest.keySet().iterator();
            while (itr.hasNext()) {
               Selector selector = (Selector)itr.next();
               Set s = (Set)selectorToInterest.get(selector);
               Vector v = new Vector();
               synchronized (s) {
                   Iterator itr1 = s.iterator();
                   while (itr1.hasNext()) {
                       Consumer c =(Consumer)itr1.next();
                       v.add(String.valueOf(c.getConsumerUID().longValue()));
                   }
               }
               sel.put((selector == null ? "no selector" : selector.toString()), v);
            }
         }
         ht.put("selectorInfo", sel);
         ht.put(MAX_SHARE_CONSUMERS, new Integer(maxSharedConsumers));
         ht.put(SHARED_PREFETCH, new Integer(sharedPrefetch));
         return ht;
    }
  
    // used only as a space holder when deleting corrupted destinations
    protected Topic(DestinationUID uid)
    {
        super(uid);
    }          
       
    protected Topic(String destination, int type,
           boolean store, ConnectionUID id, boolean autocreate)
        throws FeatureUnavailableException, BrokerException, IOException
    {
        super(destination,type,store, id,autocreate);

        maxPrefetch = TOPIC_DEFAULT_PREFETCH;

        if (autocreate) {
            maxSharedConsumers=AUTO_MAX_SHARED_CONSUMER_LIMIT;
            sharedPrefetch=AUTO_MAX_SHARED_FLOW_LIMIT;
        } else {
            maxSharedConsumers=ADMIN_MAX_SHARED_CONSUMER_LIMIT;
            sharedPrefetch=ADMIN_MAX_SHARED_FLOW_LIMIT;
        }
    }

    protected void initVar() {
       selectorToInterest = new HashMap();
       selectors = new ArrayList();
       remoteConsumers = new HashMap();
    }

    public void unload(boolean refs) {
        super.unload(refs);
        if (refs) {
            // remove the refs
            Iterator i = consumers.values().iterator();
            while (i.hasNext()) {
                Consumer con = (Consumer)i.next();
                con.unloadMessages();
            }
        }
    }

    public int getUnackSize()
    {
        Set s = null;
        synchronized (destMessages) {
            s = new HashSet(destMessages.values());
        }
        int counter = 0;
        Iterator itr = s.iterator();
        while (itr.hasNext()) {
            PacketReference ref = (PacketReference)itr.next();
            if (!ref.isInvalid() && !ref.isDestroyed() &&
                 (ref.getDeliverCnt() -
                  ref.getCompleteCnt() > 0)) {
                counter ++;
            }
        }
        return counter;
    }


    public Set routeAndMoveMessage(PacketReference oldRef,
                  PacketReference newRef)
  throws IOException, BrokerException
    {
        throw new RuntimeException("XXX not implemented");
    }


    public boolean queueMessage(PacketReference pkt, boolean trans)
        throws BrokerException
    {
        // if there are no consumers, throw it away
        if (!trans && consumers.size() == 0) {
            return false;
        }
        return super.queueMessage(pkt, trans);
    }


    /**
     * handles transient data when class is deserialized
     */
    private void readObject(java.io.ObjectInputStream ois)
        throws IOException, ClassNotFoundException
    {
        initVar();
        ois.defaultReadObject();
    }


    public void eventOccured(EventType type,  Reason reason,
            Object source, Object OrigValue, Object NewValue,
            Object userdata)
    {
        super.eventOccured(type,reason, source,
              OrigValue, NewValue, userdata);
    }

    /**
     * @deprecated
     */
    public void routeNewMessage(SysMessageID  id)
         throws BrokerException, SelectorFormatException
    {
        PacketReference ref = (PacketReference)destMessages.get(id);
        Set s = routeNewMessage(ref);
        forwardMessage(s, ref);
    }

    public int getConsumerCount() {
        int count =  super.getConsumerCount();
        // get 3.0 remote consumer count
        synchronized(remoteConsumers) {
            Iterator itr = remoteConsumers.values().iterator();
            while (itr.hasNext()) {
                RemoteConsumer rc = (RemoteConsumer)itr.next();
                count += rc.getConsumerCount();
           }
        }
        return count;
    }

    private Set matchRemoteConsumers(PacketReference msg, Set s)
         throws BrokerException, SelectorFormatException
    {
      
        synchronized(remoteConsumers) {
            if (remoteConsumers.isEmpty()) {
                return s;
            }
            Set newset = new HashSet(s);
            Iterator itr = remoteConsumers.values().iterator();
            while (itr.hasNext()) {
                RemoteConsumer rc = (RemoteConsumer)itr.next();
                if (rc.match(msg, newset)) {
                    s.add(rc);
                }
               
            }
            return newset;
        }
    }

    protected ConsumerUID[] routeLoadedTransactionMessage(
           PacketReference msg)
         throws BrokerException, SelectorFormatException
    {
        Set matching = new HashSet();

        Map props = null;
        Map headers = null;

        synchronized(selectorToInterest) {
            Iterator itr = selectorToInterest.keySet().iterator();
            while (itr.hasNext()) {
               Selector selector = (Selector)itr.next();
           
               if (selector != null) {
                   if (props == null && selector.usesProperties()) {
                       try {
                           props = msg.getProperties();
                       } catch (ClassNotFoundException ex) {
                           logger.logStack(Logger.ERROR,"INTERNAL ERROR", ex);
                           props = new HashMap();
                       }
                   }
                   if (headers == null && selector.usesFields()) {
                       headers = msg.getHeaders();
                   }
               }
                  
               boolean matches = (selector == null) ||
                   selector.match(props, headers);
               if (matches) {
                   if (DEBUG) {
                       logger.log(Logger.INFO,"Selector " + selector
                          + " Matches " + msg.getSysMessageID());
                   }
                   Set s = (Set)selectorToInterest.get(selector);
                   if (s == null) continue;
                   synchronized (s) {
                       matching.addAll(s);
                   }
               }
            }
        }

        // deal w/ isLocal

        // OK .. the logic is a little different than previous releases
        //
        // If we are a subscription and have a ClientID ... compare clientID's
        // otherwise
        // Compare ConsumerUIDs

        HashSet hs = new HashSet();
        Iterator nlitr = matching.iterator();
        ConnectionUID pcuid = msg.getProducingConnectionUID();
        String clientid = msg.getClientID();
        while (nlitr.hasNext()) {
            Consumer c = (Consumer)nlitr.next();
            if (c.getNoLocal()) {
                // fix for 5025241 Durable subscriber with noLocal=true
                //         receives self-published msgs
                if (c instanceof Subscription && clientid != null &&
                    ((Subscription)c).getClientID() != null &&
                    ((Subscription)c).getClientID().equals(clientid)) {
                    // check subscription clientID case
                    nlitr.remove();
                } else if (c.getConsumerUID().getConnectionUID() ==
                       pcuid) {
                    nlitr.remove();
                } else {
                    hs.add(c.getConsumerUID());
                }
            } else {
                hs.add(c.getConsumerUID());
            }
        }
        return (ConsumerUID[])hs.toArray(new ConsumerUID[0]);
    }
   
   
    public void unrouteLoadedTransactionAckMessage(PacketReference ref,
      ConsumerUID consumerId) throws BrokerException {
    logger.log(Logger.DEBUG,
        "unrouteLoadedTransactionAckMessage num consumers = "
            + consumers.size());
    // do nothing as consumers not attatched yet?
    // need to check this
    // what happens when consumer attatches?
    Consumer consumer = getConsumer(consumerId);
    if (consumer == null) {
      logger.log(Logger.DEBUG, "could not find consumer for "
          + consumerId);
    } else
      consumer.unrouteMessage(ref);
  }

    public Set routeNewMessage(PacketReference  msg)
         throws BrokerException, SelectorFormatException
    {
        Set matching = new HashSet();

        Map props = null;
        Map headers = null;

        for (int i=0; i < selectors.size(); i ++) {
            Selector selector = null;
            try {
                //LKS-XXX NOTE: don't need selectors !!!
                selector = (Selector)selectors.get(i);
            } catch (Exception ex) {
                continue; // selector was removed
            }
            if (selector == null ) {
                Set s = (Set)selectorToInterest.get(selector);
                if (s == null) continue;
                synchronized (s) {
                    matching.addAll(s);
                }
            } else {
                if (props == null && selector.usesProperties()) {
                   try {
                       props = msg.getProperties();
                   } catch (ClassNotFoundException ex) {
                       logger.logStack(Logger.ERROR,"INTERNAL ERROR", ex);
                       props = new HashMap();
                   }
                }
                if (headers == null && selector.usesFields()) {
                    headers = msg.getHeaders();
                }
                if (selector.match(props, headers)) {
                       Set s = (Set)selectorToInterest.get(selector);
                       if (s != null) {
                           synchronized(s) {
                               matching.addAll(s);
                           }
                       }
                }
           }
        }

        // deal w/ isLocal
        if (hasNoLocalConsumers) {
            Iterator nlitr = matching.iterator();
            ConnectionUID pcuid = msg.getProducingConnectionUID();
            String clientid = msg.getClientID();
            while (nlitr.hasNext()) {
                Consumer c = (Consumer)nlitr.next();
                if (c.getNoLocal()) {
                    // fix for 5025241 Durable subscriber with noLocal=true
                    //         receives self-published msgs
                    if (c instanceof Subscription && clientid != null &&
                        ((Subscription)c).getClientID() != null &&
                        ((Subscription)c).getClientID().equals(clientid)) {
                        // check subscription clientID case
                        nlitr.remove();
                    } else if (c.getConsumerUID().getConnectionUID() ==
                       pcuid) {
                        nlitr.remove();
                    }
                }
             }
        }
        if (matching.isEmpty()) {
            removeMessage(msg.getSysMessageID(), RemoveReason.ACKNOWLEDGED);
            return null;
        } else {
            msg.store(matching);
        }

        return matching;
   }
   
    /**
     * @param msg
     * @param forStoreOnly specifies the routing info is for storage only,
     * so only need to apply selectors for consumers that 
     * @return
     * @throws BrokerException
     * @throws SelectorFormatException
     */
    public Set routeMessage(PacketReference msg, boolean forStoreOnly) throws BrokerException,
      SelectorFormatException {
    Set matching = new HashSet();

    Map props = null;
    Map headers = null;

    for (int i = 0; i < selectors.size(); i++) {
      Selector selector = null;
      try {
        // LKS-XXX NOTE: don't need selectors !!!
        selector = (Selector) selectors.get(i);
      } catch (Exception ex) {
        continue; // selector was removed
      }
      if (selector == null) {
        Set s = (Set) selectorToInterest.get(selector);
        if (s == null)
          continue;
        synchronized (s) {
          matching.addAll(s);
        }
      } else {
       
       
        if (props == null && selector.usesProperties()) {
          try {
            props = msg.getProperties();
          } catch (ClassNotFoundException ex) {
            logger.logStack(Logger.ERROR, "INTERNAL ERROR", ex);
            props = new HashMap();
          }
        }
        if (headers == null && selector.usesFields()) {
          headers = msg.getHeaders();
        }
        Set s = (Set) selectorToInterest.get(selector);
        if(s==null)
          continue;
       
        if (forStoreOnly) {
          // optimisation...
          // with this option, no need to match selector if no
          // consumers need storing

          boolean needsStoring = false;
          synchronized (s) {
            Iterator iter = s.iterator();
            while (iter.hasNext()) {
              Consumer consumer = (Consumer) iter.next();
              if (consumer != null
                  && consumer.getStoredConsumerUID().shouldStore) {
                // at least one of the consumers needs storing,
                // so
                // we will need to check the selector
                needsStoring = true;
                break;
              }
            }
            if (!needsStoring) {
              continue;
            }
          }
        }
       
        if (selector.match(props, headers)) {         
          synchronized (s) {
            matching.addAll(s);
          }
         
        }
      }
    }

    // deal w/ isLocal
    if (hasNoLocalConsumers) {
      Iterator nlitr = matching.iterator();
      ConnectionUID pcuid = msg.getProducingConnectionUID();
      String clientid = msg.getClientID();
      while (nlitr.hasNext()) {
        Consumer c = (Consumer) nlitr.next();
        if (c.getNoLocal()) {
          // fix for 5025241 Durable subscriber with noLocal=true
          // receives self-published msgs
          if (c instanceof Subscription
              && clientid != null
              && ((Subscription) c).getClientID() != null
              && ((Subscription) c).getClientID()
                  .equals(clientid)) {
            // check subscription clientID case
            nlitr.remove();
          } else if (c.getConsumerUID().getConnectionUID() == pcuid) {
            nlitr.remove();
          }
        }
      }
    }   
    return matching;
  }
   
    public ConsumerUID[] calculateStoredInterests(PacketReference ref)
      throws BrokerException, SelectorFormatException {
    // this will return null if message is not persistent

    ConsumerUID[] storedInterests = null;
    try {

      Collection allConsumers = routeMessage(ref, true);
      storedInterests = ref.getRoutingForStore(allConsumers);

    } catch (BrokerException ex) {
      throw ex;
    } catch (RuntimeException ex) {
      throw new BrokerException(ex.toString(), ex);
    }
    return storedInterests;
  }
   
    public void forwardOrphanMessages(Collection refs,
                  ConsumerUID consumer)
         throws BrokerException
    {
        BrokerException ex = new BrokerException("INTERNAL ERROR: Unexpected call");
        logger.logStack(Logger.ERROR, ex.getMessage(), ex);
        throw ex;
    }

    /* called from transaction code */
    public void forwardOrphanMessage(PacketReference ref,
                  ConsumerUID consumer)
         throws BrokerException
    {
        Consumer c = getConsumer(consumer);
        if (c == null) {
            // no more consumer -> ack us
           logger.log(Logger.DEBUG, "Could not find " + consumer + " in " + getName() + "size="+consumers.size());
           Iterator iter = consumers.keySet().iterator();
           while (iter.hasNext())
           {
             Object v = iter.next();
             logger.log(Logger.DEBUG, "consumer= " + v);
              
           }
            
            logger.log(Logger.DEBUG,"Dumping orphan message " + ref);
            try {
                if (ref.acknowledged(consumer,
                        consumer, false, false)) {
                    removeMessage(ref.getSysMessageID(),
                        RemoveReason.ACKNOWLEDGED);
                }
            } catch (Exception ex) {
                logger.logStack(Logger.DEBUG,"Error forwarding orphan", ex);
            }
        }
 
        Set matches = new HashSet();
        matches.add(c);
        forwardMessage(matches, ref);     
    }


   public void forwardMessage(Set matching, PacketReference msg)
         throws BrokerException
   {
        Set remote = null;


        if (matching == null || matching.isEmpty()) {
            removeMessage(msg.getSysMessageID(), RemoveReason.ACKNOWLEDGED);
        } else {
            Iterator con_itr = matching.iterator();
            while (con_itr.hasNext()) {
                   Consumer c = (Consumer)con_itr.next();
                   // OK .. this is ugly
                   // I really wanted to treat the remote consumers the
                   // same as local consumers (merging topics)
                   // but I dont want to send multiple copies of the
                   // message for 3.0 clients PLUS there looks like
                   // there is a timing hole in 3.0 where a message
                   // arriving late (e.g. durable) could be removed
                   // without tracking acks IF it was received just
                   // as the last consumer was acking it
                   // when we only send over 1 interest to deal w/ the
                   // ack flood, this should improve (maybe raptor
                   // but probably 3.6)
                   //
                   // For now .. I'm handling the non-durable clients seperately
                   //
                   if (c.isFalconRemote()) { // message to a remote broker
                       if (remote == null) {
                           remote = new HashSet();
                       }
                       remote.add(c);
                   } else {
                    
                       if (!c.routeMessage(msg, false)) {
                           boolean acked = false;
                           try {
                               ConsumerUID cid = c.getConsumerUID();
                               acked = msg.acknowledged(cid,
                                     c.getStoredConsumerUID(),
                                     !cid.isUnsafeAck(), true);
                               if (acked)
                                   removeMessage(msg.getSysMessageID(), null);
                           } catch (IOException ex) {
                              //XXX ??
                           }
                       }
                   }
           }
        }
        if (remote != null && !remote.isEmpty()) {
            // send the messages directly to the remote broker
            // do not pass go, do not collect $200, do not
            // deal w/ flow control
            Globals.getClusterBroadcast().forwardMessage(msg, remote);
        }
    }      

    public Consumer addConsumer(Consumer c, boolean notify, Connection conn)
        throws BrokerException, SelectorFormatException
    {

        if (c instanceof Subscription) {
            if (consumers.get(c.getConsumerUID()) != null) {
                return null;
            }
        }
        super.addConsumer(c,notify, conn);
        hasNoLocalConsumers |= c.getNoLocal();

        Selector selector = c.getSelector();
        Set s = null;

        synchronized (selectorToInterest) {
            s = (Set)selectorToInterest.get(selector);
            if ( s == null) {
                s = new HashSet();
                selectorToInterest.put(selector,s);
                selectors.add(selector);
            }
        }
        synchronized (s) {
            s.add(c);
        }

        if (!(c instanceof Subscription)) {
            notifyConsumerAdded(c, conn);
        }

        return null;
    }

    public void removeConsumer(ConsumerUID interest, boolean notify)
        throws BrokerException {
        removeConsumer(interest, null, false, notify);
    }

    public void removeConsumer(ConsumerUID interest, Map remotePendings,
                               boolean remoteCleanup, boolean notify)
                               throws BrokerException
    {
        Consumer c = (Consumer)consumers.get(interest);
        if (c == null) {
            return;
        }

        Set s = null;
        synchronized (selectorToInterest) {
            s = (Set)selectorToInterest.get(c.getSelector());
            if (s != null) {
                synchronized (s) {
                    s.remove(c);
                    if (s.isEmpty()) {
                        selectorToInterest.remove(c.getSelector());
                        selectors.remove(c.getSelector());
                    }
                }
            }
        }
        super.removeConsumer(interest, remotePendings, remoteCleanup, notify);

        if (!(c instanceof Subscription)) {
            notifyConsumerRemoved();
        }
    }


    public void sort(Comparator c) {
    }

    protected void getDestinationProps(Map m) {
        super.getDestinationProps(m);
        m.put(MAX_SHARE_CONSUMERS,new Integer(maxSharedConsumers));
        m.put(SHARED_PREFETCH, new Integer(sharedPrefetch));
    }

    public void setDestinationProperties(Map m)
        throws BrokerException
    {
        super.setDestinationProperties(m);
        if (m.get(MAX_SHARE_CONSUMERS) != null) {
           try {
               setMaxSharedConsumers(((Integer)m.get(
                     MAX_SHARE_CONSUMERS)).intValue());
           } catch (Exception ex) {
               logger.log(Logger.INFO,"Internal Error ", ex);
           }

        }
        if (m.get(SHARED_PREFETCH) != null) {
           try {
               setSharedFlowLimit(((Integer)m.get(
                     SHARED_PREFETCH)).intValue());
           } catch (Exception ex) {
               logger.log(Logger.INFO,"Internal Error ", ex);
           }

        }
     }
    public DestMetricsCounters getMetrics() {
        DestMetricsCounters dmc = super.getMetrics();
        return dmc;

        // TBD add MAX_SHARE_CONSUMERS, SHARED_PREFETCH
    }

   
    public void setMaxSharedConsumers(int max) {
       maxSharedConsumers = max;
    }
    public void setSharedFlowLimit(int prefetch) {
       sharedPrefetch = prefetch;
    }

    public int getMaxNumSharedConsumers() {
        return maxSharedConsumers;
    }
    public int getSharedConsumerFlowLimit() {
        return sharedPrefetch;
    }

}
TOP

Related Classes of com.sun.messaging.jmq.jmsserver.core.Topic

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.