All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.enunciate.modules.xfire.EnunciatedJAXWSServiceFactory Maven / Gradle / Ivy

/*
 * Copyright 2006-2008 Web Cohesion
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.codehaus.enunciate.modules.xfire;

import org.codehaus.xfire.XFireFactory;
import org.codehaus.xfire.annotations.AnnotationException;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import org.codehaus.xfire.annotations.WebAnnotations;
import org.codehaus.xfire.annotations.WebServiceAnnotation;
import org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations;
import org.codehaus.xfire.exchange.MessageSerializer;
import org.codehaus.xfire.fault.FaultSender;
import org.codehaus.xfire.handler.OutMessageSender;
import org.codehaus.xfire.service.OperationInfo;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.PostInvocationHandler;
import org.codehaus.xfire.service.binding.ServiceInvocationHandler;
import org.codehaus.xfire.soap.AbstractSoapBinding;
import org.codehaus.xfire.soap.SoapConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ClassUtils;

import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.xml.namespace.QName;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
import javax.xml.bind.ValidationEventHandler;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties;
import java.io.InputStream;
import java.io.IOException;

/**
 * The enunciate implementation of the JAXWS service factory.
 *
 * @author Ryan Heaton
 */
public class EnunciatedJAXWSServiceFactory extends AnnotationServiceFactory {

  private final Properties paramNames = new Properties();
  private ValidationEventHandler validationEventHandler = new DefaultValidationEventHandler();

  /**
   * An annotation service factory that is initialized with the {@link org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations} and
   * the default transport manager.
   */
  public EnunciatedJAXWSServiceFactory() {
    super(new Jsr181WebAnnotations(),
          XFireFactory.newInstance().getXFire().getTransportManager());

    InputStream in = ClassUtils.getDefaultClassLoader().getResourceAsStream("/enunciate-soap-parameter-names.properties");
    if (in != null) {
      try {
        paramNames.load(in);
      }
      catch (IOException e) {
        //fall through....
      }
    }
  }


  /**
   * Ensures that any service created has MTOM enabled.
   * 
   * @param clazz The class.
   * @param name The name.
   * @param namespace The namespace.
   * @param properties The properties.
   * @return The service.
   */
  @Override
  public Service create(final Class clazz, String name, String namespace, Map properties) {
    Service service = super.create(clazz, name, namespace, properties);
    service.setProperty(SoapConstants.MTOM_ENABLED, Boolean.TRUE.toString());
    return service;
  }

  /**
   * The handlers for a service include the defaults, with the exception of the {@link org.codehaus.enunciate.modules.xfire.EnunciatedJAXWSWebFaultHandler}.
   *
   * @param service The service for which to register the handlers.
   */
  protected void registerHandlers(Service service) {
    service.addInHandler(new ServiceInvocationHandler());
    service.addInHandler(new PostInvocationHandler());
    service.addOutHandler(new OutMessageSender());
    service.addFaultHandler(new FaultSender());
    service.addFaultHandler(new EnunciatedJAXWSWebFaultHandler());
  }

  /**
   * The service name according to the JAXWS specification is looked up with the metadata on the endpoint
   * interface and defaults to the simple name of the endpoint interface + "Service" if not specified.
   *
   * @param clazz      The class for which to lookup the service name.
   * @param annotation The relevant annotation to use.
   * @param current    A suggested name (ignored).
   * @return The service name.
   */
  @Override
  protected String createServiceName(Class clazz, WebServiceAnnotation annotation, String current) {
    WebAnnotations webAnnotations = getAnnotations();
    Class endpointInterface = clazz;
    String eiValue = annotation.getEndpointInterface();
    if (eiValue != null && eiValue.length() > 0) {
      //the metadata is supplied on another class...
      try {
        endpointInterface = loadClass(annotation.getEndpointInterface());
        if (!webAnnotations.hasWebServiceAnnotation(endpointInterface)) {
          throw new AnnotationException("Endpoint interface " + endpointInterface.getName() + " does not have a WebService annotation");
        }

        WebServiceAnnotation eiAnnotation = webAnnotations.getWebServiceAnnotation(endpointInterface);
        String serviceName = eiAnnotation.getServiceName();
        if ((serviceName != null) && (serviceName.length() > 0)) {
          return serviceName;
        }
      }
      catch (ClassNotFoundException e) {
        throw new AnnotationException("Couldn't find endpoint interface " + annotation.getEndpointInterface(), e);
      }
    }
    else {
      String serviceName = annotation.getServiceName();
      if ((serviceName != null) && (serviceName.length() > 0)) {
        return serviceName;
      }
    }

    return endpointInterface.getSimpleName() + "Service";
  }


  @Override
  public WebAnnotations getAnnotations() {
    return super.getAnnotations();
  }

  /**
   * The faults don't need to be initialized.  The {@link org.codehaus.enunciate.modules.xfire.EnunciatedJAXWSWebFaultHandler}
   * will handle it.
   *
   * @param service The service.
   * @param op      The operation.
   */
  @Override
  protected void initializeFaults(final Service service, final OperationInfo op) {
    //no-op....
  }

  /**
   * The serializer for a SOAP message.  For Enunciate it is a {@link org.codehaus.enunciate.modules.xfire.EnunciatedJAXWSMessageBinding}.
   *
   * @param binding The binding.
   * @return The default serializer for the binding.
   */
  @Override
  protected MessageSerializer getSerializer(AbstractSoapBinding binding) {
    EnunciatedJAXWSMessageBinding messageBinding = new EnunciatedJAXWSMessageBinding();
    messageBinding.setValidationEventHandler(getValidationEventHandler());
    return messageBinding;
  }

  /**
   * The input message name depends on the metadata of the operation.  If the operation is rpc/lit, the
   * input message name is the operation name.  If the operation is doc/lit bare, the input message name
   * is the element name of the input parameter.  Otherwise (doc/lit wrapped), the input message name
   * is wrapped with the wrapper element, as described in the JAXWS specification.
   *
   * @param op The operation for which to determine the input message name.
   * @return The input message name.
   */
  @Override
  protected QName createInputMessageName(OperationInfo op) {
    Method method = op.getMethod();
    Class ei = method.getDeclaringClass();
    SOAPBinding.Style style = SOAPBinding.Style.DOCUMENT;
    SOAPBinding.ParameterStyle paramStyle = SOAPBinding.ParameterStyle.WRAPPED;

    if (method.isAnnotationPresent(SOAPBinding.class)) {
      SOAPBinding annotation = method.getAnnotation(SOAPBinding.class);
      style = annotation.style();
      paramStyle = annotation.parameterStyle();
    }
    else if (ei.isAnnotationPresent(SOAPBinding.class)) {
      SOAPBinding annotation = ((SOAPBinding) ei.getAnnotation(SOAPBinding.class));
      style = annotation.style();
      paramStyle = annotation.parameterStyle();
    }

    if (style == SOAPBinding.Style.RPC) {
      //if it's an rpc-style method call, the message name is the operation name.
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      String operationName = method.getName();
      if (method.isAnnotationPresent(WebMethod.class)) {
        WebMethod annotation = method.getAnnotation(WebMethod.class);
        if (annotation.operationName().length() > 0) {
          operationName = annotation.operationName();
        }
      }

      return new QName(namespace, operationName);
    }
    else if (paramStyle == SOAPBinding.ParameterStyle.BARE) {
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      //find the first "in" param.  Presumably, there are no more, since we're BARE.
      WebParam annotation = null;
      int paramIndex;
      Annotation[][] parameterAnnotations = method.getParameterAnnotations();
      PARAM_ANNOTATIONS : for (paramIndex = 0; paramIndex < parameterAnnotations.length; paramIndex++) {
        Annotation[] annotations = parameterAnnotations[paramIndex];
        for (Annotation candidate : annotations) {
          if (candidate instanceof WebParam && !((WebParam) candidate).header()) {
            WebParam.Mode mode = ((WebParam) candidate).mode();
            switch(mode) {
              case IN:
              case INOUT:
                annotation = (WebParam) candidate;
                break PARAM_ANNOTATIONS;
            }
          }
        }
      }

      String name;
      if (annotation == null || !"".equals(annotation.name())) {
        name = method.getName();
      }
      else {
        name = annotation.name();
      }

      return new QName(namespace, name);
    }
    else {
      //default doc/lit behavior.
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      String name = method.getName();
      if (method.isAnnotationPresent(RequestWrapper.class)) {
        RequestWrapper wrapper = method.getAnnotation(RequestWrapper.class);

        if (!"".equals(wrapper.targetNamespace())) {
          namespace = wrapper.targetNamespace();
        }

        if (!"".equals(wrapper.localName())) {
          name = wrapper.localName();
        }
      }

      return new QName(namespace, name);
    }
  }

  /**
   * Loads the parameter name for the specified parameter.
   *
   * @param ei The endpoint interface.
   * @param method The method.
   * @param paramIndex The parameter index.
   * @return The parameter name.
   */
  protected String loadParamName(Class ei, Method method, int paramIndex) {
    String operationName = method.getName();
    if (method.isAnnotationPresent(WebMethod.class)) {
      WebMethod wm = method.getAnnotation(WebMethod.class);
      if (wm.operationName().length() > 0) {
        operationName = wm.operationName();
      }
    }

    String paramName = null;
    String paramNames = (String) this.paramNames.get(ei.getName() + "#" + operationName);
    if (paramNames != null) {
      String[] paramsNames = paramNames.split(",");
      if (paramsNames.length > paramIndex) {
        paramName = paramsNames[paramIndex];
      }
    }

    if (paramName == null) {
      throw new IllegalStateException("Unknown parameter name for parameter '" + paramIndex + "' of web method " + ei.getName() + "#" + operationName);
    }
    
    return paramName;
  }

  @Override
  protected QName createOutputMessageName(OperationInfo op) {
    Method method = op.getMethod();
    Class ei = method.getDeclaringClass();
    SOAPBinding.Style style = SOAPBinding.Style.DOCUMENT;
    SOAPBinding.ParameterStyle paramStyle = SOAPBinding.ParameterStyle.WRAPPED;

    if (method.isAnnotationPresent(SOAPBinding.class)) {
      SOAPBinding annotation = method.getAnnotation(SOAPBinding.class);
      style = annotation.style();
      paramStyle = annotation.parameterStyle();
    }
    else if (ei.isAnnotationPresent(SOAPBinding.class)) {
      SOAPBinding annotation = ((SOAPBinding) ei.getAnnotation(SOAPBinding.class));
      style = annotation.style();
      paramStyle = annotation.parameterStyle();
    }

    if (style == SOAPBinding.Style.RPC) {
      //if it's an rpc-style method call, the message name is the operation name.
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      String operationName = method.getName();
      if (method.isAnnotationPresent(WebMethod.class)) {
        WebMethod annotation = method.getAnnotation(WebMethod.class);
        if (annotation.operationName().length() > 0) {
          operationName = annotation.operationName();
        }
      }

      return new QName(namespace, operationName);
    }
    else if (paramStyle == SOAPBinding.ParameterStyle.BARE) {
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      String name;
      if (method.getReturnType() == Void.TYPE) {
        //find the first "out" param.  Presumably, there are no more, since we're BARE.
        WebParam annotation = null;
        int paramIndex;
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        PARAM_ANNOTATIONS : for (paramIndex = 0; paramIndex < parameterAnnotations.length; paramIndex++) {
          Annotation[] annotations = parameterAnnotations[paramIndex];
          for (Annotation candidate : annotations) {
            if (candidate instanceof WebParam && !((WebParam) candidate).header()) {
              WebParam.Mode mode = ((WebParam) candidate).mode();
              switch(mode) {
                case OUT:
                case INOUT:
                  annotation = (WebParam) candidate;
                  break PARAM_ANNOTATIONS;
              }
            }
          }
        }

        if (annotation == null || !"".equals(annotation.name())) {
          name = method.getName() + "Response";
        }
        else {
          name = annotation.name();
        }
      }
      else {
        WebResult annotation = method.getAnnotation(WebResult.class);
        name = annotation == null || "".equals(annotation.name()) ? method.getName() + "Response" : annotation.name();
      }
      
      return new QName(namespace, name);
    }
    else {
      //default doc/lit behavior.
      String namespace = ((WebService) ei.getAnnotation(WebService.class)).targetNamespace();
      if ("".equals(namespace)) {
        namespace = calculateNamespaceURI(ei);
      }

      String name = method.getName();
      if (method.isAnnotationPresent(ResponseWrapper.class)) {
        ResponseWrapper wrapper = method.getAnnotation(ResponseWrapper.class);

        if (!"".equals(wrapper.targetNamespace())) {
          namespace = wrapper.targetNamespace();
        }

        if (!"".equals(wrapper.localName())) {
          name = wrapper.localName();
        }
      }

      return new QName(namespace, name);
    }
  }

  /**
   * Overridden to fix a bug in XFire.
   *
   * @param method The method.
   * @param paramIndex The parameter index.
   * @return Whether the parameter index is an out param.
   */
  @Override
  protected boolean isOutParam(Method method, int paramIndex) {
    //xfire 1.2.1 chokes on -1...
    return paramIndex == -1 || super.isOutParam(method, paramIndex);
  }

  /**
   * Overridden to fix a bug in XFire.
   *
   * @param method The method.
   * @param paramIndex The parameter index.
   * @return Whether the parameter index is an in param.
   */
  @Override
  protected boolean isInParam(Method method, int paramIndex) {
    //xfire 1.2.1 chokes on -1...
    return paramIndex != -1 && super.isInParam(method, paramIndex);
  }

  /**
   * XFire defaults the name of the return value to "out".  The JAXWS spec says "return"....
   *
   * @param service The service.
   * @param op The operation.
   * @param method The method.
   * @param paramNumber The parameter number.
   * @param doc Whether its doc-style binding.
   * @return The out parameter name.
   */
  @Override
  protected QName getOutParameterName(final Service service, final OperationInfo op, final Method method, final int paramNumber, final boolean doc) {
    QName parameterName = super.getOutParameterName(service, op, method, paramNumber, doc);

    if (paramNumber == -1) {
      WebResult webResult = method.getAnnotation(WebResult.class);
      if ((webResult == null) || ("".equals(webResult.name()))) {
        parameterName = new QName(parameterName.getNamespaceURI(), "return");
      }
    }

    return parameterName;
  }

  /**
   * Calculates a namespace URI for a given package.  Default implementation uses the algorithm defined in
   * section 3.2 of the jax-ws spec.
   *
   * @param jaxwsClass The class for which to calculate the namespace based on the JAXWS spec.
   * @return The calculated namespace uri.
   */
  protected String calculateNamespaceURI(Class jaxwsClass) {
    Package pckg = jaxwsClass.getPackage();
    String[] tokens = pckg.getName().split("\\.");
    String uri = "http://";
    for (int i = tokens.length - 1; i >= 0; i--) {
      uri += tokens[i];
      if (i != 0) {
        uri += ".";
      }
    }
    uri += "/";
    return uri;
  }

  /**
   * The validation event handler.
   *
   * @return The validation event handler.
   */
  public ValidationEventHandler getValidationEventHandler() {
    return validationEventHandler;
  }

  /**
   * The validation event handler.
   *
   * @param validationEventHandler The validation event handler.
   */
  @Autowired( required = false )
  public void setValidationEventHandler(ValidationEventHandler validationEventHandler) {
    this.validationEventHandler = validationEventHandler;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy