Example #1
0
  /**
   * This method is called from a messaging adapter to handle the delivery of messages to one or
   * more clients. If you pass in the <code>sendToAllSubscribers</code> parameter as <code>true
   * </code>, the message is routed to all clients who are subscribed to receive messages from this
   * destination. When you use this method, the selector expressions for all subscribing clients are
   * not evaluated. If you want the selector expressions to be evaluated, use a combination of the
   * <code>pushMessageToClients</code> method and the <code>sendPushMessageFromPeer</code> methods.
   *
   * @param message The <code>Message</code> to send.
   * @param sendToAllSubscribers If <code>true</code>, send this message to all clients subscribed
   *     to the destination of this message. If <code>false</code>, send the message only to the
   *     clientId specified in the message.
   */
  public void serviceMessageFromAdapter(Message message, boolean sendToAllSubscribers) {
    // Update management metrics.
    if (managed) {
      MessageDestination destination =
          (MessageDestination) getDestination(message.getDestination());
      if (destination != null && destination.isManaged()) {
        MessageDestinationControl destinationControl =
            (MessageDestinationControl) destination.getControl();
        if (destinationControl != null) // Should not happen but just in case.
        destinationControl.incrementServiceMessageFromAdapterCount();
      }
    }

    // in this service's case, this invocation occurs when an adapter has asynchronously
    // received a message from one of its adapters acting as a consumer
    if (sendToAllSubscribers) {
      pushMessageToClients(message, false);
      sendPushMessageFromPeer(message, false);
    } else {
      // TODO - need to do something here to locate the proper qualified client.
      // the adapter has already processed the subscribers
      Set subscriberIds = new TreeSet();
      subscriberIds.add(message.getClientId());
      pushMessageToClients(subscriberIds, message, false);
    }
  }
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();
    }
  }
  /**
   * Broadcast this subscribe/unsubscribe message to the cluster so everyone is aware of this
   * server's interest in messages matching this selector and subtopic.
   *
   * @param subscribe are we subscribing?
   * @param selector the selector
   * @param subtopic the subtopic
   */
  protected void sendSubscriptionToPeer(boolean subscribe, String selector, String subtopic) {
    if (Log.isDebug())
      Log.getLogger(MessageService.LOG_CATEGORY)
          .debug(
              "Sending subscription to peers for subscribe? "
                  + subscribe
                  + " selector: "
                  + selector
                  + " subtopic: "
                  + subtopic);

    ((MessageService) destination.getService())
        .sendSubscribeFromPeer(destination.getId(), subscribe, selector, subtopic);
  }
Example #4
0
  /**
   * Issue messages to request the remote subscription table from each server in the cluster (except
   * this one).
   */
  public void initRemoteSubscriptions(String destinationId) {
    ClusterManager clm = getMessageBroker().getClusterManager();
    String serviceType = getClass().getName();
    MessageDestination dest = (MessageDestination) getDestination(destinationId);

    Cluster cluster = clm.getCluster(serviceType, destinationId);
    if (cluster != null) cluster.addRemoveNodeListener(dest.getRemoteSubscriptionManager());

    List members = clm.getClusterMemberAddresses(serviceType, destinationId);
    for (int i = 0; i < members.size(); i++) {
      Object addr = members.get(i);
      if (!clm.getLocalAddress(serviceType, destinationId).equals(addr))
        requestSubscriptions(destinationId, addr);
    }
  }
Example #5
0
 /**
  * Used to increment the message count metric for the <code>MessageService</code>. This value is
  * stored in the corresponding MBean. The <code>MessageService</code> already invokes this method
  * in its <code>serviceMessage()</code> and <code>serviceCommand()</code> implementations, but if
  * a subclass overrides these methods completely it should invoke this method appropriately as it
  * processes messages.
  *
  * @param commandMessage Pass <code>true</code> if the message being processed is a <code>
  *     CommandMessage</code>; otherwise <code>false</code>.
  */
 protected void incrementMessageCount(boolean commandMessage, Message message) {
   if (managed) // Update management metrics.
   {
     MessageDestination destination =
         (MessageDestination) getDestination(message.getDestination());
     if (destination != null && destination.isManaged()) {
       MessageDestinationControl destinationControl =
           (MessageDestinationControl) destination.getControl();
       if (destinationControl != null) // Should not happen but just in case.
       {
         if (commandMessage) destinationControl.incrementServiceCommandCount();
         else destinationControl.incrementServiceMessageCount();
       }
     }
   }
 }
  /**
   * Implement a serializer instance which wraps the subscription manager in a transient variable.
   * It will need to block out all sub/unsub messages before they are broadcast to the remote
   * server, iterate through the maps of subscriptions and for each "unique" subscription it writes
   * the selector and subtopic.
   *
   * <p>synchronization note: this assumes no add/remove subscriptions are occurring while this
   * method is called.
   *
   * @return a List of subscriptions selectors and subtopics
   */
  public Object getSubscriptionState() {
    ArrayList<String> subState = new ArrayList<String>();

    if (globalSubscribers.defaultSubscriptions != null
        && !globalSubscribers.defaultSubscriptions.isEmpty()) {
      subState.add(null); // selector string
      subState.add(null); // subtopic string
    }
    if (globalSubscribers.selectorSubscriptions != null) {
      for (String s : globalSubscribers.selectorSubscriptions.keySet()) {
        subState.add(s);
        subState.add(null); // subtopic
      }
    }
    addSubscriptionState(subState, subscribersPerSubtopic);
    addSubscriptionState(subState, subscribersPerSubtopicWildcard);

    if (Log.isDebug())
      Log.getLogger(MessageService.LOG_CATEGORY)
          .debug(
              "Retrieved subscription state to send to new cluster member for destination: "
                  + destination.getId()
                  + ": "
                  + StringUtils.NEWLINE
                  + subState);

    return subState;
  }
Example #7
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 #8
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);
      }
    }
  }
Example #9
0
  /**
   * Pushes a message to all clients that are subscribed to the destination targeted by this
   * message.
   *
   * @param message The <code>Message</code> to push to the destination's subscribers.
   * @param evalSelector <code>true</code> to evaluate each subscriber's selector before pushing the
   *     message to them; <code>false</code> to skip selector evaluation.
   */
  public void pushMessageToClients(Message message, boolean evalSelector) {
    MessageDestination destination = (MessageDestination) getDestination(message);
    SubscriptionManager subscriptionManager = destination.getSubscriptionManager();
    Set subscriberIds = subscriptionManager.getSubscriberIds(message, evalSelector);

    if (debug)
      Log.getLogger(LOG_CATEGORY)
          .debug(
              "Sending message: "
                  + message
                  + StringUtils.NEWLINE
                  + "    to subscribed clientIds: "
                  + subscriberIds);

    if ((subscriberIds != null) && !subscriberIds.isEmpty()) {
      /* We have already filtered based on the selector and so pass false below */
      pushMessageToClients(destination, subscriberIds, message, false);
    }
  }
  void addSubtopicSubscribers(
      Subtopic subtopic, Message message, Set<Object> ids, boolean evalSelector) {
    // If we have a subtopic, we need to route the message only to that
    // subset of subscribers.
    if (!destination.getServerSettings().getAllowSubtopics()) {
      // Throw an error - the destination doesn't allow subtopics.
      ServiceException se = new ServiceException();
      se.setMessage(
          SUBTOPICS_NOT_SUPPORTED, new Object[] {subtopic.getValue(), destination.getId()});
      throw se;
    }

    // Give a MessagingAdapter a chance to block the send to this subtopic.
    ServiceAdapter adapter = destination.getAdapter();
    if (adapter instanceof MessagingSecurity) {
      if (!((MessagingSecurity) adapter).allowSend(subtopic)) {
        ServiceException se = new ServiceException();
        se.setMessage(10558, new Object[] {subtopic.getValue()});
        throw se;
      }
    }

    TopicSubscription ts;
    if (subscribersPerSubtopic.containsKey(subtopic)) {
      ts = subscribersPerSubtopic.get(subtopic);
      addTopicSubscribers(ts, message, ids, evalSelector);
    }

    /*
     * TODO: performance - organize these into a tree so we can find consumers via
     * a hashtable lookup rather than a linear search
     */
    Set<Subtopic> subtopics = subscribersPerSubtopicWildcard.keySet();
    if (!subtopics.isEmpty()) {
      for (Subtopic st : subtopics) {
        if (st.matches(subtopic)) {
          ts = subscribersPerSubtopicWildcard.get(st);
          addTopicSubscribers(ts, message, ids, evalSelector);
        }
      }
    }
  }
Example #11
0
  /**
   * Creates a <code>MessageDestination</code> instance, sets its id, sets it manageable if the
   * <code>AbstractService</code> that created it is manageable, and sets its <code>Service</code>
   * to the <code>AbstractService</code> that created it.
   *
   * @param id The id of the <code>MessageDestination</code>.
   * @return The <code>Destination</code> instanced created.
   */
  @Override
  public Destination createDestination(String id) {
    if (id == null) {
      // Cannot add ''{0}'' with null id to the ''{1}''
      ConfigurationException ex = new ConfigurationException();
      ex.setMessage(
          ConfigurationConstants.NULL_COMPONENT_ID, new Object[] {"Destination", "Service"});
      throw ex;
    }

    // check with the message broker to make sure that no destination with the id already exists
    getMessageBroker().isDestinationRegistered(id, getId(), true);

    MessageDestination destination = new MessageDestination();
    destination.setId(id);
    destination.setManaged(isManaged());
    destination.setService(this);

    return destination;
  }
Example #12
0
 /**
  * This method is provided for a cluster peer broadcast, it is not invoked locally. It is used by
  * remote clients to send their subscription table to this server.
  *
  * @exclude
  */
 public void receiveSubscriptions(
     String destinationId, Object subscriptions, Object senderAddress) {
   Destination destination = getDestination(destinationId);
   if (destination instanceof MessageDestination)
     ((MessageDestination) destination)
         .getRemoteSubscriptionManager()
         .setSubscriptionState(subscriptions, senderAddress);
   else if (subscriptions != null && Log.isError())
     Log.getLogger(LOG_CATEGORY)
         .error(
             "receiveSubscriptions called with non-null value but destination: "
                 + destinationId
                 + " is not a MessageDestination");
 }
Example #13
0
  /**
   * @exclude This method is used by messaging adapters to send a message to a specific set of
   *     clients that are directly connected to this server. It does not propagate the message to
   *     other servers in the cluster.
   */
  public void pushMessageToClients(
      MessageDestination destination, Set subscriberIds, Message message, boolean evalSelector) {
    if (subscriberIds != null) {
      try {
        // Place notifier in thread-local scope.
        MessageRoutedNotifier routingNotifier = new MessageRoutedNotifier(message);
        FlexContext.setMessageRoutedNotifier(routingNotifier);

        SubscriptionManager subscriptionManager = destination.getSubscriptionManager();

        for (Object clientId : subscriberIds) {
          MessageClient client = subscriptionManager.getSubscriber(clientId);

          // Skip if the client is null or invalidated.
          if (client == null || !client.isValid()) {
            if (debug)
              Log.getLogger(MessageService.LOG_CATEGORY)
                  .debug(
                      "Warning: could not find MessageClient for clientId in pushMessageToClients: "
                          + clientId
                          + " for destination: "
                          + destination.getId());
            continue;
          }

          pushMessageToClient(client, destination, message, evalSelector);
        }

        // Done with the push, notify any listeners.
        routingNotifier.notifyMessageRouted();
      } finally {
        // Unset the notifier for this message.
        FlexContext.setMessageRoutedNotifier(null);
      }
    }
  }
 private Subtopic getSubtopic(String subtopic) {
   return subtopic != null
       ? new Subtopic(subtopic, destination.getServerSettings().getSubtopicSeparator())
       : null;
 }
  /**
   * 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);
    }
  }
Example #17
0
 /**
  * Returns a Set of clientIds of the clients subscribed to receive this message. If the message
  * has a subtopic header, the subtopics are used to gather the subscribers. If there is no
  * subtopic header, subscribers to the destination with no subtopic are used. If a subscription
  * has a selector expression associated with it and evalSelector is true, the subscriber is only
  * returned if the selector expression evaluates to true.
  *
  * <p>In normal usage, you can use the pushMessageToClients(message, evalSelector) method to both
  * find the subscribers and send the message. You use this method only if you want to do
  * additional processing to the subscribers list - for example, merging it into another list of
  * subscribers or logging the ids of the subscribers who should receive the message. Once this
  * method returns, you can use the pushMessageToClients variant which takes the set of subscriber
  * ids to deliver these messages.
  *
  * <p>This method only returns subscriptions maintained by the current server instance. It does
  * not return any information for subscribers that might be connected to remote instances. To send
  * the message to remotely connected servers, use the sendPushMessageFromPeer method.
  *
  * @param message The <code>Messsage</code> Typically
  */
 public Set getSubscriberIds(Message message, boolean evalSelector) {
   MessageDestination destination = (MessageDestination) getDestination(message);
   SubscriptionManager subscriptionManager = destination.getSubscriptionManager();
   return subscriptionManager.getSubscriberIds(message, evalSelector);
 }
Example #18
0
  /**
   * Processes subscription related <code>CommandMessage</code>s. Subclasses that perform additional
   * custom subscription management should invoke <code>super.manageSubscriptions()</code> if they
   * choose to override this method.
   *
   * @param command The <code>CommandMessage</code> to process.
   */
  protected Message manageSubscriptions(CommandMessage command) {
    Message replyMessage = null;

    MessageDestination destination = (MessageDestination) getDestination(command);
    SubscriptionManager subscriptionManager = destination.getSubscriptionManager();

    Object clientId = command.getClientId();
    String endpointId = (String) command.getHeader(Message.ENDPOINT_HEADER);

    String subtopicString = (String) command.getHeader(AsyncMessage.SUBTOPIC_HEADER_NAME);

    ServiceAdapter adapter = destination.getAdapter();

    if (command.getOperation() == CommandMessage.SUBSCRIBE_OPERATION) {
      String selectorExpr = (String) command.getHeader(CommandMessage.SELECTOR_HEADER);

      getMessageBroker().inspectChannel(command, destination);

      // Give MessagingAdapter a chance to block the subscribe.
      if ((adapter instanceof MessagingAdapter)) {
        MessagingSecurityConstraintManager manager =
            ((MessagingAdapter) adapter).getSecurityConstraintManager();
        if (manager != null) manager.assertSubscribeAuthorization();
      }

      try {
        /*
         * This allows parallel add/remove subscribe calls (protected by the
         * concurrent hash table) but prevents us from doing any table mods
         * when the getSubscriptionState method is active
         */
        subscribeLock.readLock().lock();

        if (adapter.handlesSubscriptions()) {
          replyMessage = (Message) adapter.manage(command);
        } else {
          testSelector(selectorExpr, command);
        }
        /*
         * Even if the adapter is managing the subscription, we still need to
         * register this with the subscription manager so that we can match the
         * endpoint with the clientId.  I am not sure I like this though because
         * now the subscription is registered both with the adapter and with our
         * system so keeping them in sync is potentially problematic.   Also, it
         * seems like the adapter should have the option to manage endpoints themselves?
         */

        // Extract the maxFrequency that might have been specified by the client.
        int maxFrequency = processMaxFrequencyHeader(command);
        subscriptionManager.addSubscriber(
            clientId, selectorExpr, subtopicString, endpointId, maxFrequency);
      } finally {
        subscribeLock.readLock().unlock();
      }

      if (replyMessage == null) replyMessage = new AcknowledgeMessage();
    } else if (command.getOperation() == CommandMessage.UNSUBSCRIBE_OPERATION) {
      // Give MessagingAdapter a chance to block the unsubscribe, as long as
      // the subscription has not been invalidated.
      if ((adapter instanceof MessagingAdapter)
          && command.getHeader(CommandMessage.SUBSCRIPTION_INVALIDATED_HEADER) == null) {
        MessagingSecurityConstraintManager manager =
            ((MessagingAdapter) adapter).getSecurityConstraintManager();
        if (manager != null) manager.assertSubscribeAuthorization();
      }

      String selectorExpr = (String) command.getHeader(CommandMessage.SELECTOR_HEADER);

      try {
        subscribeLock.readLock().lock();

        if (adapter.handlesSubscriptions()) {
          replyMessage = (Message) adapter.manage(command);
        }
        subscriptionManager.removeSubscriber(clientId, selectorExpr, subtopicString, endpointId);
      } finally {
        subscribeLock.readLock().unlock();
      }

      if (replyMessage == null) replyMessage = new AcknowledgeMessage();
    } else if (command.getOperation() == CommandMessage.MULTI_SUBSCRIBE_OPERATION) {
      getMessageBroker().inspectChannel(command, destination);

      // Give MessagingAdapter a chance to block the multi subscribe.
      if ((adapter instanceof MessagingAdapter)) {
        MessagingSecurityConstraintManager manager =
            ((MessagingAdapter) adapter).getSecurityConstraintManager();
        if (manager != null) manager.assertSubscribeAuthorization();
      }

      try {
        /*
         * This allows parallel add/remove subscribe calls (protected by the
         * concurrent hash table) but prevents us from doing any table mods
         * when the getSubscriptionState method is active
         */
        subscribeLock.readLock().lock();

        if (adapter.handlesSubscriptions()) {
          replyMessage = (Message) adapter.manage(command);
        }

        // Deals with legacy collection setting
        Object[] adds =
            getObjectArrayFromHeader(command.getHeader(CommandMessage.ADD_SUBSCRIPTIONS));
        Object[] rems =
            getObjectArrayFromHeader(command.getHeader(CommandMessage.REMOVE_SUBSCRIPTIONS));

        if (adds != null) {
          // Extract the maxFrequency that might have been specified
          // by the client for every subscription (selector/subtopic).
          int maxFrequency = processMaxFrequencyHeader(command);
          for (int i = 0; i < adds.length; i++) {
            // Use the maxFrequency by default.
            int maxFrequencyPerSubscription = maxFrequency;
            String ss = (String) adds[i];
            int ix = ss.indexOf(CommandMessage.SUBTOPIC_SEPARATOR);
            if (ix != -1) {
              String subtopic = (ix == 0 ? null : ss.substring(0, ix));
              String selector = null;
              String selectorAndMaxFrequency =
                  ss.substring(ix + CommandMessage.SUBTOPIC_SEPARATOR.length());
              if (selectorAndMaxFrequency.length() != 0) {
                int ix2 = selectorAndMaxFrequency.indexOf(CommandMessage.SUBTOPIC_SEPARATOR);
                if (ix2 != -1) {
                  selector = (ix2 == 0 ? null : selectorAndMaxFrequency.substring(0, ix2));
                  String maxFrequencyString =
                      selectorAndMaxFrequency.substring(
                          ix2 + CommandMessage.SUBTOPIC_SEPARATOR.length());
                  if (maxFrequencyString.length() != 0) {
                    // Choose the minimum of Consumer level maxFrequency and subscription level
                    // maxFrequency.
                    int maxFrequencyCandidate = Integer.parseInt(maxFrequencyString);
                    maxFrequencyPerSubscription =
                        maxFrequencyPerSubscription == 0
                            ? maxFrequencyCandidate
                            : Math.min(maxFrequencyPerSubscription, maxFrequencyCandidate);
                  }
                }
              }
              subscriptionManager.addSubscriber(
                  clientId, selector, subtopic, endpointId, maxFrequencyPerSubscription);
            }
            // invalid message
          }
        }

        if (rems != null) {
          for (int i = 0; i < rems.length; i++) {
            String ss = (String) rems[i];
            int ix = ss.indexOf(CommandMessage.SUBTOPIC_SEPARATOR);
            if (ix != -1) {
              String subtopic = (ix == 0 ? null : ss.substring(0, ix));
              String selector = null;
              String selectorAndMaxFrequency =
                  ss.substring(ix + CommandMessage.SUBTOPIC_SEPARATOR.length());
              if (selectorAndMaxFrequency.length() != 0) {
                int ix2 = selectorAndMaxFrequency.indexOf(CommandMessage.SUBTOPIC_SEPARATOR);
                if (ix2 != -1)
                  selector = ix2 == 0 ? null : selectorAndMaxFrequency.substring(0, ix2);
              }
              subscriptionManager.removeSubscriber(clientId, selector, subtopic, endpointId);
            }
          }
        }
      } finally {
        subscribeLock.readLock().unlock();
      }

      if (replyMessage == null) replyMessage = new AcknowledgeMessage();
    } else if (command.getOperation() == CommandMessage.POLL_OPERATION) {
      // This code path handles poll messages sent by Consumer.receive().
      // This API should not trigger server side waits, so we invoke poll
      // and if there are no queued messages for this Consumer instance we
      // return an empty acknowledgement immediately.
      MessageClient client = null;
      try {
        client = subscriptionManager.getMessageClient(clientId, endpointId);

        if (client != null) {
          if (adapter.handlesSubscriptions()) {
            List missedMessages = (List) adapter.manage(command);
            if (missedMessages != null && !missedMessages.isEmpty()) {
              MessageBroker broker = getMessageBroker();
              for (Iterator iter = missedMessages.iterator(); iter.hasNext(); )
                broker.routeMessageToMessageClient((Message) iter.next(), client);
            }
          }
          FlushResult flushResult = client.getFlexClient().poll(client);
          List messagesToReturn = (flushResult != null) ? flushResult.getMessages() : null;
          if (messagesToReturn != null && !messagesToReturn.isEmpty()) {
            replyMessage = new CommandMessage(CommandMessage.CLIENT_SYNC_OPERATION);
            replyMessage.setBody(messagesToReturn.toArray());
          } else {
            replyMessage = new AcknowledgeMessage();
          }
          // Adaptive poll wait is never used in responses to Consumer.receive() calls.
        } else {
          ServiceException se = new ServiceException();
          se.setCode(NOT_SUBSCRIBED_CODE);
          se.setMessage(NOT_SUBSCRIBED, new Object[] {destination.getId()});
          throw se;
        }
      } finally {
        subscriptionManager.releaseMessageClient(client);
      }
    } else {
      ServiceException se = new ServiceException();
      se.setMessage(UNKNOWN_COMMAND, new Object[] {new Integer(command.getOperation())});
      throw se;
    }

    return replyMessage;
  }
Example #19
0
 /**
  * Returns the set of subscribers for the specified destination, subtopic/subtopic pattern and
  * message headers. The message headers can be null. If specified, they are used to match against
  * any selector patterns that were used for subscribers.
  */
 public Set getSubscriberIds(String destinationId, String subtopicPattern, Map messageHeaders) {
   MessageDestination destination = (MessageDestination) getDestination(destinationId);
   SubscriptionManager subscriptionManager = destination.getSubscriptionManager();
   return subscriptionManager.getSubscriberIds(subtopicPattern, messageHeaders);
 }
Example #20
0
  /** @exclude */
  public Object serviceMessage(Message message, boolean throttle, MessageDestination dest) {
    if (managed) incrementMessageCount(false, message);

    if (throttle) {
      // Throttle the inbound message; also attempts to prevent duplicate messages sent by a client.
      dest = (MessageDestination) getDestination(message);
      ThrottleManager throttleManager = dest.getThrottleManager();
      if (throttleManager != null && throttleManager.throttleIncomingMessage(message))
        return null; // Message throttled.
    }

    // Block any sent messages that have a subtopic header containing
    // wildcards - wildcards are only supported in subscribe/unsubscribe
    // commands (see serviceCommand() and manageSubscriptions()).
    Object subtopicObj = message.getHeader(AsyncMessage.SUBTOPIC_HEADER_NAME);
    List<Subtopic> subtopics = null;
    if (subtopicObj != null) {
      if (subtopicObj instanceof Object[]) subtopicObj = Arrays.asList((Object[]) subtopicObj);

      if (subtopicObj instanceof String) {
        String subtopicString = (String) subtopicObj;
        if (subtopicString != null && subtopicString.length() > 0) {
          if (dest == null) dest = (MessageDestination) getDestination(message);
          Subtopic subtopic =
              testProducerSubtopic(dest.getServerSettings().getSubtopicSeparator(), subtopicString);
          if (subtopics == null) subtopics = new ArrayList<Subtopic>();
          subtopics.add(subtopic);
        }
      } else if (subtopicObj instanceof List) {
        @SuppressWarnings("unchecked")
        List<String> subtopicList = (List<String>) subtopicObj;
        String subtopicSeperator = null;
        for (String subtopicString : subtopicList) {
          if (subtopicString != null && subtopicString.length() > 0) {
            if (dest == null) dest = (MessageDestination) getDestination(message);
            subtopicSeperator = dest.getServerSettings().getSubtopicSeparator();
            Subtopic subtopic = testProducerSubtopic(subtopicSeperator, subtopicString);
            if (subtopics == null) subtopics = new ArrayList<Subtopic>();
            subtopics.add(subtopic);
          }
        }
      }
    }

    // Override TTL if there was one specifically configured for this destination
    if (dest == null) dest = (MessageDestination) getDestination(message);
    ServerSettings destServerSettings = dest.getServerSettings();
    if (destServerSettings.getMessageTTL() >= 0)
      message.setTimeToLive(destServerSettings.getMessageTTL());

    long start = 0;
    if (debug) start = System.currentTimeMillis();

    // Give MessagingAdapter a chance to block the send.
    ServiceAdapter adapter = dest.getAdapter();
    if (adapter instanceof MessagingAdapter) {
      MessagingSecurityConstraintManager manager =
          ((MessagingAdapter) adapter).getSecurityConstraintManager();
      if (manager != null) manager.assertSendAuthorization();
    }

    MessagePerformanceUtils.markServerPreAdapterTime(message);
    Object result = adapter.invoke(message);
    MessagePerformanceUtils.markServerPostAdapterTime(message);

    if (debug) {
      long end = System.currentTimeMillis();
      Log.getLogger(TIMING_LOG_CATEGORY)
          .debug("After invoke service: " + getId() + "; execution time = " + (end - start) + "ms");
    }

    return result;
  }
Example #21
0
  void pushMessageToClient(
      MessageClient client, MessageDestination destination, Message message, boolean evalSelector) {
    // Normally we'll process the message selector criteria as part of fetching the
    // clients which should receive this message. However, because the API exposed the evalSelecor
    // flag
    // in pushMessageToClients(Set, Message, boolean), we need to run the client.testMessage()
    // method
    // here to make sure subtopic and selector expressions are evaluated correctly in this case.
    // The general code path passes evalSelector as false, so the testMessage() method is not
    // generally
    // invoked as part of a message push operation.
    if (evalSelector && !client.testMessage(message, destination)) {
      return;
    }

    // Push the message to the client. Note that client level outbound throttling
    // might still happen at the FlexClientOutboundQueueProcessor level.
    try {
      // Only update client last use if the message is not a pushed server command.
      if (!(message instanceof CommandMessage)) client.updateLastUse();

      // Remove any data in the base message that should not be included in the multicast copies.
      Map messageHeaders = message.getHeaders();
      messageHeaders.remove(Message.FLEX_CLIENT_ID_HEADER);
      messageHeaders.remove(Message.ENDPOINT_HEADER);

      // Add the default message priority headers, if it's not already set.
      int priority = destination.getServerSettings().getPriority();
      if (priority != -1) {
        Object header = message.getHeader(Message.PRIORITY_HEADER);
        if (header == null) message.setHeader(Message.PRIORITY_HEADER, priority);
      }

      // FIXME: [Pete] Investigate whether this is a performance issue.
      // We also need to ensure message ids do not expose FlexClient ids
      // message.setMessageId(UUIDUtils.createUUID());

      // We need a unique instance of the message for each client; both to prevent
      // outbound queue processing for various clients from interfering with each other
      // as well as needing to target the copy of the message to a specific MessageAgent
      // instance on the client.
      Message messageForClient = (Message) message.clone();

      // the MPIUTil call will be a no-op if MPI is not enabled.  Otherwise it will add
      // a server pre-push processing timestamp to the MPI object
      MessagePerformanceUtils.markServerPrePushTime(message);
      MessagePerformanceUtils.markServerPostAdapterTime(message);
      MessagePerformanceUtils.markServerPostAdapterExternalTime(message);

      // Target the message to a specific MessageAgent on the client.
      messageForClient.setClientId(client.getClientId());

      if (debug)
        Log.getLogger(MessageService.LOG_CATEGORY)
            .debug(
                "Routing message to FlexClient id:"
                    + client.getFlexClient().getId()
                    + "', MessageClient id: "
                    + client.getClientId());

      getMessageBroker().routeMessageToMessageClient(messageForClient, client);
    } catch (MessageException ignore) {
      // Client is subscribed but has disconnected or the network failed.
      // There's nothing we can do to correct this so just continue server processing.
    }
  }