/*
   *  (non-Javadoc)
   * @see org.apache.axis2.jaxws.core.controller.InvocationController#invokeAsync(org.apache.axis2.jaxws.core.InvocationContext)
   */
  public Response doInvokeAsync(MessageContext request) {
    // We need the qname of the operation being invoked to know which
    // AxisOperation the OperationClient should be based on.
    // Note that the OperationDesc is only set through use of the Proxy. Dispatch
    // clients do not use operations, so the operationDesc will be null.  In this
    // case an anonymous AxisService with anoymouns AxisOperations for the supported
    // MEPs will be created; and it is that anonymous operation name which needs to
    // be specified
    QName operationName = getOperationNameToUse(request, ServiceClient.ANON_OUT_IN_OP);

    // TODO: Will the ServiceClient stick around on the InvocationContext
    // or will we need some other mechanism of creating this?
    // Try to create an OperationClient from the passed in ServiceClient
    InvocationContext ic = request.getInvocationContext();
    ServiceClient svcClient = ic.getServiceClient();
    OperationClient opClient = createOperationClient(svcClient, operationName);

    initOperationClient(opClient, request);

    // Setup the client so that it knows whether the underlying call to
    // Axis2 knows whether or not to start a listening port for an
    // asynchronous response.
    Boolean useAsyncMep = (Boolean) request.getProperty(Constants.USE_ASYNC_MEP);
    if ((useAsyncMep != null && useAsyncMep.booleanValue())
        || opClient.getOptions().isUseSeparateListener()) {
      configureAsyncListener(opClient);
    } else {
      if (log.isDebugEnabled()) {
        log.debug(
            "Asynchronous message exchange not enabled.  The invocation will be synchronous.");
      }
    }

    AsyncResponse resp = ic.getAsyncResponseListener();
    PollingFuture pf = new PollingFuture(ic);
    opClient.setCallback(pf);

    org.apache.axis2.context.MessageContext axisRequestMsgCtx = request.getAxisMessageContext();
    try {
      execute(opClient, false, axisRequestMsgCtx);
    } catch (AxisFault af) {
      if (log.isDebugEnabled()) {
        log.debug(
            axisRequestMsgCtx.getLogIDString()
                + " AxisFault received from client: "
                + af.getMessage());
      }
      /*
       * Save the exception on the callback.  The client will learn about the error when they try to
       * retrieve the async results via the Response.get().  "Errors that occur during the invocation
       * are reported via an exception when the client attempts to retrieve the results of the operation."
       * -- JAXWS 4.3.3
       */
      pf.onError(af);
    }

    return resp;
  }
  /*
   *  (non-Javadoc)
   * @see org.apache.axis2.jaxws.core.controller.InvocationController#invokeOneWay(org.apache.axis2.jaxws.core.InvocationContext)
   */
  public void doInvokeOneWay(MessageContext request) throws WebServiceException {
    // Make sure that a one-way invocation does not attempt to use a separate channel
    // for the response.
    Boolean useAsyncMep = (Boolean) request.getProperty(Constants.USE_ASYNC_MEP);
    if (useAsyncMep != null && useAsyncMep.booleanValue()) {
      throw ExceptionFactory.makeWebServiceException(Messages.getMessage("onewayAsync"));
    }

    // We need the qname of the operation being invoked to know which
    // AxisOperation the OperationClient should be based on.
    // Note that the OperationDesc is only set through use of the Proxy. Dispatch
    // clients do not use operations, so the operationDesc will be null.  In this
    // case an anonymous AxisService with anoymouns AxisOperations for the supported
    // MEPs will be created; and it is that anonymous operation name which needs to
    // be specified
    QName operationName = getOperationNameToUse(request, ServiceClient.ANON_OUT_ONLY_OP);

    InvocationContext ic = request.getInvocationContext();
    ServiceClient svcClient = ic.getServiceClient();
    OperationClient opClient = createOperationClient(svcClient, operationName);

    initOperationClient(opClient, request);

    org.apache.axis2.context.MessageContext axisRequestMsgCtx = request.getAxisMessageContext();

    try {
      execute(opClient, true, axisRequestMsgCtx);
    } catch (AxisFault af) {
      // JAXWS 6.4.2 says to throw it...
      // Whatever exception we get here will not be from the server since a one-way
      // invocation has no response.  This will always be a SENDER fault
      if (log.isDebugEnabled()) {
        log.debug(
            axisRequestMsgCtx.getLogIDString()
                + " AxisFault received from client: "
                + af.getMessage());
      }
      throw ExceptionFactory.makeWebServiceException(ClassUtils.getRootCause(af));
    }

    return;
  }
  /*
   * (non-Javadoc)
   * @see org.apache.axis2.jaxws.core.controller.InvocationController#invoke(org.apache.axis2.jaxws.core.InvocationContext)
   */
  public MessageContext doInvoke(MessageContext request) {

    // We need the qname of the operation being invoked to know which
    // AxisOperation the OperationClient should be based on.
    // Note that the OperationDesc is only set through use of the Proxy. Dispatch
    // clients do not use operations, so the operationDesc will be null.  In this
    // case an anonymous AxisService with anoymouns AxisOperations for the supported
    // MEPs will be created; and it is that anonymous operation name which needs to
    // be specified
    QName operationName = getOperationNameToUse(request, ServiceClient.ANON_OUT_IN_OP);

    // TODO: Will the ServiceClient stick around on the InvocationContext
    // or will we need some other mechanism of creating this?
    // Try to create an OperationClient from the passed in ServiceClient
    InvocationContext ic = request.getInvocationContext();
    ServiceClient svcClient = ic.getServiceClient();
    OperationClient opClient = createOperationClient(svcClient, operationName);

    initOperationClient(opClient, request);

    // Setup the client so that it knows whether the underlying call to
    // Axis2 knows whether or not to start a listening port for an
    // asynchronous response.
    Boolean useAsyncMep = (Boolean) request.getProperty(Constants.USE_ASYNC_MEP);
    if ((useAsyncMep != null && useAsyncMep.booleanValue())
        || opClient.getOptions().isUseSeparateListener()) {
      configureAsyncListener(opClient);
    } else {
      if (log.isDebugEnabled()) {
        log.debug(
            "Asynchronous message exchange not enabled.  The invocation will be synchronous.");
      }
    }

    org.apache.axis2.context.MessageContext axisRequestMsgCtx = request.getAxisMessageContext();
    org.apache.axis2.context.MessageContext axisResponseMsgCtx = null;

    MessageContext response = null;

    AxisFault faultexception =
        null; // don't let the keyword "fault" confuse you.  This is an exception class.
    try {
      execute(opClient, true, axisRequestMsgCtx);
    } catch (AxisFault af) {
      // If an AxisFault was thrown, we need to cleanup the original OperationContext.
      // Failure to do so results in a memory leak.
      opClient.getOperationContext().cleanup();
      // save the fault in case it didn't come from the endpoint, and thus
      // there would be no message on the MessageContext
      faultexception = af;
      if (log.isDebugEnabled()) {
        log.debug(
            axisRequestMsgCtx.getLogIDString()
                + " AxisFault received from client: "
                + af.getMessage());
      }
    }

    try {
      // Collect the response MessageContext and envelope
      axisResponseMsgCtx = opClient.getMessageContext(WSDLConstants.MESSAGE_LABEL_IN_VALUE);
      response = new MessageContext(axisResponseMsgCtx);
      response.setMEPContext(request.getMEPContext());

      // If the Message object is still null, then it's possible that a
      // local AxisFault was thrown and we need to save it for later throwing
      // We do not want to create a message and go through the whole handler or
      // XMLFault processing because it's unnecessary.
      //
      // Same is true if we get a valid non-fault server response but some jaxws
      // client processing (a handler, perhaps) throws an exception.
      //
      // If the response message itself is a fault message, let it pass through.
      if ((faultexception != null)
          && ((response.getMessage() == null) || (!response.getMessage().isFault()))) {
        MessageFactory factory = (MessageFactory) FactoryRegistry.getFactory(MessageFactory.class);
        Message message = factory.create(request.getMessage().getProtocol());
        response.setLocalException(faultexception);
        response.setMessage(message);
      }

      // This assumes that we are on the ultimate execution thread
      ThreadContextMigratorUtil.performMigrationToThread(
          Constants.THREAD_CONTEXT_MIGRATOR_LIST_ID, axisResponseMsgCtx);
    } catch (Exception e) {
      throw ExceptionFactory.makeWebServiceException(e);
    }

    return response;
  }
  /*
   *  (non-Javadoc)
   * @see org.apache.axis2.jaxws.core.controller.InvocationController#invokeAsync(org.apache.axis2.jaxws.core.InvocationContext, javax.xml.ws.AsyncHandler)
   */
  public Future<?> doInvokeAsync(MessageContext request, AsyncHandler callback) {
    // We need the qname of the operation being invoked to know which
    // AxisOperation the OperationClient should be based on.
    // Note that the OperationDesc is only set through use of the Proxy. Dispatch
    // clients do not use operations, so the operationDesc will be null.  In this
    // case an anonymous AxisService with anoymouns AxisOperations for the supported
    // MEPs will be created; and it is that anonymous operation name which needs to
    // be specified
    QName operationName = getOperationNameToUse(request, ServiceClient.ANON_OUT_IN_OP);

    // TODO: Will the ServiceClient stick around on the InvocationContext
    // or will we need some other mechanism of creating this?
    // Try to create an OperationClient from the passed in ServiceClient
    InvocationContext ic = request.getInvocationContext();
    ServiceClient svcClient = ic.getServiceClient();
    OperationClient opClient = createOperationClient(svcClient, operationName);

    initOperationClient(opClient, request);

    // Setup the client so that it knows whether the underlying call to
    // Axis2 knows whether or not to start a listening port for an
    // asynchronous response.
    Boolean useAsyncMep = (Boolean) request.getProperty(Constants.USE_ASYNC_MEP);
    if ((useAsyncMep != null && useAsyncMep.booleanValue())
        || opClient.getOptions().isUseSeparateListener()) {
      configureAsyncListener(opClient);
    } else {
      if (log.isDebugEnabled()) {
        log.debug(
            "Asynchronous message exchange not enabled.  The invocation will be synchronous.");
      }
    }

    CallbackFuture cbf = null;
    if (callback != null) {
      cbf = new CallbackFuture(ic, callback);
    } else {
      throw ExceptionFactory.makeWebServiceException(Messages.getMessage("ICErr4"));
    }

    opClient.setCallback(cbf);

    org.apache.axis2.context.MessageContext axisRequestMsgCtx = request.getAxisMessageContext();
    try {
      execute(opClient, false, axisRequestMsgCtx);
    } catch (AxisFault af) {
      if (log.isDebugEnabled()) {
        log.debug(
            axisRequestMsgCtx.getLogIDString()
                + " AxisFault received from client: "
                + af.getMessage());
      }
      /*
       * Save the exception on the callback.  The client will learn about the error when they try to
       * retrieve the async results via the Response.get().  "Errors that occur during the invocation
       * are reported via an exception when the client attempts to retrieve the results of the operation."
       * -- JAXWS 4.3.3
       */

      /*
       * TODO:  This is the appropriate thing to do here since the thrown exception may occur before
       * we switch threads to the async thread.  But... what happens if we've already switched over
       * to the async thread?  So far, it appears that the exception gets set on the FutureTask
       * Concurrent object, and we never hit this scope.  This means that later, when the client
       * calls future.get(), no exception will be thrown despite what the spec says.  The client can,
       * however, retrieve errors via it's AsyncHandler.
       */
      cbf.onError(af);
    }

    return cbf.getFutureTask();
  }