| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package flex.messaging; |
| |
| import flex.messaging.client.FlexClient; |
| import flex.messaging.client.FlexClientListener; |
| import flex.messaging.log.LogCategories; |
| import flex.messaging.messages.Message; |
| import flex.messaging.util.TimeoutAbstractObject; |
| |
| import java.security.Principal; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.concurrent.CopyOnWriteArrayList; |
| |
| /** |
| * The base for FlexSession implementations. |
| */ |
| public abstract class FlexSession extends TimeoutAbstractObject implements FlexClientListener, MessageClientListener |
| { |
| //-------------------------------------------------------------------------- |
| // |
| // Public Static Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Log category for FlexSession related messages. |
| */ |
| public static final String FLEX_SESSION_LOG_CATEGORY = LogCategories.ENDPOINT_FLEXSESSION; |
| |
| /** |
| * |
| */ |
| public static final int MAX_CONNECTIONS_PER_SESSION_UNLIMITED = -1; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Private Static Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * The set of session created listeners to notify upon a new session creation. |
| */ |
| private static final CopyOnWriteArrayList<FlexSessionListener> createdListeners = new CopyOnWriteArrayList<FlexSessionListener>(); |
| |
| /** |
| * Error string constants. |
| */ |
| private static final int FLEX_SESSION_INVALIDATED = 10019; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Constructor |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * |
| * @deprecated Post 2.6.1 releases require use of the constructor that takes an <tt>AbstractFlexSessionProvider</tt> argument. |
| */ |
| public FlexSession() |
| { |
| this(null); |
| } |
| |
| /** |
| * |
| * Constructs a new FlexSession instance. |
| * |
| * @param sessionProvider The provider that instantiated this instance. |
| */ |
| public FlexSession(AbstractFlexSessionProvider sessionProvider) |
| { |
| this.sessionProvider = sessionProvider; |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Static Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Adds a session created listener that will be notified when new sessions |
| * are created. |
| * |
| * @see flex.messaging.FlexSessionListener |
| * |
| * @param listener The listener to add. |
| */ |
| public static void addSessionCreatedListener(FlexSessionListener listener) |
| { |
| if (listener != null) |
| createdListeners.addIfAbsent(listener); |
| } |
| |
| /** |
| * Removes a session created listener. |
| * |
| * @see flex.messaging.FlexSessionListener |
| * |
| * @param listener The listener to remove. |
| */ |
| public static void removeSessionCreatedListener(FlexSessionListener listener) |
| { |
| if (listener != null) |
| createdListeners.remove(listener); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Variables |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Flag used to break cycles during invalidation. |
| */ |
| protected boolean invalidating; |
| |
| /** |
| * Instance level lock to sync for state changes. |
| */ |
| protected final Object lock = new Object(); |
| |
| /** |
| * Flag indicated whether the session has been invalidated/destroyed. |
| */ |
| protected boolean valid = true; |
| |
| /** |
| * The attributes associated with this session. |
| */ |
| private Map<String, Object> attributes; |
| |
| /** |
| * Registered attribute listeners for the session. |
| */ |
| private volatile CopyOnWriteArrayList<FlexSessionAttributeListener> attributeListeners; |
| |
| /** |
| * Flag indicating whether creation notification has been completed. |
| */ |
| private boolean creationNotified; |
| |
| /** |
| * The set of session destroy listeners to notify when the session is destroyed. |
| */ |
| private volatile CopyOnWriteArrayList<FlexSessionListener> destroyedListeners; |
| |
| /** |
| * The associated FlexClients. |
| */ |
| private final CopyOnWriteArrayList<FlexClient> flexClients = new CopyOnWriteArrayList<FlexClient>(); |
| |
| /** |
| * List of associated MessageClients created while this session was active (thread local). |
| */ |
| private volatile CopyOnWriteArrayList<MessageClient> messageClients; |
| |
| /** |
| * Storage for remote credentials associated with the session; used by the HTTPProxyService |
| * when requests are made to a secured remote endpoint. |
| */ |
| private volatile Map remoteCredentials; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Properties |
| // |
| //-------------------------------------------------------------------------- |
| |
| //---------------------------------- |
| // asyncPollMap |
| //---------------------------------- |
| |
| /** |
| * |
| * Used internally to manage async long-polls; not for public use. |
| * |
| * A map of endpoint to async poll objects that keeps track of what |
| * client is parked on a long-poll with what endpoint. |
| * We only want an endpoint to have a single connection to a client. |
| * Normally, the server leaves an async long-poll in place until |
| * data arrives to push to the client or a timeout is reached. |
| * However, if two or more browser tabs/windows are sharing the same server session and are both attempting |
| * async long-polling, we alternate their parked requests to avoid locking up the browser process by holding too many |
| * Http connections open at once. |
| * This also aids with closing out 'orphaned' long polls following a browser page reload. |
| * Generically typed as <code>Object</code>; using this reference is left up to async poll |
| * implementation code. |
| */ |
| public volatile HashMap<String, FlexClient.AsyncPollWithTimeout> asyncPollMap; |
| |
| //---------------------------------- |
| // flexSessionProvider |
| //---------------------------------- |
| |
| private final AbstractFlexSessionProvider sessionProvider; |
| |
| /** |
| * |
| * Returns the session provider that created this instance. |
| * |
| * @return The session provider that created this instance. |
| */ |
| public AbstractFlexSessionProvider getFlexSessionProvider() |
| { |
| return sessionProvider; |
| } |
| |
| //---------------------------------- |
| // principal |
| //---------------------------------- |
| |
| /** |
| * The principal associated with the session. |
| */ |
| private Principal userPrincipal; |
| |
| /** |
| * This method should be called on FlexContext and not on this class. Keeping |
| * this method for backwards compatibility. This method will produce |
| * correct results when perClientAuthentication is false. However, it will |
| * not return correct results when perClientAuthentication is true. |
| * |
| * Returns the principal associated with the session. If the client has not |
| * authenticated the principal will be null. |
| * |
| * @return The principal associated with the session. |
| */ |
| public Principal getUserPrincipal() |
| { |
| synchronized (lock) |
| { |
| checkValid(); |
| return userPrincipal; |
| } |
| } |
| |
| /** |
| * This method should be called on FlexContext and not on this class. Keeping |
| * this method for backwards compatibility. Calling this when perClientAuthentication |
| * is true will not correctly set the UserPrincipal. |
| * |
| * @param userPrincipal The principal to associate with the session. |
| */ |
| public void setUserPrincipal(Principal userPrincipal) |
| { |
| synchronized (lock) |
| { |
| checkValid(); |
| this.userPrincipal = userPrincipal; |
| } |
| } |
| |
| //---------------------------------- |
| // canStream |
| //---------------------------------- |
| |
| /** |
| * |
| * Used internally by streaming endpoints to enforce session level streaming |
| * connection limits; not for public use. |
| * This flag is volatile to allow for consistent reads across thread without |
| * needing to pay the cost for a synchronized lock for each read. |
| */ |
| public volatile boolean canStream = true; |
| |
| //---------------------------------- |
| // maxConnectionsPerSession |
| //---------------------------------- |
| |
| /** |
| * |
| * Used internally by streaming and long polling endpoints to enforce session |
| * level streaming connection limits; not for public use. Default value is -1 |
| * (limitless) |
| */ |
| public int maxConnectionsPerSession = MAX_CONNECTIONS_PER_SESSION_UNLIMITED; |
| |
| //---------------------------------- |
| // streamingClientsCount |
| //---------------------------------- |
| |
| /** |
| * |
| * Used internally by streaming and long polling endpoints to enforce |
| * session level streaming connection limits; not for public use. |
| * |
| * Some browsers put limits on the number of connections per session. For |
| * example, Firefox has network.http.max-connections-per-server=8 limit which |
| * limits the number of streaming connections per session to 7. Similarly, |
| * IE has a limit of 2 per session. |
| * |
| * This variable is used by streaming and long polling endpoint to keep |
| * track of open connections per session and disallow them when needed. |
| * |
| */ |
| public int streamingConnectionsCount; |
| |
| //---------------------------------- |
| // useSmallMessages |
| //---------------------------------- |
| |
| /** |
| * |
| */ |
| private boolean useSmallMessages; |
| |
| /** |
| * |
| * Determines whether the server can attempt to send small messages |
| * for those messages that have a small form. This setting can be overridden |
| * by an endpoint's enableSmallMessages switch which controls whether |
| * small messages should be sent, even if they are supported. |
| * |
| * The default is false. |
| * |
| * @return true if the server can attempt to send small messages. |
| */ |
| public boolean useSmallMessages() |
| { |
| return useSmallMessages; |
| } |
| |
| /** |
| * @param value true if the server can attempt to send small messages. |
| */ |
| public void setUseSmallMessages(boolean value) |
| { |
| useSmallMessages = value; |
| } |
| |
| //---------------------------------- |
| // waitMonitor |
| //---------------------------------- |
| |
| /** |
| * |
| * Used internally to manage wait()-based long-polls; not for public use. |
| * |
| * This is the monitor that a request handling thread associated with this |
| * FlexSession is waiting on. Normally, the waiting request handling thread will wait until |
| * a new message arrives that can be returned in a poll response or its wait interval times out. |
| * This also aids with closing out 'orphaned' long polls following a browser page reload. |
| */ |
| public volatile HashMap<String, FlexClient.EndpointQueue> waitMonitor; |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Adds a session attribute listener that will be notified when an |
| * attribute is added, removed or changed. |
| * |
| * @param listener The listener to add. |
| */ |
| public void addSessionAttributeListener(FlexSessionAttributeListener listener) |
| { |
| if (listener != null) |
| { |
| checkValid(); |
| |
| if (attributeListeners == null) |
| { |
| synchronized (lock) |
| { |
| if (attributeListeners == null) |
| attributeListeners = new CopyOnWriteArrayList<FlexSessionAttributeListener>(); |
| } |
| } |
| |
| attributeListeners.addIfAbsent(listener); |
| } |
| } |
| |
| /** |
| * Adds a session destroy listener that will be notified when the session |
| * is destroyed. Session destroy listeners are notified after all attributes |
| * have been unbound from the session and any FlexSessionBindingListeners |
| * and FlexSessionAttributeListeners have been notified. |
| * |
| * @see flex.messaging.FlexSessionListener |
| * |
| * @param listener The listener to add. |
| */ |
| public void addSessionDestroyedListener(FlexSessionListener listener) |
| { |
| if (listener != null) |
| { |
| checkValid(); |
| |
| if (destroyedListeners == null) |
| { |
| synchronized (lock) |
| { |
| if (destroyedListeners == null) |
| destroyedListeners = new CopyOnWriteArrayList<FlexSessionListener>(); |
| } |
| } |
| |
| destroyedListeners.addIfAbsent(listener); |
| } |
| } |
| |
| /** |
| * Returns the attribute bound to the specified name in the session, or null |
| * if no attribute is bound under the name. |
| * |
| * @param name The name the target attribute is bound to. |
| * @return The attribute bound to the specified name. |
| */ |
| public Object getAttribute(String name) |
| { |
| synchronized (lock) |
| { |
| checkValid(); |
| |
| return (attributes == null) ? null : attributes.get(name); |
| } |
| } |
| |
| /** |
| * Returns a snapshot of the names of all attributes bound to the session. |
| * |
| * @return A snapshot of the names of all attributes bound to the session. |
| */ |
| public Enumeration<String> getAttributeNames() |
| { |
| synchronized (lock) |
| { |
| checkValid(); |
| |
| if (attributes == null) |
| return Collections.enumeration(Collections.<String>emptyList()); |
| |
| // Return a copy so we do not run into concurrent modification problems if |
| // someone adds to the attributes while iterating through the returned enumeration. |
| return Collections.enumeration(new ArrayList<String>(attributes.keySet())); |
| } |
| } |
| |
| /** |
| * |
| * Implements MessageClientListener. |
| * Handling created events is a no-op. |
| * |
| * @param messageClient The new MessageClient. |
| */ |
| public void messageClientCreated(MessageClient messageClient) {} |
| |
| /** |
| * |
| * Implements MessageClientListener. |
| * Notification that an associated MessageClient was destroyed. |
| * |
| * @param messageClient The MessageClient that was destroyed. |
| */ |
| public void messageClientDestroyed(MessageClient messageClient) |
| { |
| unregisterMessageClient(messageClient); |
| } |
| |
| /** |
| * |
| * FlexClient invokes this to determine whether the session can be used to push messages |
| * to the client. |
| * |
| * @return true if the FlexSession supports direct push; otherwise false (polling is assumed). |
| */ |
| public abstract boolean isPushSupported(); |
| |
| /** |
| * |
| * FlexClient invokes this to push a message to a remote client. |
| * |
| * @param message The message to push. |
| */ |
| public void push(Message message) |
| { |
| throw new UnsupportedOperationException("Push not supported."); |
| } |
| |
| /** |
| * Removes the attribute bound to the specified name in the session. |
| * |
| * @param name The name of the attribute to remove. |
| */ |
| public void removeAttribute(String name) |
| { |
| Object value; // Used for event dispatch after the attribute is removed. |
| |
| synchronized (lock) |
| { |
| checkValid(); // Re-enters lock but should be fast because we're already holding it. |
| |
| value = (attributes != null) ? attributes.remove(name) : null; |
| } |
| |
| // If no value was bound under this name it's a no-op. |
| if (value == null) |
| return; |
| |
| notifyAttributeUnbound(name, value); |
| notifyAttributeRemoved(name, value); |
| } |
| |
| /** |
| * Removes a session attribute listener. |
| * |
| * @param listener The listener to remove. |
| */ |
| public void removeSessionAttributeListener(FlexSessionAttributeListener listener) |
| { |
| // No need to check validity; removing a listener is always ok. |
| if (listener != null && attributeListeners != null) |
| attributeListeners.remove(listener); |
| } |
| |
| /** |
| * Removes a session destroy listener. |
| * |
| * @see flex.messaging.FlexSessionListener |
| * |
| * @param listener The listener to remove. |
| */ |
| public void removeSessionDestroyedListener(FlexSessionListener listener) |
| { |
| // No need to check validity; removing a listener is always ok. |
| if (listener != null && destroyedListeners != null) |
| destroyedListeners.remove(listener); |
| } |
| |
| /** |
| * Binds an attribute value to the session under the specified name. |
| * |
| * @param name The name to bind the attribute under. |
| * @param value The value of the attribute. |
| */ |
| public void setAttribute(String name, Object value) |
| { |
| // Null value is the same as removeAttribute(). |
| if (value == null) |
| { |
| removeAttribute(name); |
| return; |
| } |
| |
| Object oldValue; // Used to determine which events to dispatch after the set is performed. |
| |
| // Only synchronize for the attribute mutation; event dispatch doesn't require it. |
| synchronized (lock) |
| { |
| checkValid(); // Re-enters lock but should be fast because we're already holding it. |
| |
| if (attributes == null) |
| attributes = new HashMap<String, Object>(); |
| |
| oldValue = attributes.put(name, value); |
| } |
| |
| if (oldValue == null) |
| { |
| notifyAttributeBound(name, value); |
| notifyAttributeAdded(name, value); |
| } |
| else |
| { |
| notifyAttributeUnbound(name, oldValue); |
| notifyAttributeReplaced(name, oldValue); |
| notifyAttributeBound(name, value); |
| } |
| } |
| |
| /** |
| * Stores remote credentials in the session for proxied calls to remote systems. |
| * |
| * @param credentials The remote credentials. |
| */ |
| public void putRemoteCredentials(FlexRemoteCredentials credentials) |
| { |
| if (credentials != null) |
| { |
| // We only need to hold the lock to lazy-init the remoteCredentials variable. |
| if (remoteCredentials == null) |
| { |
| synchronized (lock) |
| { |
| // Init size to 4 because that's the number of shipping service types |
| // (messaging, remoting, proxy, data management). |
| if (remoteCredentials == null) |
| remoteCredentials = new HashMap(4); |
| } |
| } |
| synchronized (remoteCredentials) |
| { |
| Map serviceMap = (Map)remoteCredentials.get(credentials.getService()); |
| if (serviceMap == null) |
| { |
| // Init size to half the normal number of buckets; most services won't have a large |
| // number of destinations with remote credentials. |
| serviceMap = new HashMap(7); |
| remoteCredentials.put(credentials.getService(), serviceMap); |
| } |
| serviceMap.put(credentials.getDestination(), credentials); |
| } |
| } |
| } |
| |
| /** |
| * Returns the remote credentials stored in the session for the specified service destination. |
| * |
| * @param serviceId The service id. |
| * @param destinationId The destination id. |
| * @return The stored remote credentials for the specified service destination. |
| */ |
| public FlexRemoteCredentials getRemoteCredentials(String serviceId, String destinationId) |
| { |
| if (serviceId != null && destinationId != null) |
| { |
| if (remoteCredentials == null) |
| return null; |
| synchronized (remoteCredentials) |
| { |
| Map serviceMap = (Map)remoteCredentials.get(serviceId); |
| return (serviceMap != null) ? (FlexRemoteCredentials)serviceMap.get(destinationId) : null; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Clears any stored remote credentials from the session for the specified service destination. |
| * |
| * @param serviceId The service Id. |
| * @param destinationId The destination Id. |
| */ |
| public void clearRemoteCredentials(String serviceId, String destinationId) |
| { |
| if (serviceId != null && destinationId != null) |
| { |
| if (remoteCredentials == null) |
| return; |
| synchronized (remoteCredentials) |
| { |
| Map serviceMap = (Map)remoteCredentials.get(serviceId); |
| if (serviceMap != null) |
| { |
| serviceMap.put(destinationId, null); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Invalidates the FlexSession. |
| */ |
| public void invalidate() |
| { |
| synchronized (lock) |
| { |
| if (!valid || invalidating) |
| return; // Already shutting down. |
| |
| invalidating = true; // This thread gets to shut the FlexSession down. |
| cancelTimeout(); |
| if (sessionProvider != null) |
| sessionProvider.removeFlexSession(this); |
| } |
| |
| // Unregister all FlexClients. |
| if (!flexClients.isEmpty()) |
| { |
| for (FlexClient flexClient : flexClients) |
| unregisterFlexClient(flexClient); |
| } |
| |
| // Invalidate associated MessageClient subscriptions. |
| if (messageClients != null && !messageClients.isEmpty()) |
| { |
| for (Iterator<MessageClient> iter = messageClients.iterator(); iter.hasNext();) |
| { |
| MessageClient messageClient = iter.next(); |
| messageClient.removeMessageClientDestroyedListener(this); |
| messageClient.invalidate(); |
| } |
| messageClients.clear(); |
| } |
| |
| |
| // Notify destroy listeners that we're shutting the FlexSession down. |
| if (destroyedListeners != null && !destroyedListeners.isEmpty()) |
| { |
| for (FlexSessionListener destroyListener : destroyedListeners) |
| { |
| destroyListener.sessionDestroyed(this); |
| } |
| destroyedListeners.clear(); |
| } |
| |
| // Unbind all attributes. |
| if (attributes != null && !attributes.isEmpty()) |
| { |
| Set<String> keySet = attributes.keySet(); |
| String[] keys = keySet.toArray(new String[keySet.size()]); |
| for (String key : keys) |
| removeAttribute(key); |
| |
| attributes = null; |
| } |
| |
| internalInvalidate(); |
| |
| synchronized (lock) |
| { |
| valid = false; |
| invalidating = false; |
| } |
| |
| // Notify any waiting threads. |
| if (waitMonitor != null) |
| { |
| for (FlexClient.EndpointQueue endpointQueue : waitMonitor.values()) |
| { |
| synchronized (endpointQueue) |
| { |
| endpointQueue.notifyAll(); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Hook for subclasses to perform any custom shutdown. |
| * Invoked after the FlexSession has performed generic shutdown but right before the session's valid |
| * property flips to false. |
| */ |
| protected void internalInvalidate() {} |
| |
| /** |
| * Returns a snapshot of the FlexClients associated with the FlexSession |
| * when this method is invoked. |
| * This list is not guaranteed to remain consistent with the actual list |
| * of active FlexClients associated with the FlexSession over time. |
| * |
| * @return A snapshot of the current list of FlexSessions associated with the FlexClient. |
| */ |
| public List<FlexClient> getFlexClients() |
| { |
| List<FlexClient> currentFlexClients = null; |
| synchronized (lock) |
| { |
| checkValid(); // Re-enters lock but should be fast because we're already holding it. |
| |
| currentFlexClients = new ArrayList<FlexClient>(flexClients); // Make a copy of the current list to return. |
| } |
| return currentFlexClients; |
| } |
| |
| /** |
| * Returns a snapshot of the MessageClients (subscriptions) associated with the FlexSession |
| * when this method is invoked. |
| * This list is not guaranteed to remain consistent with the actual list |
| * of active MessageClients associated with the FlexSession over time. |
| * |
| * @return A snapshot of the current list of MessageClients associated with the FlexSession. |
| */ |
| public List<MessageClient> getMessageClients() |
| { |
| List<MessageClient> currentMessageClients = null; |
| synchronized (lock) |
| { |
| checkValid(); // Re-enters lock but should be fast because we're already holding it. |
| |
| currentMessageClients = (messageClients != null) ? new ArrayList<MessageClient>(messageClients) // Make a copy of the current list to return. |
| : new ArrayList<MessageClient>(); // Return an empty list. |
| } |
| return currentMessageClients; |
| } |
| |
| /** |
| * Returns the Id for the session. |
| * |
| * @return The Id for the session. |
| */ |
| public abstract String getId(); |
| |
| /** |
| * Returns whether the current user is in the specified role. |
| * |
| * @param role The role to test. |
| * @return true if the user is in the role; otherwise false. |
| */ |
| public boolean isUserInRole(String role) |
| { |
| ArrayList list = new ArrayList(); |
| list.add(role); |
| return FlexContext.getMessageBroker().getLoginManager().checkRoles(userPrincipal, list); |
| } |
| |
| /** |
| * Returns whether the session is valid. |
| * |
| * @return true if the session is valid; otherwise false. |
| */ |
| public boolean isValid() |
| { |
| synchronized (lock) |
| { |
| return valid; |
| } |
| } |
| |
| /** |
| * |
| * Implements FlexClientListener interface. |
| * Notification that a FlexClient was created. |
| * This is a no-op because the FlexSession is never added as a static FlexClient created listener |
| * but this method is required by the interface. We only listen for the destroyed event from |
| * associated FlexClients. |
| * |
| * @param flexClient The FlexClient that was created. |
| */ |
| public void clientCreated(FlexClient flexClient) {} |
| |
| /** |
| * |
| * Implements FlexClientListener interface. |
| * Notification that an associated FlexClient was destroyed. |
| * |
| * @param flexClient The FlexClient that was destroyed. |
| */ |
| public void clientDestroyed(FlexClient flexClient) |
| { |
| unregisterFlexClient(flexClient); |
| } |
| |
| /** |
| * |
| * Used internally to associate a FlexClient with the FlexSession. |
| * |
| * @param flexClient The FlexClient to assocaite with the session. |
| */ |
| public void registerFlexClient(FlexClient flexClient) |
| { |
| if (flexClients.addIfAbsent(flexClient)) |
| { |
| flexClient.addClientDestroyedListener(this); |
| flexClient.registerFlexSession(this); |
| } |
| } |
| |
| /** |
| * |
| * Used internally to disassociate a FlexClient from the FlexSession. |
| * |
| * @param flexClient The FlexClient to disassociate from the session. |
| */ |
| public void unregisterFlexClient(FlexClient flexClient) |
| { |
| if (flexClients.remove(flexClient)) |
| { |
| flexClient.removeClientDestroyedListener(this); |
| flexClient.unregisterFlexSession(this); |
| } |
| } |
| |
| /** |
| * |
| * Used internally to associate a MessagClient (subscription) with the FlexSession. |
| * |
| * @param messageClient The MessageClient to associate with the session. |
| */ |
| public void registerMessageClient(MessageClient messageClient) |
| { |
| if (messageClients == null) |
| { |
| synchronized (lock) |
| { |
| if (messageClients == null) |
| messageClients = new CopyOnWriteArrayList<MessageClient>(); |
| } |
| } |
| |
| if (messageClients.addIfAbsent(messageClient)) |
| messageClient.addMessageClientDestroyedListener(this); |
| } |
| |
| /** |
| * |
| * Used internally to disassociate a MessageClient (subscription) from a FlexSession. |
| * |
| * @param messageClient The MessageClient to disassociate from the session. |
| */ |
| public void unregisterMessageClient(MessageClient messageClient) |
| { |
| if (messageClients != null && messageClients.remove(messageClient)) |
| messageClient.removeMessageClientDestroyedListener(this); |
| } |
| |
| /** |
| * Default implementation invokes <code>invalidate()</code> upon timeout. |
| * |
| * @see flex.messaging.util.TimeoutCapable#timeout() |
| */ |
| public void timeout() |
| { |
| invalidate(); |
| } |
| |
| //-------------------------------------------------------------------------- |
| // |
| // Protected Methods |
| // |
| //-------------------------------------------------------------------------- |
| |
| /** |
| * Ensures that the session has not been invalidated. |
| */ |
| protected void checkValid() |
| { |
| synchronized (lock) |
| { |
| if (!valid) |
| { |
| LocalizedException e = new LocalizedException(); |
| e.setMessage(FLEX_SESSION_INVALIDATED); |
| throw e; |
| } |
| } |
| } |
| |
| /** |
| * Notify attribute listeners that an attribute has been added. |
| * |
| * @param name The name of the attribute. |
| * @param value The new value of the attribute. |
| */ |
| protected void notifyAttributeAdded(String name, Object value) |
| { |
| if (attributeListeners != null && !attributeListeners.isEmpty()) |
| { |
| FlexSessionBindingEvent event = new FlexSessionBindingEvent(this, name, value); |
| // CopyOnWriteArrayList is iteration-safe from ConcurrentModificationExceptions. |
| for (FlexSessionAttributeListener attribListener : attributeListeners) |
| attribListener.attributeAdded(event); |
| } |
| } |
| |
| /** |
| * Notify binding listener that it has been bound to the session. |
| * |
| * @param name The attribute name. |
| * @param value The attribute that has been bound. |
| */ |
| protected void notifyAttributeBound(String name, Object value) |
| { |
| if ((value != null) && (value instanceof FlexSessionBindingListener)) |
| { |
| FlexSessionBindingEvent bindingEvent = new FlexSessionBindingEvent(this, name); |
| ((FlexSessionBindingListener)value).valueBound(bindingEvent); |
| } |
| } |
| |
| /** |
| * Notify attribute listeners that an attribute has been removed. |
| * |
| * @param name The name of the attribute. |
| * @param value The previous value of the attribute. |
| */ |
| protected void notifyAttributeRemoved(String name, Object value) |
| { |
| if (attributeListeners != null && !attributeListeners.isEmpty()) |
| { |
| FlexSessionBindingEvent event = new FlexSessionBindingEvent(this, name, value); |
| // CopyOnWriteArrayList is iteration-safe from ConcurrentModificationExceptions. |
| for (FlexSessionAttributeListener attribListener : attributeListeners) |
| attribListener.attributeRemoved(event); |
| } |
| } |
| |
| /** |
| * Notify attribute listeners that an attribute has been replaced. |
| * |
| * @param name The name of the attribute. |
| * @param value The previous value of the attribute. |
| */ |
| protected void notifyAttributeReplaced(String name, Object value) |
| { |
| if (attributeListeners != null && !attributeListeners.isEmpty()) |
| { |
| FlexSessionBindingEvent event = new FlexSessionBindingEvent(this, name, value); |
| // CopyOnWriteArrayList is iteration-safe from ConcurrentModificationExceptions. |
| for (FlexSessionAttributeListener attribListener : attributeListeners) |
| attribListener.attributeReplaced(event); |
| } |
| } |
| |
| /** |
| * Notify binding listener that it has been unbound from the session. |
| * |
| * @param name The attribute name. |
| * @param value The attribute that has been unbound. |
| */ |
| protected void notifyAttributeUnbound(String name, Object value) |
| { |
| if ((value != null) && (value instanceof FlexSessionBindingListener)) |
| { |
| FlexSessionBindingEvent bindingEvent = new FlexSessionBindingEvent(this, name); |
| ((FlexSessionBindingListener)value).valueUnbound(bindingEvent); |
| } |
| } |
| |
| /** |
| * Invoked by subclass upon session creation to notify all registered |
| * session create listeners of the event. |
| * This method must be invoked in the subclass constructor. |
| */ |
| protected void notifyCreated() |
| { |
| // This guard is here only to prevent duplicate notifications if there's a coding error |
| // in the subclass. Not likely.. |
| synchronized (lock) |
| { |
| if (creationNotified) |
| return; |
| |
| creationNotified = true; |
| } |
| |
| if (!createdListeners.isEmpty()) |
| { |
| // CopyOnWriteArrayList is iteration-safe from ConcurrentModificationExceptions. |
| for (Iterator<FlexSessionListener> iter = createdListeners.iterator(); iter.hasNext();) |
| iter.next().sessionCreated(this); |
| } |
| } |
| } |