/**
  * Instantiate the service. Typically the derived constructor will call {@ #subscribe(String,
  * String)} to map subscriptions to methods.
  *
  * @param bayeux The bayeux instance.
  * @param name The name of the service (used as client ID prefix).
  * @param maxThreads The size of a ThreadPool to create to handle messages.
  * @param synchronous True if message delivery will be synchronized on the client.
  */
 public BayeuxService(Bayeux bayeux, String name, int maxThreads, boolean synchronous) {
   if (maxThreads > 0) setThreadPool(new QueuedThreadPool(maxThreads));
   _name = name;
   _bayeux = bayeux;
   _client = _bayeux.newClient(name);
   _listener = (synchronous) ? new SyncListen() : new AsyncListen();
   _client.addListener(_listener);
 }
  /**
   * Subscribe to a channel. Subscribe to channel and map a method to handle received messages. The
   * method must have a unique name and one of the following signatures:
   *
   * <ul>
   *   <li><code>myMethod(Client fromClient,Object data)</code>
   *   <li><code>myMethod(Client fromClient,Object data,String id)</code>
   *   <li><code>myMethod(Client fromClient,String channel,Object data,String id)</code> The data
   *       parameter can be typed if the type of the data object published by the client is known
   *       (typically Map<String,Object>). If the type of the data parameter is {@link Message} then
   *       the message object itself is passed rather than just the data.
   *       <p>Typically a service will subscribe to a channel in the "/service/**" space which is
   *       not a broadcast channel. Messages published to these channels are only delivered to
   *       server side clients like this service.
   *       <p>Any object returned by a mapped subscription method is delivered to the calling client
   *       and not broadcast. If the method returns void or null, then no response is sent. A mapped
   *       subscription method may also call {@link #send(Client, String, Object, String)} to
   *       deliver a response message(s) to different clients and/or channels. It may also publish
   *       methods via the normal {@link Bayeux} API.
   *       <p>
   *
   * @param channelId The channel to subscribe to
   * @param methodName The name of the method on this object to call when messages are recieved.
   */
  protected void subscribe(String channelId, String methodName) {
    Method method = null;

    Class<?> c = this.getClass();
    while (c != null && c != Object.class) {
      Method[] methods = c.getDeclaredMethods();
      for (int i = methods.length; i-- > 0; ) {
        if (methodName.equals(methods[i].getName())) {
          if (method != null)
            throw new IllegalArgumentException("Multiple methods called '" + methodName + "'");
          method = methods[i];
        }
      }
      c = c.getSuperclass();
    }

    if (method == null) throw new NoSuchMethodError(methodName);
    int params = method.getParameterTypes().length;
    if (params < 2 || params > 4)
      throw new IllegalArgumentException(
          "Method '" + methodName + "' does not have 2or3 parameters");
    if (!Client.class.isAssignableFrom(method.getParameterTypes()[0]))
      throw new IllegalArgumentException(
          "Method '" + methodName + "' does not have Client as first parameter");

    Channel channel = _bayeux.getChannel(channelId, true);

    if (((ChannelImpl) channel).getChannelId().isWild()) {
      final Method m = method;
      Client wild_client = _bayeux.newClient(_name + "-wild");
      wild_client.addListener(
          _listener instanceof MessageListener.Asynchronous
              ? new AsyncWildListen(wild_client, m)
              : new SyncWildListen(wild_client, m));
      channel.subscribe(wild_client);
    } else {
      _methods.put(channelId, method);
      channel.subscribe(_client);
    }
  }
 /**
  * Send data to a individual client. The data passed is sent to the client as the "data" member of
  * a message with the given channel and id. The message is not published on the channel and is
  * thus not broadcast to all channel subscribers. However to the target client, the message
  * appears as if it was broadcast.
  *
  * <p>Typcially this method is only required if a service method sends response(s) to channels
  * other than the subscribed channel. If the response is to be sent to the subscribed channel,
  * then the data can simply be returned from the subscription method.
  *
  * @param toClient The target client
  * @param onChannel The channel the message is for
  * @param data The data of the message
  * @param id The id of the message (or null for a random id).
  */
 protected void send(Client toClient, String onChannel, Object data, String id) {
   toClient.deliver(getClient(), onChannel, data, id);
 }