public void start() throws Exception {
    log.info("Starting Gravity...");
    synchronized (this) {
      if (!started) {
        adapterFactory = new AdapterFactory(this);
        internalStart();
        serverChannel = new ServerChannel(this, ServerChannel.class.getName(), null, null);

        if (gravityConfig.isUseUdp()) {
          ServiceLoader<UdpReceiverFactory> loader = ServiceLoader.load(UdpReceiverFactory.class);
          Iterator<UdpReceiverFactory> factories = loader.iterator();
          if (factories.hasNext()) {
            udpReceiverFactory = factories.next();
            udpReceiverFactory.setPort(gravityConfig.getUdpPort());
            udpReceiverFactory.setNio(gravityConfig.isUdpNio());
            udpReceiverFactory.setConnected(gravityConfig.isUdpConnected());
            udpReceiverFactory.setSendBufferSize(gravityConfig.getUdpSendBufferSize());
            udpReceiverFactory.start();
          } else log.warn("UDP receiver factory not found");
        }

        started = true;
      }
    }
    log.info("Gravity successfully started.");
  }
  public void stop(boolean now) throws Exception {
    log.info("Stopping Gravity (now=%s)...", now);
    synchronized (this) {
      if (adapterFactory != null) {
        try {
          adapterFactory.stopAll();
        } catch (Exception e) {
          log.error(e, "Error while stopping adapter factory");
        }
        adapterFactory = null;
      }

      if (serverChannel != null) {
        try {
          removeChannel(serverChannel.getId(), false);
        } catch (Exception e) {
          log.error(e, "Error while removing server channel: %s", serverChannel);
        }
        serverChannel = null;
      }

      if (channelsTimer != null) {
        try {
          channelsTimer.cancel();
        } catch (Exception e) {
          log.error(e, "Error while cancelling channels timer");
        }
        channelsTimer = null;
      }

      if (gravityPool != null) {
        try {
          if (now) gravityPool.shutdownNow();
          else gravityPool.shutdown();
        } catch (Exception e) {
          log.error(e, "Error while stopping thread pool");
        }
        gravityPool = null;
      }

      if (udpReceiverFactory != null) {
        try {
          udpReceiverFactory.stop();
        } catch (Exception e) {
          log.error(e, "Error while stopping udp receiver factory");
        }
        udpReceiverFactory = null;
      }

      started = false;
    }
    log.info("Gravity sucessfully stopped.");
  }
  protected void createUdpReceiver(UdpReceiverFactory factory, AsyncHttpContext asyncHttpContext) {
    OutputStream os = null;
    try {
      Message connectMessage = asyncHttpContext.getConnectMessage();

      if (udpReceiver == null || udpReceiver.isClosed())
        udpReceiver = factory.newReceiver(this, asyncHttpContext.getRequest(), connectMessage);

      AsyncMessage reply = udpReceiver.acknowledge(connectMessage);

      HttpServletRequest request = asyncHttpContext.getRequest();
      HttpServletResponse response = asyncHttpContext.getResponse();

      GraniteContext context =
          HttpGraniteContext.createThreadIntance(
              gravity.getGraniteConfig(), gravity.getServicesConfig(), null, request, response);
      ((AMFContextImpl) context.getAMFContext())
          .setCurrentAmf3Message(asyncHttpContext.getConnectMessage());

      GravityServletUtil.serialize(
          gravity,
          response,
          new AsyncMessage[] {reply},
          ContentType.forMimeType(request.getContentType()));
    } catch (ServletException e) {
      log.error(e, "Could not send UDP connect acknowledgement to channel: %s", this);
    } catch (IOException e) {
      log.error(e, "Could not send UDP connect acknowledgement to channel: %s", this);
    } finally {
      try {
        GraniteContext.release();
      } catch (Exception e) {
        // should never happen...
      }

      // Close output stream.
      try {
        if (os != null) {
          try {
            os.close();
          } catch (IOException e) {
            log.warn(e, "Could not close output stream (ignored)");
          }
        }
      } finally {
        releaseAsyncHttpContext(asyncHttpContext);
      }
    }
  }
  public boolean runReceived(AsyncHttpContext asyncHttpContext) {

    GravityInternal gravity = getGravity();

    if (asyncHttpContext != null && gravity.hasUdpReceiverFactory()) {
      UdpReceiverFactory factory = gravity.getUdpReceiverFactory();

      if (factory.isUdpConnectRequest(asyncHttpContext.getConnectMessage())) {
        createUdpReceiver(factory, asyncHttpContext);
        return true;
      }

      if (udpReceiver != null) {
        if (!udpReceiver.isClosed()) udpReceiver.close(false);
        udpReceiver = null;
      }
    }

    boolean httpAsParam = (asyncHttpContext != null);
    LinkedList<AsyncMessage> messages = null;
    OutputStream os = null;

    try {
      receivedQueueLock.lock();
      try {
        // Do we have any pending messages?
        if (receivedQueue.isEmpty()) return false;

        // Do we have a valid http context?
        if (asyncHttpContext == null) {
          asyncHttpContext = acquireAsyncHttpContext();
          if (asyncHttpContext == null) return false;
        }

        // Both conditions are ok, get all pending messages.
        messages = receivedQueue;
        receivedQueue = new LinkedList<AsyncMessage>();
      } finally {
        receivedQueueLock.unlock();
      }

      HttpServletRequest request = asyncHttpContext.getRequest();
      HttpServletResponse response = asyncHttpContext.getResponse();

      // Set response messages correlation ids to connect request message id.
      String correlationId = asyncHttpContext.getConnectMessage().getMessageId();
      AsyncMessage[] messagesArray = new AsyncMessage[messages.size()];
      int i = 0;
      for (AsyncMessage message : messages) {
        message.setCorrelationId(correlationId);
        messagesArray[i++] = message;
      }

      // Setup serialization context (thread local)
      GraniteContext context =
          HttpGraniteContext.createThreadIntance(
              gravity.getGraniteConfig(), gravity.getServicesConfig(), null, request, response);
      ((AMFContextImpl) context.getAMFContext())
          .setCurrentAmf3Message(asyncHttpContext.getConnectMessage());

      // Write messages to response output stream.
      GravityServletUtil.serialize(
          gravity, response, messagesArray, ContentType.forMimeType(request.getContentType()));

      return true; // Messages were delivered, http context isn't valid anymore.
    } catch (ServletException e) {
      log.error(e, "Configuration error for channel: %s", getId());

      return true;
    } catch (IOException e) {
      log.warn(e, "Could not send messages to channel: %s (retrying later)", getId());

      GravityConfig gravityConfig = getGravity().getGravityConfig();
      if (gravityConfig.isRetryOnError()) {
        receivedQueueLock.lock();
        try {
          if (receivedQueue.size() + messages.size()
              > gravityConfig.getMaxMessagesQueuedPerChannel()) {
            log.warn(
                "Channel %s has reached its maximum queue capacity %s (throwing %s messages)",
                getId(), gravityConfig.getMaxMessagesQueuedPerChannel(), messages.size());
          } else receivedQueue.addAll(0, messages);
        } finally {
          receivedQueueLock.unlock();
        }
      }

      return true; // Messages weren't delivered, but http context isn't valid anymore.
    } finally {
      // Cleanup serialization context (thread local)
      try {
        GraniteContext.release();
      } catch (Exception e) {
        // should never happen...
      }

      // Close output stream.
      try {
        if (os != null) {
          try {
            os.close();
          } catch (IOException e) {
            log.warn(e, "Could not close output stream (ignored)");
          }
        }
      } finally {
        // Cleanup http context (only if this method wasn't explicitly called with a non null
        // AsyncHttpContext from the servlet).
        if (!httpAsParam) releaseAsyncHttpContext(asyncHttpContext);
      }
    }
  }