/**
   * {@inheritDoc}
   *
   * @see com.corona.context.InjectMethod#invoke(com.corona.context.ContextManager,
   *     java.lang.Object)
   */
  @SuppressWarnings({"unchecked", "rawtypes"})
  @Override
  public Object invoke(final ContextManager contextManager, final Object component) {

    // find server by server name from current context and make sure it is defined
    Server server = contextManager.get(Server.class, this.serverName);
    if (server == null) {
      this.logger.error(
          "Server [{0}] isn't registered in context, should define it first", this.serverName);
      throw new ValueException(
          "Server [{0}] isn't registered in context, should define it first", this.serverName);
    }

    // transform client input stream into request object
    ServerRequest request = null;
    try {
      request = this.getRequest(contextManager, server);
    } catch (RemoteException e) {
      this.logger.error(
          "Fail to open client input stream or transform stream to server request object", e);
      return new ServerInternalErrorResponse(
          server, "Fail to get server request object: " + e.getMessage());
    }

    // execute producing method according to client request and return it in order to create server
    // response
    try {
      switch (request.getCode()) {
        case Constants.REQUEST.EXECUTE:
          Object outcome = this.execute(component, (ServerExecuteRequest) request);
          if (outcome != null) {
            return new ServerExecutedResponse(
                server,
                server.getToken(((ServerExecuteRequest) request).getToken()),
                this.getMarshaller(),
                outcome);
          } else {
            return new ServerExecutedResponse(
                server, server.getToken(((ServerExecuteRequest) request).getToken()), null, null);
          }

        case Constants.REQUEST.LOGIN:
          if (server.isSupportedVersion(((ServerLoginRequest) request).getClientLibraryVersion())) {
            String token = this.login(component, (ServerLoginRequest) request);
            if (token != null) {
              return new ServerLoggedInResponse(server, token);
            } else {
              return new ServerCantLoggedInResponse(
                  server, "Invalid user name or password to log in");
            }
          } else {
            return new ServerCantLoggedInResponse(server, "Client libray version is not supported");
          }

        case Constants.REQUEST.LOGOUT:
          this.logout(component, (ServerLogoutRequest) request);
          return new ServerLoggedOutResponse(server);

        default:
          return new ServerInvalidActionResponse(
              server, ((ServerInvalidActionRequest) request).getInvalidCode());
      }
    } catch (Exception e) {
      return new ServerFailExecuteResponse(server, e);
    }
  }
  /**
   * @param contextManager the current context manager
   * @param server the server
   * @return the request
   * @throws RemoteException if fail to translate stream to request object
   */
  private ServerRequest getRequest(final ContextManager contextManager, final Server server)
      throws RemoteException {

    // get input stream from
    InputStream input;
    try {
      input = contextManager.get(HttpServletRequest.class).getInputStream();
      input = new GZIPInputStream(input);
    } catch (IOException e) {
      this.logger.error("Fail to open client servlet request as GZIP input stream", e);
      throw new RemoteException("Fail to open client servlet request as GZIP input stream", e);
    }

    // read identifier and check whether this identifier is valid or not
    int identifier;
    try {
      identifier = input.read();
    } catch (IOException e) {
      this.logger.error("Fail to read identifier from client input stream", e);
      throw new RemoteException("Fail to read identifier from client input stream", e);
    }

    if (identifier == -1) {
      this.logger.error("Should can read identifier but client input stream is empty");
      throw new RemoteException("Should can read identifier but client input stream is empty");
    } else if (((byte) identifier) != Constants.IDENTIFIER) {
      this.logger.error("Invalid identifier [{0}] read from client input stream", identifier);
      throw new RemoteException(
          "Invalid identifier [{0}] read from client input stream", identifier);
    }

    // read action code and make sure it is read from client stream
    int action;
    try {
      action = input.read();
    } catch (IOException e) {
      this.logger.error("Fail to read action code from client input stream", e);
      throw new RemoteException("Fail to read action code from client input stream", e);
    }

    if (action == -1) {
      this.logger.error("Should can read action code but client input stream is empty");
      throw new RemoteException("Should can read action code but client input stream is empty");
    }

    // create request according to action code
    ServerRequest request;
    switch (action) {
      case Constants.REQUEST.EXECUTE:
        request = new ServerExecuteRequest(server, this.getUnmarshaller());
        break;

      case Constants.REQUEST.LOGIN:
        request = new ServerLoginRequest(server);
        break;

      case Constants.REQUEST.LOGOUT:
        request = new ServerLogoutRequest(server);
        break;

      default:
        request = new ServerInvalidActionRequest(server, (byte) action);
        break;
    }

    // read request specified data from client input stream
    request.read(input);
    return request;
  }