Example #1
0
  @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();
  }
Example #2
0
  /**
   * 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();
    }
  }
Example #3
0
  /**
   * 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);
    }
  }