@Override public void start() { String serviceType = getClass().getName(); ClusterManager clm = getMessageBroker().getClusterManager(); super.start(); /* * For any destinations which are not using broadcast mode, * we need to init the remote subscriptions. First we send out * the requestSubscription messages, then we wait for the sendSubscriptions * messages to come in. */ for (String destName : destinations.keySet()) { MessageDestination dest = (MessageDestination) getDestination(destName); if (!dest.getServerSettings().isBroadcastRoutingMode() && dest.isClustered()) { initRemoteSubscriptions(destName); } } /* Now go through and wait for the response to these messages... */ for (String destName : destinations.keySet()) { MessageDestination dest = (MessageDestination) getDestination(destName); if (!dest.getServerSettings().isBroadcastRoutingMode() && dest.isClustered()) { List members = clm.getClusterMemberAddresses(serviceType, destName); for (Object addr : members) { if (!clm.getLocalAddress(serviceType, destName).equals(addr)) { RemoteSubscriptionManager subMgr = dest.getRemoteSubscriptionManager(); subMgr.waitForSubscriptions(addr); } } } } debug = Log.isDebug(); }
/** * This method is invoked remotely via jgroups. It builds a snapshot of the local subscription * state and sends it back to the requesting server by calling its receiveSubscriptions method. * * @exclude */ public void sendSubscriptions(String destinationId, Object remoteAddress) { MessageDestination destination = (MessageDestination) getDestination(destinationId); Object subscriptions; /* * Avoid trying to use the cluster stuff if this destination does not * exist or is not clustered on this server. */ if (destination == null) { if (Log.isError()) Log.getLogger(LOG_CATEGORY) .error( "Destination: " + destinationId + " does not exist on this server but we received a request for the subscription info from a peer server where the destination exists as clustered. Check the cluster configuration for this destination and make sure it matches on all servers."); return; } else if (!destination.isClustered()) { if (Log.isError()) Log.getLogger(LOG_CATEGORY) .error( "Destination: " + destinationId + " is not clustered on this server but we received a request for the subscription info from a peer server which is clustered. Check the cluster configuration for this destination and make sure it matches on all servers."); return; } RemoteSubscriptionManager subMgr = destination.getRemoteSubscriptionManager(); /* * The remote server has no subscriptions initially since it has not * started yet. We initialize the server here so that when it sends * the first add subscription request, we'll receive it. This is because * servers will not process remote add/remove subscribe requests until * they have received the subscription state from each server. */ subMgr.setSubscriptionState(Collections.EMPTY_LIST, remoteAddress); /* * To ensure that we send the remote server a clean copy of the subscription * table we need to block out the code which adds/removes subscriptions and sends * them to remote servers between here... */ try { subscribeLock.writeLock().lock(); subscriptions = destination.getSubscriptionManager().getSubscriptionState(); ClusterManager clm = getMessageBroker().getClusterManager(); clm.invokePeerToPeerOperation( getClass().getName(), destinationId, "receiveSubscriptions", new Object[] {destinationId, subscriptions}, remoteAddress); } finally { /* ... And here */ subscribeLock.writeLock().unlock(); } }
/** * Same as the previous method but it accepts a destination parameter as well to avoid potentially * costly destination lookup. * * @param message The <code>Message</code> to push to peer server nodes in the cluster. * @param destination The destination to push the message to. * @param evalSelector <code>true</code> to evaluate each remote subscriber's selector before * pushing the message to them; <code>false</code> to skip selector evaluation. */ public void sendPushMessageFromPeer( Message message, MessageDestination destination, boolean evalSelector) { if (!destination.isClustered()) return; ClusterManager clm = getMessageBroker().getClusterManager(); if (destination.getServerSettings().isBroadcastRoutingMode()) { if (debug) Log.getLogger(LOG_CATEGORY) .debug( "Broadcasting message to peer servers: " + message + " evalSelector: " + evalSelector); // tell the message service on other nodes to push the message clm.invokeServiceOperation( getClass().getName(), message.getDestination(), "pushMessageFromPeer", new Object[] {message, evalSelector}); } else { RemoteSubscriptionManager mgr = destination.getRemoteSubscriptionManager(); Set serverAddresses = mgr.getSubscriberIds(message, evalSelector); if (debug) Log.getLogger(LOG_CATEGORY) .debug( "Sending message to peer servers: " + serverAddresses + StringUtils.NEWLINE + " message: " + message + StringUtils.NEWLINE + " evalSelector: " + evalSelector); for (Object remoteAddress : serverAddresses) { clm.invokePeerToPeerOperation( getClass().getName(), message.getDestination(), "pushMessageFromPeerToPeer", new Object[] {message, evalSelector}, remoteAddress); } } }
/** * Remove a subscriber. * * @param clientId the client id * @param selector the selector * @param subtopicString the subtopic * @param endpointId the endpoint */ public void removeSubscriber( Object clientId, String selector, String subtopicString, String endpointId) { MessageClient client = null; try { synchronized (allSubscriptionsLock) { // Do a simple lookup first to avoid the creation of a new MessageClient instance // in the following call to getMessageClient() if the subscription is already removed. client = allSubscriptions.get(clientId); if (client == null) // Subscription was already removed. return; // Re-get in order to track refs correctly. client = getMessageClient(clientId, endpointId); } Subtopic subtopic = getSubtopic(subtopicString); TopicSubscription topicSub; Map<Object, MessageClient> subs; Map<Subtopic, TopicSubscription> map = null; if (subtopic == null) { topicSub = globalSubscribers; } else { if (subtopic.containsSubtopicWildcard()) map = subscribersPerSubtopicWildcard; else map = subscribersPerSubtopic; topicSub = map.get(subtopic); if (topicSub == null) throw new MessageException( "Client: " + clientId + " not subscribed to subtopic: " + subtopic); } if (selector == null) subs = topicSub.defaultSubscriptions; else subs = topicSub.selectorSubscriptions.get(selector); if (subs == null || subs.get(clientId) == null) throw new MessageException( "Client: " + clientId + " not subscribed to destination with selector: " + selector); synchronized (this) { subs.remove(clientId); if (subs.isEmpty() && destination.isClustered() && destination.getServerSettings().getRoutingMode() == RoutingMode.SERVER_TO_SERVER) sendSubscriptionToPeer(false, selector, subtopicString); if (subs.isEmpty()) { if (selector != null) { if (topicSub.selectorSubscriptions != null && !topicSub.selectorSubscriptions.isEmpty()) topicSub.selectorSubscriptions.remove(selector); } if (subtopic != null && (topicSub.selectorSubscriptions == null || topicSub.selectorSubscriptions.isEmpty()) && (topicSub.defaultSubscriptions == null || topicSub.defaultSubscriptions.isEmpty())) { if ((topicSub.selectorSubscriptions == null || topicSub.selectorSubscriptions.isEmpty()) && (topicSub.defaultSubscriptions == null || topicSub.defaultSubscriptions.isEmpty())) map.remove(subtopic); } } } if (client.removeSubscription(selector, subtopicString)) { allSubscriptions.remove(clientId); client.invalidate(); // Destroy the MessageClient. } } finally { if (client != null) releaseMessageClient(client); } }
/** * Add a subscriber. * * @param clientId the client id * @param selector the selector * @param subtopicString the subtopic * @param endpointId the endpoint * @param maxFrequency maximum frequency */ public void addSubscriber( Object clientId, String selector, String subtopicString, String endpointId, int maxFrequency) { Subtopic subtopic = getSubtopic(subtopicString); MessageClient client = null; TopicSubscription topicSub; Map<Object, MessageClient> subs; Map<Subtopic, TopicSubscription> map; try { // Handle resubscribes from the same client and duplicate subscribes from different clients boolean subscriptionAlreadyExists = (getSubscriber(clientId) != null); client = getMessageClient(clientId, endpointId); FlexClient flexClient = FlexContext.getFlexClient(); if (subscriptionAlreadyExists) { // Block duplicate subscriptions from multiple FlexClients if they // attempt to use the same clientId. (when this is called from a remote // subscription, there won't be a flex client so skip this test). if (flexClient != null && !flexClient.getId().equals(client.getFlexClient().getId())) { ServiceException se = new ServiceException(); se.setMessage(10559, new Object[] {clientId}); throw se; } // It's a resubscribe. Reset the endpoint push state for the subscription to make sure its // current // because a resubscribe could be arriving over a new endpoint or a new session. client.resetEndpoint(endpointId); } ServiceAdapter adapter = destination.getAdapter(); client.updateLastUse(); if (subtopic == null) { topicSub = globalSubscribers; } else { if (!destination.getServerSettings().getAllowSubtopics()) { // Throw an error - the destination doesn't allow subtopics. ServiceException se = new ServiceException(); se.setMessage( SUBTOPICS_NOT_SUPPORTED, new Object[] {subtopicString, destination.getId()}); throw se; } if (subtopic.containsSubtopicWildcard() && destination.getServerSettings().isDisallowWildcardSubtopics()) { // Attempt to subscribe to the subtopic, ''{0}'', on destination, ''{1}'', that does not // allow wilcard subtopics failed. ServiceException se = new ServiceException(); se.setMessage( WILDCARD_SUBTOPICS_NOT_ALLOWED, new Object[] {subtopicString, destination.getId()}); throw se; } // Give a MessagingAdapter a chance to block the subscribe. if ((adapter instanceof MessagingSecurity) && (subtopic != null)) { if (!((MessagingSecurity) adapter).allowSubscribe(subtopic)) { ServiceException se = new ServiceException(); se.setMessage(10557, new Object[] {subtopicString}); throw se; } } /* * If there is a wildcard, we always need to match that subscription * against the producer. If it has no wildcard, we can do a quick * lookup to find the subscribers. */ if (subtopic.containsSubtopicWildcard()) map = subscribersPerSubtopicWildcard; else map = subscribersPerSubtopic; synchronized (this) { topicSub = map.get(subtopic); if (topicSub == null) { topicSub = new TopicSubscription(); map.put(subtopic, topicSub); } } } /* Subscribing with no selector */ if (selector == null) { subs = topicSub.defaultSubscriptions; if (subs == null) { synchronized (this) { if ((subs = topicSub.defaultSubscriptions) == null) topicSub.defaultSubscriptions = subs = new ConcurrentHashMap<Object, MessageClient>(); } } } /* Subscribing with a selector - store all subscriptions under the selector key */ else { synchronized (this) { if (topicSub.selectorSubscriptions == null) topicSub.selectorSubscriptions = new ConcurrentHashMap<String, Map<Object, MessageClient>>(); } subs = topicSub.selectorSubscriptions.get(selector); if (subs == null) { synchronized (this) { if ((subs = topicSub.selectorSubscriptions.get(selector)) == null) topicSub.selectorSubscriptions.put( selector, subs = new ConcurrentHashMap<Object, MessageClient>()); } } } if (subs.containsKey(clientId)) { /* I'd rather this be an error but in 2.0 we allowed this without error */ if (Log.isWarn()) Log.getLogger(JMSSelector.LOG_CATEGORY) .warn( "Client: " + clientId + " already subscribed to: " + destination.getId() + " selector: " + selector + " subtopic: " + subtopicString); } else { client.addSubscription(selector, subtopicString, maxFrequency); synchronized (this) { /* * Make sure other members of the cluster know that we are subscribed to * this info if we are in server-to-server mode * * This has to be done in the synchronized section so that we properly * order subscribe and unsubscribe messages for our peers so their * subscription state matches the one in the local server. */ if (subs.isEmpty() && destination.isClustered() && destination.getServerSettings().getRoutingMode() == RoutingMode.SERVER_TO_SERVER) sendSubscriptionToPeer(true, selector, subtopicString); subs.put(clientId, client); } monitorTimeout( client); // local operation, timeouts on remote host are not started until failover // Finally, if a new MessageClient was created, notify its created // listeners now that MessageClient's subscription state is setup. if (!subscriptionAlreadyExists) client.notifyCreatedListeners(); } } finally { releaseMessageClient(client); } }