private Message handleDisconnectMessage(
      final ChannelFactory<?> channelFactory, CommandMessage message) {
    Channel client = getChannel(channelFactory, (String) message.getClientId());
    if (client == null) return handleUnknownClientMessage(message);

    removeChannel(client.getId(), false);

    AcknowledgeMessage reply = new AcknowledgeMessage(message);
    reply.setDestination(message.getDestination());
    reply.setClientId(client.getId());
    return reply;
  }
  private Message handlePingMessage(ChannelFactory<?> channelFactory, CommandMessage message) {

    Channel channel = createChannel(channelFactory, (String) message.getClientId());

    AsyncMessage reply = new AcknowledgeMessage(message);
    reply.setClientId(channel.getId());
    Map<String, Object> advice = new HashMap<String, Object>();
    advice.put(RECONNECT_INTERVAL_MS_KEY, Long.valueOf(gravityConfig.getReconnectIntervalMillis()));
    advice.put(RECONNECT_MAX_ATTEMPTS_KEY, Long.valueOf(gravityConfig.getReconnectMaxAttempts()));
    advice.put(ENCODE_MESSAGE_BODY_KEY, Boolean.valueOf(gravityConfig.isEncodeMessageBody()));
    reply.setBody(advice);
    reply.setDestination(message.getDestination());

    log.debug("handshake.handle: reply=%s", reply);

    return reply;
  }
  private Message handleUnsubscribeMessage(
      final ChannelFactory<?> channelFactory, CommandMessage message) {
    Channel channel = getChannel(channelFactory, (String) message.getClientId());
    if (channel == null) return handleUnknownClientMessage(message);

    AsyncMessage reply = null;

    ServiceAdapter adapter = adapterFactory.getServiceAdapter(message);

    reply = (AcknowledgeMessage) adapter.manage(channel, message);

    postManage(channel);

    if (!(reply instanceof ErrorMessage)) {
      // Remove subscription message in distributed data (clustering).
      try {
        DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance();
        if (gdd != null) {
          String subscriptionId =
              (String) message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
          log.debug(
              "Removing subscription message from channel info: %s - %s",
              channel.getId(), subscriptionId);
          gdd.removeSubcription(channel.getId(), subscriptionId);
        }
      } catch (Exception e) {
        log.error(
            e,
            "Could not remove subscription from distributed data: %s - %s",
            channel.getId(),
            message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER));
      }
    }

    reply.setDestination(message.getDestination());
    reply.setClientId(channel.getId());
    reply.getHeaders().putAll(message.getHeaders());

    return reply;
  }
  private Message handleSubscribeMessage(
      final ChannelFactory<?> channelFactory,
      final CommandMessage message,
      final boolean saveMessageInSession) {

    final GraniteContext context = GraniteContext.getCurrentInstance();

    // Get and check destination.
    final Destination destination =
        context
            .getServicesConfig()
            .findDestinationById(message.getMessageRefType(), message.getDestination());

    if (destination == null) return getInvalidDestinationError(message);

    GravityInvocationContext invocationContext =
        new GravityInvocationContext(message, destination) {
          @Override
          public Object invoke() throws Exception {
            // Subscribe...
            Channel channel = getChannel(channelFactory, (String) message.getClientId());
            if (channel == null) return handleUnknownClientMessage(message);

            String subscriptionId =
                (String) message.getHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER);
            if (subscriptionId == null) {
              subscriptionId = UUIDUtil.randomUUID();
              message.setHeader(AsyncMessage.DESTINATION_CLIENT_ID_HEADER, subscriptionId);
            }

            DistributedData gdd = graniteConfig.getDistributedDataFactory().getInstance();
            if (gdd != null) {
              if (!gdd.hasChannelId(channel.getId())) {
                gdd.addChannelId(
                    channel.getId(),
                    channel.getFactory().getClass().getName(),
                    context.getClientType());
                log.debug("Stored channel %s in distributed data", channel.getId());
              }

              if (Boolean.TRUE
                  .toString()
                  .equals(destination.getProperties().get("session-selector"))) {
                String selector = gdd.getDestinationSelector(destination.getId());
                log.debug("Session selector found: %s", selector);
                if (selector != null) message.setHeader(CommandMessage.SELECTOR_HEADER, selector);
              }
            }

            ServiceAdapter adapter = adapterFactory.getServiceAdapter(message);

            AsyncMessage reply = (AsyncMessage) adapter.manage(channel, message);

            postManage(channel);

            if (saveMessageInSession && !(reply instanceof ErrorMessage)) {
              // Save subscription message in distributed data (clustering).
              try {
                if (gdd != null) {
                  log.debug(
                      "Saving new subscription message for channel: %s - %s",
                      channel.getId(), message);
                  gdd.addSubcription(channel.getId(), message);
                }
              } catch (Exception e) {
                log.error(
                    e,
                    "Could not add subscription in distributed data: %s - %s",
                    channel.getId(),
                    subscriptionId);
              }
            }

            reply.setDestination(message.getDestination());
            reply.setClientId(channel.getId());
            reply.getHeaders().putAll(message.getHeaders());

            if (gdd != null && message.getDestination() != null) {
              gdd.setDestinationClientId(message.getDestination(), channel.getId());
              gdd.setDestinationSubscriptionId(message.getDestination(), subscriptionId);
            }

            return reply;
          }
        };

    // Check security 1 (destination).
    if (destination.getSecurizer() instanceof GravityDestinationSecurizer) {
      try {
        ((GravityDestinationSecurizer) destination.getSecurizer()).canSubscribe(invocationContext);
      } catch (Exception e) {
        return new ErrorMessage(message, e);
      }
    }

    // Check security 2 (security service).
    GraniteConfig config = context.getGraniteConfig();
    try {
      if (config.hasSecurityService() && config.getSecurityService().acceptsContext())
        return (Message) config.getSecurityService().authorize(invocationContext);

      return (Message) invocationContext.invoke();
    } catch (Exception e) {
      return new ErrorMessage(message, e, true);
    }
  }