Package flex.messaging

Source Code of flex.messaging.MessageClient$SubscriptionInfo

/*************************************************************************
*
* ADOBE CONFIDENTIAL
* __________________
*
*  [2002] - [2007] Adobe Systems Incorporated
*  All Rights Reserved.
*
* NOTICE:  All information contained herein is, and remains
* the property of Adobe Systems Incorporated and its suppliers,
* if any.  The intellectual and technical concepts contained
* herein are proprietary to Adobe Systems Incorporated
* and its suppliers and may be covered by U.S. and Foreign Patents,
* patents in process, and are protected by trade secret or copyright law.
* Dissemination of this information or reproduction of this material
* is strictly forbidden unless prior written permission is obtained
* from Adobe Systems Incorporated.
*/
package flex.messaging;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;

import edu.emory.mathcs.backport.java.util.concurrent.CopyOnWriteArrayList;

import flex.messaging.client.FlexClient;
import flex.messaging.log.LogCategories;
import flex.messaging.log.Log;
import flex.messaging.messages.AsyncMessage;
import flex.messaging.messages.CommandMessage;
import flex.messaging.messages.Message;
import flex.messaging.services.MessageService;
import flex.messaging.services.messaging.selector.JMSSelector;
import flex.messaging.services.messaging.selector.JMSSelectorException;
import flex.messaging.util.TimeoutAbstractObject;
import flex.messaging.util.StringUtils;

/**
* Represents a client-side MessageAgent instance.
* Currently a server-side MessageClient is only created if its client-side counterpart has subscribed
* to a destination for pushed data (e.g. Consumer). Client-side Producers do not result in the creation of
* corresponding server-side MessageClient instances.
*
* Client-side MessageAgents communicate with the server over a Channel that corresponds to a FlexSession.
* Server-side MessageClient instances are always created in the context of a FlexSession and when the FlexSession
* is invalidated any associated MessageClients are invalidated as well.
*
* MessageClients may also be timed out on a per-destination basis and this is based on subscription inactivity.
* If no messages are pushed to the MessageClient within the destination's subscription timeout period the
* MessageClient will be shutdown even if the associated FlexSession is still active and connected.
* Per-destination subscription timeout is an optional configuration setting, and should only be used when inactive
* subscriptions should be shut down opportunistically to preserve server resources.
*/
public class MessageClient extends TimeoutAbstractObject implements Serializable
{
    //--------------------------------------------------------------------------
    //
    // Public Static Variables
    //
    //--------------------------------------------------------------------------

    /**
     * Log category for MessageClient related messages.
     */
    public static final String MESSAGE_CLIENT_LOG_CATEGORY = LogCategories.CLIENT_MESSAGECLIENT;    
   
    //--------------------------------------------------------------------------
    //
    // Static Constants
    //
    //--------------------------------------------------------------------------
   
    /**
     * Serializable to support broadcasting subscription state across the cluster for
     * optimized message routing.
     */
    static final long serialVersionUID = 3730240451524954453L;

    //--------------------------------------------------------------------------
    //
    // Static Variables
    //
    //--------------------------------------------------------------------------   
   
    /**
     * The list of MessageClient created listeners.
     */
    private static final CopyOnWriteArrayList createdListeners = new CopyOnWriteArrayList();
   
    //--------------------------------------------------------------------------
    //
    // Static Methods
    //
    //--------------------------------------------------------------------------
   
    /**
     * Adds a MessageClient created listener.
     *
     * @see flex.messaging.MessageClientListener
     *
     * @param listener The listener to add.
     */
    public static void addMessageClientCreatedListener(MessageClientListener listener)
    {
        if (listener != null)
            createdListeners.addIfAbsent(listener);
    }   
   
    /**
     * Removes a MessageClient created listener.
     *
     * @see flex.messaging.MessageClientListener
     *
     * @param listener The listener to remove.
     */
    public static void removeMessageClientCreatedListener(MessageClientListener listener)
    {
        if (listener != null)
            createdListeners.remove(listener);
    }

    //--------------------------------------------------------------------------
    //
    // Private Static Methods
    //
    //--------------------------------------------------------------------------
   
    /**
     * Utility method.
     */
    private static boolean equalStrings(String a, String b)
    {
        return a == b || (a != null && a.equals(b));
    }

    /**
     * Utility method.
     */
    private static int compareStrings(String a, String b)
    {
        if (a == b)
            return 0;

        if (a != null && b != null)
            return a.compareTo(b);

        if (a == null)
            return -1;

        return 1;
    }   
   
    //--------------------------------------------------------------------------
    //
    // Constructor
    //
    //--------------------------------------------------------------------------

    /**
     * @exclude
     * Constructs a new MessageClient for local use.
     *
     * @param clientId The clientId for the MessageClient.
     * @param destination The destination the MessageClient is subscribed to.
     * @param endpointId The Id of the endpoint this MessageClient subscription was created over.
     */
    public MessageClient(Object clientId, MessageDestination destination, String endpointId)
    {
        this(clientId, destination, endpointId, true);
    }   
   
    /**
     * @exclude
     * Constructs a new MessageClient.
     *
     * @param clientId The clientId for the MessageClient.
     * @param destination The destination the MessageClient is subscribed to.
     * @param endpointId The Id of the endpoint this MessageClient subscription was created over.
     * @param useSession RemoteMessageClient instances should not be associated with a FlexSession (pass false).
     */
    public MessageClient(Object clientId, MessageDestination destination, String endpointId, boolean useSession)
    {
        valid = true;       
        this.clientId = clientId;
        this.destination = destination;
        this.endpointId = endpointId;
        destinationId = destination.getId();
        updateLastUse(); // Initialize last use timestamp to construct time.

        /* If this is for a remote server, we do not associate with the session. */
        if (useSession)
        {
            flexSession = FlexContext.getFlexSession();
            flexSession.registerMessageClient(this);

            flexClient = FlexContext.getFlexClient();
            flexClient.registerMessageClient(this);           
           
            // Notify MessageClient created listeners.
            if (!createdListeners.isEmpty())
            {
                // CopyOnWriteArrayList is iteration-safe from ConcurrentModificationExceptions.
                for (Iterator iter = createdListeners.iterator(); iter.hasNext();)
                    ((MessageClientListener)iter.next()).messageClientCreated(this);
            }
        }
        else
        {
            flexClient = null;
            flexSession = null;
            // Use an instance level lock.
            lock = new Object();          
            // On a remote server we don't notify created listeners.
        }              
       
        if (Log.isDebug())
            Log.getLogger(MESSAGE_CLIENT_LOG_CATEGORY).debug("MessageClient created with clientId '" + this.clientId + "' for destination '" + destinationId + "'.");       
    }   

    //--------------------------------------------------------------------------
    //
    // Variables
    //
    //--------------------------------------------------------------------------   
   
    /**
     *  This flag is set to true when the client channel that this subscription was
     *  established over is disconnected.
     *  It supports cleaning up per-endpoint outbound queues maintained by the FlexClient.
     *  If the client notifies the server that its channel is disconnecting, the FlexClient
     *  does not need to maintain an outbound queue containing a subscription invalidation
     *  message for this MessageClient to send to the client.
     */
    private volatile boolean clientChannelDisconnected;
   
    /**
     *  The clientId for the MessageClient.
     *  This value is specified by the client directly or is autogenerated on the client.
     */
    protected final Object clientId;

    /**
     * Internal reference to the associated MessageDestination; don't expose this in the public API.
     */
    protected final MessageDestination destination;
   
    /**
     *  The destination the MessageClient is subscribed to.
     */
    protected final String destinationId;

    /**
     * The set of session destroy listeners to notify when the session is destroyed.
     */
     private transient volatile CopyOnWriteArrayList destroyedListeners;
   
    /**
     * The Id for the endpoint this MessageClient subscription was created over.
     */
    private String endpointId;
   
    /**
     * The FlexClient associated with the MessageClient.
     */
    private final transient FlexClient flexClient;
   
    /**
     * The FlexSession associated with the MessageClient.
     * Not final because this needs to be reset if the subscription fails over to a new endpoint.
     */
    private transient FlexSession flexSession;
   
    /**
     * Flag used to break cycles during invalidation.
     */
    private boolean invalidating;
   
    /**
     * The lock to use to guard all state changes for the MessageClient.
     */
    protected Object lock = new Object();
   
    /**
     * Flag indicating whether the MessageClient is attempting to notify the remote client of
     * its invalidation.
     */
    private volatile boolean attemptingInvalidationClientNotification;
   
    /**
     * A counter used to control invalidation for a MessageClient that has multiple
     * subscriptions to its destination.
     * Unsubscribing from one will not invalidate the MessageClient as long as other
     * subscriptions remain active.
     */
    private transient int numReferences;
   
    /**
     * A list of all of the subscriptions managed by this message client.
     * This is a TreeSet so that it is efficient in memory for small sets and
     * yet still efficient to search and remove for large sets.  Since this
     * gets used for representing all of the remote subscriptions for a given
     * client, the list may be large in some cases.
     */
    protected final Set subscriptions = new TreeSet();
   
    /**
     * Flag indicating whether this client is valid or not.
     */
    protected boolean valid;   
   
    /**
     * Flag that indicates whether the MessageClient has a per-destination subscription timeout.
     * If false, the MessageClient will remain valid until its associated FlexSession is invalidated.
     */
    private volatile boolean willTimeout;

    /**
     * Has anyone explicitly registered this message client.  This indicates that
     * there is a reference to this MessageClient which is not an explicit subscription.
     * This is a hook for FDMS and other adapters which want to use pushMessageToClients
     * with clientIds but that do not want the subscription manager to manage subscriptions
     * for them.
     */
    private volatile boolean registered = false;

    //--------------------------------------------------------------------------
    //
    // Public Methods
    //
    //--------------------------------------------------------------------------   
   
    /**
     * Returns the clientId for the MessageClient.
     *
     * @return The clientId for the MessageClient.
     */
    public Object getClientId()
    {
        return clientId; // Field is final; no need to sync.
    }
   
    /**
     * Returns the destination the MessageClient is subscribed to.
     *
     * @return The destination the MessageClient is subscribed to.
     */
    public String getDestinationId()
    {
        return destinationId; // Field is final; no need to sync.
    }
   
    /**
     * Returns the Id for the endpoint the MessageClient subscription was created over.
     *
     * @return The Id for the endpoint the MessageClient subscription was created over.
     */
    public String getEndpointId()
    {
        return endpointId; // Field is final; no need to sync.
    }
   
    /**
     * Returns the FlexClient associated with this MessageClient.
     *
     * @return The FlexClient assocaited with this MessageClient.
     */
    public FlexClient getFlexClient()
    {
        return flexClient; // Field is final; no need to sync.
    }
   
    /**
     * Returns the FlexSession associated with this MessageClient.
     *
     * @return The FlexSession associated with this MessageClient.
     */
    public FlexSession getFlexSession()
    {
        synchronized (lock)
        {
            return flexSession;
        }
    }
   
    /**
     * @return The number of subscriptions associated with this MessageClient.
     */
    public int getSubscriptionCount()
    {
        int count;
       
        synchronized (lock)
        {
            count = (subscriptions != null) ? subscriptions.size() : 0;
        }
       
        return count;
    }   
   
    /**
     * @exclude
     * This is used for FlexClient outbound queue management. When a MessageClient is invalidated
     * if it is attempting to notify the client, then we must leave the outbound queue containing
     * the notification in place. Otherwise, any messages queued for the subscription may be
     * removed from the queue and possibly shut down immediately.
     */
    public boolean isAttemptingInvalidationClientNotification()
    {
        return attemptingInvalidationClientNotification;
    }
   
    /**
     * @exclude
     * This is set to true when the MessageClient is invalidated due to the client
     * channel the subscription was established over disconnecting.
     * It allows the FlexClient class to cleanup the outbound queue for the channel's
     * corresponding server endpoint for the remote client, because we know that no
     * currently queued messages need to be retained for delivery.
     */
    public void setClientChannelDisconnected(boolean value)
    {
        clientChannelDisconnected = value;
    }
   
    /**
     * @exclude
     */
    public boolean isClientChannelDisconnected()
    {
        return clientChannelDisconnected;
    }

    /**
     * @exclude
     * This is true when some code other than the SubscriptionManager
     * is maintaining subscriptions for this message client.  It ensures
     * that we have this MessageClient kept around until the session
     * expires.
     */
    public void setRegistered(boolean reg)
    {
        registered = reg;
    }
   
    /**
     * @exclude
     */
    public boolean isRegistered()
    {
        return registered;
    }
   
    /**
     * Adds a MessageClient destroy listener.
     *
     * @see flex.messaging.MessageClientListener
     *
     * @param listener The listener to add.
     */
    public void addMessageClientDestroyedListener(MessageClientListener listener)
    {       
        if (listener != null)
        {
            checkValid();
           
            if (destroyedListeners == null)
            {
                synchronized (lock)
                {
                    if (destroyedListeners == null)
                        destroyedListeners = new CopyOnWriteArrayList();
                }
            }
                       
            destroyedListeners.addIfAbsent(listener);
        }
    }
   
    /**
     * Removes a MessageClient destroyed listener.
     *
     * @see flex.messaging.MessageClientListener
     *
     * @param listener The listener to remove.
     */
    public void removeMessageClientDestroyedListener(MessageClientListener listener)
    {      
        // No need to check validity; removing a listener is always ok.
        if (listener != null && destroyedListeners != null)
            destroyedListeners.remove(listener);
    }   
   
    /**
     * @exclude
     * Adds a subscription to the subscription set for this MessageClient.
     *
     * @param selector The selector expression used for the subscription.
     * @param subtopic The subtopic used for the subscription.
     */
    public void addSubscription(String selector, String subtopic)
    {
        synchronized (lock)
        {
            checkValid();
           
            incrementReferences();
            subscriptions.add(new SubscriptionInfo(selector, subtopic));
        }
    }
   
    /**
     * @exclude
     * Removes a subscription from the subscription set for this MessageClient.
     *
     * @param selector The selector expression for the subscription.
     * @param subtopic The subtopic for the subscription.
     * @return true if no subscriptions remain for this MessageClient; otherwise false.
     */
    public boolean removeSubscription(String selector, String subtopic)
    {
        synchronized (lock)
        {
            if (subscriptions.remove(new SubscriptionInfo(selector, subtopic)))
                return decrementReferences();
            else if (Log.isError())
                Log.getLogger(MessageService.LOG_CATEGORY).error("Error - unable to find subscription to remove for MessageClient: " + clientId + " selector: " + selector + " subtopic: " + subtopic);
            return numReferences == 0;           
        }
    }
   
    /**
     * @exclude
     * We use the same MessageClient for more than one subscription with different
     * selection criteria.  This tracks the number of subscriptions that are active
     * so that we know when we are finished. 
     */
    public void incrementReferences()
    {
        synchronized (lock)
        {
            numReferences++;
        }
    }   

    /**
     * @exclude
     * Decrements the numReferences variable and returns true if this was the last reference.
     */
    public boolean decrementReferences()
    {
        synchronized (lock)
        {
            if (--numReferences == 0)
            {
                cancelTimeout();
                if (destination.getThrottleManager() != null)
                    destination.getThrottleManager().removeClientThrottleMark(clientId);
                return true;
            }
            return false;           
        }
    }
   
    /**
     * @exclude
     * Invoked by SubscriptionManager at the end of handling a subscribe request.
     * If the request is updating an existing subscription the 'push' state in the associated FlexClient
     * needs to be updated to ensure that the current endpoint is used for this subscription.
     *
     * @param newEndpointId The id for the new endpoint that the subscription has failed over to.
     */
    public void resetEndpoint(String newEndpointId)
    {
        if (flexClient != null) // flexClient can be null in RemoteMessageClients
            flexClient.unregisterMessageClient(this);
       
        synchronized (lock)
        {
            endpointId = newEndpointId;               
            flexSession = FlexContext.getFlexSession();
        }
   
        if (flexClient != null) // flexClient can be null in RemoteMessageClients
            flexClient.registerMessageClient(this);
    }
   
    /**
     * @exclude
     * Used to test whether this client should receive this message
     * based on the list of subscriptions we have recorded for it.
     * It must match both the subtopic and the selector expression.
     * Usually this is done by the subscription manager - this logic is
     * only here to maintain api compatibility with one of the variants
     * of the pushMessageToClients which has subscriberIds and an evalSelector
     * property.
     *
     * @param message The message to test.
     */
    public boolean testMessage(Message message)
    {
        String subtopic = (String) message.getHeader(AsyncMessage.SUBTOPIC_HEADER_NAME);
        synchronized (lock)
        {
            for (Iterator it = subscriptions.iterator(); it.hasNext(); )
            {
                SubscriptionInfo si = (SubscriptionInfo) it.next();
                String csel = si.selector;
                String csub = si.subtopic;

                if (equalStrings(csub, subtopic))
                {
                    if (csel == null
                        return true;

                    JMSSelector selector = new JMSSelector(csel);
                    try
                    {
                        if (selector.match(message))
                        {
                            return true;
                        }
                    }
                    catch (JMSSelectorException jmse)
                    {
                        // Log a warning for this client's selector and continue
                        if (Log.isWarn())
                        {
                            Log.getLogger(JMSSelector.LOG_CATEGORY).warn("Error processing message selector: " +
                                 jmse.toString() + StringUtils.NEWLINE +
                                 "  incomingMessage: " + message + StringUtils.NEWLINE +
                                 "  selector: " + csel + StringUtils.NEWLINE);
                        }
                        return false;
                    }
                }
            }
        }
        return false;
    }

    /**
     * Returns true if the MessageClient is valid; false if it has been invalidated.
     *
     * @return true if the MessageClient is valid; otherwise false.
     */
    public boolean isValid()
    {
        synchronized (lock)
        {
            return valid;
        }
    }
   
    /**
     * Invalidates the MessageClient.
     */
    public void invalidate()
    {
        invalidate(false /* don't attempt to notify the client */);
    }
   
    /**
     * Invalidates the MessageClient, and optionally attempts to notify the client that
     * this subscription has been invalidated.
     * This overload is used when a subscription is timed out while the client is still
     * actively connected to the server but should also be used by any custom code on the server
     * that invalidates MessageClients but wishes to notify the client cleanly.
     *
     * @param notifyClient <code>true</code> to notify the client that its subscription has been
     *                     invalidated.
     */
    public void invalidate(boolean notifyClient)
    {
        synchronized (lock)
        {
            if (!valid || invalidating)
                return; // Already shutting down.
           
            invalidating = true; // This thread gets to shut the MessageClient down.
            cancelTimeout();
        }
       
        // Record whether we're attempting to notify the client or not.
        attemptingInvalidationClientNotification = notifyClient;
       
        // Build a subscription invalidation message and push to the client if it is still valid.
        if (notifyClient && flexClient != null && flexClient.isValid())
        {               
            CommandMessage msg = new CommandMessage();
            msg.setDestination(destination.getId());
            msg.setClientId(clientId);
            msg.setOperation(CommandMessage.SUBSCRIPTION_INVALIDATE_OPERATION);
            Set subscriberIds = new TreeSet();
            subscriberIds.add(clientId);
            try
            {
                ((MessageService)destination.getService()).pushMessageToClients(destination, subscriberIds, msg, false /* don't eval selector */);
            }
            catch (MessageException ignore) {}
        }       
       
        // Notify messageClientDestroyed listeners that we're being invalidated.
        if (destroyedListeners != null && !destroyedListeners.isEmpty())
        {
            for (Iterator iter = destroyedListeners.iterator(); iter.hasNext();)
            {
                ((MessageClientListener)iter.next()).messageClientDestroyed(this);               
            }
            destroyedListeners.clear();
        }

        // And generate unsubscribe messages for all of the MessageClient's subscriptions and
        // route them to the destination this MessageClient is subscribed to.
        // The reason we send a message to the service rather than just going straight to the SubscriptionManager
        // is that some adapters manage their own subscription state (i.e. JMS) in addition to us keeping track of
        // things with our SubscriptionManager.
        ArrayList unsubMessages = new ArrayList();
        synchronized (lock)
        {
            for (Iterator iter = subscriptions.iterator(); iter.hasNext();)
            {
                SubscriptionInfo subInfo = (SubscriptionInfo)iter.next();
                CommandMessage unsubMessage = new CommandMessage();
                unsubMessage.setDestination(destination.getId());
                unsubMessage.setClientId(clientId);
                unsubMessage.setOperation(CommandMessage.UNSUBSCRIBE_OPERATION);
                unsubMessage.setHeader(CommandMessage.SUBSCRIPTION_INVALIDATED_HEADER, Boolean.TRUE);
                unsubMessage.setHeader(CommandMessage.SELECTOR_HEADER, subInfo.selector);
                unsubMessage.setHeader(AsyncMessage.SUBTOPIC_HEADER_NAME, subInfo.subtopic);
                unsubMessages.add(unsubMessage);
            }
        }
        // Release the lock and send the unsub messages.
        for (Iterator iter = unsubMessages.iterator(); iter.hasNext();)
            destination.getService().serviceCommand((CommandMessage)iter.next());       
       
        synchronized (lock)
        {
            // If we didn't clean up all subscriptions log an error and continue with shutdown.
            int remainingSubscriptionCount = subscriptions.size();
            if (remainingSubscriptionCount > 0 && Log.isError())
                Log.getLogger(MESSAGE_CLIENT_LOG_CATEGORY).error("MessageClient: " + getClientId() + " failed to remove " + remainingSubscriptionCount + " subscription(s) during invalidation");
        }

        // If someone registered this message client, invalidating it will free
        // their reference which will typically also remove this message client.
        if (registered)
            destination.getSubscriptionManager().releaseMessageClient(this);
           
        synchronized (lock)
        {
            valid = false;
            invalidating = false;
        }
           
        if (Log.isDebug())
            Log.getLogger(MESSAGE_CLIENT_LOG_CATEGORY).debug("MessageClient with clientId '" + clientId + "' for destination '" + destinationId + "' has been invalidated.");
    }     

    /**
     * Pushes the supplied message and then invalidates the MessageClient.
     *
     * @param message The message to push to the client before invalidating.
     * When message is null, MessageClient is invalidated silently.
     */
    public void invalidate(Message message)
    {
        if (message != null)
        {
            message.setDestination(destination.getId());
            message.setClientId(clientId);

            Set subscriberIds = new TreeSet();
            subscriberIds.add(clientId);
            try
            {
                ((MessageService)destination.getService()).pushMessageToClients(destination, subscriberIds, message, false /* don't eval selector */);
            }
            catch (MessageException ignore) {}
           
            invalidate(true /* attempt to notify remote client */);
        }       
        else
        {
            invalidate();
        }
    }
   
    /**
     * @exclude
     * Compares this MessageClient to the specified object. The result is true if
     * the argument is not null and is a MessageClient instance with a matching
     * clientId value.
     *
     * @param o The object to compare this MessageClient to.
     * @return true if the MessageClient is equal; otherwise false.
     */
    public boolean equals(Object o)
    {
        if (o instanceof MessageClient)
        {
            MessageClient c = (MessageClient) o;
            if (c != null && c.getClientId().equals(clientId))
                return true;
        }
        return false;
    }
   
    /**
     * @exclude
     * Returns the hash code for this MessageClient. The returned value is
     * the hash code for the MessageClient's clientId property.
     *
     * @return The hash code value for this MessageClient.
     */
    public int hashCode()
    {
        return getClientId().hashCode();
    }      

    /**
     * @exclude
     * The String representation of this MessageClient is returned (its clientId value).
     *
     * @return The clientId value for this MessageClient.
     */
    public String toString()
    {
        return String.valueOf(clientId);
    }
   
    //----------------------------------
    //  TimeoutAbstractObject overrides
    //----------------------------------    
   
    /**
     * @exclude
     * Implements TimeoutCapable.
     * This method returns the timeout value configured for the MessageClient's destination.
     */
    public long getTimeoutPeriod()
    {
        return destination.getSubscriptionManager().getSubscriptionTimeoutMillis();
    }

    /**
     * @exclude
     * Implements TimeoutCapable.
     * This method is invoked when the MessageClient has timed out and it
     * invalidates the MessageClient.
     */
    public void timeout()
    {
        invalidate(true /* notify client */);
    }
   
    /**
     * @exclude
     * Returns true if a timeout task is running for this MessageClient.
     */
    public boolean isTimingOut()
    {
        return willTimeout;
    }
   
    /**
     * @exclude
     * Records whether a timeout task is running for this MessageClient.
     */
    public void setTimingOut(boolean value)
    {
        willTimeout = value;
    }   

    //--------------------------------------------------------------------------
    //
    // Private Methods
    //
    //--------------------------------------------------------------------------   
   
    /**
     * Utility method that tests validity and throws an exception if the instance
     * has been invalidated.
     */
    private void checkValid()
    {
        synchronized (lock)
        {
            if (!valid)
            {
                throw new RuntimeException("MessageClient has been invalidated."); // TODO - localize
            }
        }
    }

    //--------------------------------------------------------------------------
    //
    // Nested Classes
    //
    //--------------------------------------------------------------------------   
   
    /**
     * Represents a MessageClient's subscription to a destination.
     * It captures the optional selector expression and subtopic for the
     * subscription.
     */
    protected static class SubscriptionInfo implements Comparable
    {
        public String selector, subtopic;

        SubscriptionInfo(String sel, String sub)
        {
            this.selector = sel;
            this.subtopic = sub;
        }

        public boolean equals(Object o)
        {
            if (o instanceof SubscriptionInfo)
            {
                SubscriptionInfo other = (SubscriptionInfo) o;
                return equalStrings(other.selector, selector) &&
                       equalStrings(other.subtopic, subtopic);
            }
            return false;
        }

        public int hashCode()
        {
            return (selector == null ? 0 : selector.hashCode()) +
                   (subtopic == null ? 1 : subtopic.hashCode());
        }

        /**
         * Compares the two subscription infos (being careful to
         * ensure we compare in a consistent way if the arguments
         * are switched)
         */
        public int compareTo(Object o)
        {
            SubscriptionInfo other = (SubscriptionInfo) o;
            int result;

            if ((result = compareStrings(other.selector, selector)) != 0)
                return result;
            else if ((result = compareStrings(other.subtopic, subtopic)) != 0)
                return result;

            return 0;
        }
    }
}
TOP

Related Classes of flex.messaging.MessageClient$SubscriptionInfo

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.