Package org.codehaus.enunciate.contract.jaxws

Source Code of org.codehaus.enunciate.contract.jaxws.WebFault

/*
* 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.contract.jaxws;

import com.sun.mirror.declaration.*;
import com.sun.mirror.type.ArrayType;
import com.sun.mirror.type.ClassType;
import com.sun.mirror.type.PrimitiveType;
import com.sun.mirror.type.TypeMirror;
import com.sun.mirror.util.SourcePosition;
import net.sf.jelly.apt.decorations.TypeMirrorDecorator;
import net.sf.jelly.apt.decorations.declaration.DecoratedClassDeclaration;
import net.sf.jelly.apt.decorations.declaration.PropertyDeclaration;
import net.sf.jelly.apt.decorations.type.DecoratedTypeMirror;
import net.sf.jelly.apt.freemarker.FreemarkerModel;
import org.codehaus.enunciate.ClientName;
import org.codehaus.enunciate.apt.EnunciateFreemarkerModel;
import org.codehaus.enunciate.contract.Facet;
import org.codehaus.enunciate.contract.HasFacets;
import org.codehaus.enunciate.contract.jaxb.ElementDeclaration;
import org.codehaus.enunciate.contract.jaxb.ImplicitChildElement;
import org.codehaus.enunciate.contract.jaxb.ImplicitRootElement;
import org.codehaus.enunciate.contract.jaxb.adapters.Adaptable;
import org.codehaus.enunciate.contract.jaxb.adapters.AdapterType;
import org.codehaus.enunciate.contract.jaxb.adapters.AdapterUtil;
import org.codehaus.enunciate.contract.jaxb.types.XmlType;
import org.codehaus.enunciate.contract.jaxb.types.XmlTypeException;
import org.codehaus.enunciate.contract.jaxb.types.XmlTypeFactory;
import org.codehaus.enunciate.contract.validation.ValidationException;
import org.codehaus.enunciate.soap.annotations.WebFaultPropertyOrder;
import org.codehaus.enunciate.util.MapType;
import org.codehaus.enunciate.util.MapTypeUtil;

import javax.xml.bind.annotation.XmlAttachmentRef;
import javax.xml.bind.annotation.XmlMimeType;
import javax.xml.bind.annotation.XmlTransient;
import javax.xml.namespace.QName;
import java.util.*;

/**
* A fault that is declared potentially thrown in some web service call.
*
* @author Ryan Heaton
*/
public class WebFault extends DecoratedClassDeclaration implements WebMessage, WebMessagePart, ImplicitRootElement, HasFacets {

  private final javax.xml.ws.WebFault annotation;
  private final ClassType explicitFaultBeanType;
  private final Set<Facet> facets = new TreeSet<Facet>();

  public WebFault(ClassDeclaration delegate) {
    super(delegate);

    this.annotation = getAnnotation(javax.xml.ws.WebFault.class);

    ClassType explicitFaultBeanType = null;
    Collection<PropertyDeclaration> properties = getProperties();
    PropertyDeclaration faultInfoProperty = null;
    for (PropertyDeclaration propertyDeclaration : properties) {
      if ("faultInfo".equals(propertyDeclaration.getPropertyName())) {
        faultInfoProperty = propertyDeclaration;
        break;
      }
    }

    if ((faultInfoProperty != null) && (faultInfoProperty.getPropertyType() instanceof ClassType)) {
      ClassType faultInfoType = (ClassType) faultInfoProperty.getPropertyType();
      if (faultInfoType.getDeclaration() == null) {
        throw new ValidationException(getPosition(), getQualifiedName() + ": class not found: " + faultInfoType + ".");
      }

      boolean messageConstructorFound = false;
      boolean messageAndThrowableConstructorFound = false;
      Collection<ConstructorDeclaration> constructors = getConstructors();
      for (ConstructorDeclaration constructor : constructors) {
        if (constructor.getModifiers().contains(Modifier.PUBLIC)) {
          ParameterDeclaration[] parameters = constructor.getParameters().toArray(new ParameterDeclaration[constructor.getParameters().size()]);
          if (parameters.length >= 2) {
            DecoratedTypeMirror param0Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[0].getType());
            DecoratedTypeMirror param1Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[1].getType());
            if (parameters.length == 2) {
              messageConstructorFound |= param0Type.isInstanceOf(String.class.getName()) && param1Type.isInstanceOf(faultInfoType.getDeclaration().getQualifiedName());
            }
            else if (parameters.length == 3) {
              DecoratedTypeMirror param2Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[2].getType());
              messageAndThrowableConstructorFound |= param0Type.isInstanceOf(String.class.getName())
                && param1Type.isInstanceOf(faultInfoType.getDeclaration().getQualifiedName())
                && param2Type.isInstanceOf(Throwable.class.getName());
            }
          }
        }
      }

      if (messageConstructorFound && messageAndThrowableConstructorFound) {
        explicitFaultBeanType = faultInfoType;
      }
    }

    if (faultInfoProperty != null && explicitFaultBeanType == null) {
      throw new ValidationException(faultInfoProperty.getPosition(), "The 'getFaultInfo' method is only allowed on a web fault if you're " +
        "declaring an explicit fault bean, and you don't have the right constructor signatures set up in order for '" +
        faultInfoProperty.getPropertyType() + "' to be an explicit fault bean.");
    }

    this.explicitFaultBeanType = explicitFaultBeanType;
    this.facets.addAll(Facet.gatherFacets(delegate));
  }

  /**
   * The message name of this fault.
   *
   * @return The message name of this fault.
   */
  public String getMessageName() {
    return getSimpleName();
  }

  /**
   * The message documentation for a fault is the documentation for its type.
   *
   * @return The documentation for its type.
   */
  public String getMessageDocs() {
    return getElementDocs();
  }

  @Override
  public Set<Facet> getFacets() {
    return this.facets;
  }

  /**
   * The element name of the implicit web fault bean, or null if this isn't an implicit web fault.
   *
   * @return The element name of the implicit web fault, or null.
   */
  public String getElementName() {
    String name = null;

    if (isImplicitSchemaElement()) {
      name = getSimpleName();

      if ((annotation != null) && (annotation.name() != null) && (!"".equals(annotation.name()))) {
        name = annotation.name();
      }
    }

    return name;
  }

  /**
   * The simple name for client-side code generation.
   *
   * @return The simple name for client-side code generation.
   */
  public String getClientSimpleName() {
    String clientSimpleName = getSimpleName();
    ClientName clientName = getAnnotation(ClientName.class);
    if (clientName != null) {
      clientSimpleName = clientName.value();
    }
    return clientSimpleName;
  }

  /**
   * The comments on the fault itself.
   *
   * @return The comments on the fault itself.
   */
  public String getElementDocs() {
    String docs = getJavaDoc().toString();
    if (docs.trim().length() == 0) {
      docs = null;
    }
    return docs;
  }

  /**
   * The part name of this web fault as it would appear in wsdl.
   *
   * @return The part name of this web fault as it would appear in wsdl.
   */
  public String getPartName() {
    return getSimpleName();
  }

  /**
   * @return null.
   */
  public String getPartDocs() {
    return null;
  }

  /**
   * The qualified name of the implicit fault bean of this web fault, or null if this web fault
   * does not define an implicit faul bean.
   *
   * @return The qualified name of the implicit fault bean of this web fault.
   */
  public String getImplicitFaultBeanQualifiedName() {
    String faultBean = null;

    if (isImplicitSchemaElement()) {
      faultBean = getPackage().getQualifiedName() + ".jaxws." + getSimpleName() + "Bean";

      if ((annotation != null) && (annotation.faultBean() != null) && (!"".equals(annotation.faultBean()))) {
        faultBean = annotation.faultBean();
      }
    }

    return faultBean;
  }

  /**
   * A web fault has an explicit fault bean if all three of the following are present:
   * <p/>
   * <ol>
   * <li>A getFaultInfo method that returns the bean instance of a class type.
   * <li>A constructor taking a message and bean instance.
   * <li>A constructor taking a message, a bean instance, and a cause.
   * </ol>
   *
   * @return The type of the explicit fault bean, if exists, or null otherwise.
   */
  public ClassType getExplicitFaultBeanType() {
    return explicitFaultBeanType;
  }

  /**
   * A web fault has an explicit fault bean if all three of the following are present:
   * <p/>
   * <ol>
   * <li>A getFaultInfo method that returns the bean instance of a class type.
   * <li>A constructor taking a message and bean instance.
   * <li>A constructor taking a message, a bean instance, and a cause.
   * </ol>
   *
   * @return The explicit fault bean of this web fault, if exists, or null otherwise.
   */
  public ElementDeclaration findExplicitFaultBean() {
    if (this.explicitFaultBeanType == null || this.explicitFaultBeanType.getDeclaration() == null) {
      return null;
    }

    return ((EnunciateFreemarkerModel) FreemarkerModel.get()).findElementDeclaration(this.explicitFaultBeanType.getDeclaration());
  }

  /**
   * @return {@link org.codehaus.enunciate.contract.jaxws.WebMessagePart.ParticleType#ELEMENT}
   */
  public ParticleType getParticleType() {
    return ParticleType.ELEMENT;
  }

  /**
   * The qname reference to the fault info.
   *
   * @return The qname reference to the fault info.
   */
  public QName getParticleQName() {
    ElementDeclaration faultBean = findExplicitFaultBean();
    if (faultBean != null) {
      return new QName(faultBean.getNamespace(), faultBean.getName());
    }
    else {
      return new QName(getTargetNamespace(), getElementName());
    }
  }

  /**
   * Gets the target namespace of the implicit fault bean, or null if this web fault defines
   * an explicit fault info bean.
   *
   * @return the target namespace of the implicit fault bean, or null.
   */
  public String getTargetNamespace() {
    String targetNamespace = null;

    if (isImplicitSchemaElement()) {
      if (annotation != null) {
        targetNamespace = annotation.targetNamespace();
      }

      if ((targetNamespace == null) || ("".equals(targetNamespace))) {
        targetNamespace = calculateNamespaceURI();
      }
    }

    return targetNamespace;
  }


  /**
   * Calculates a namespace URI for a given package.  Default implementation uses the algorithm defined in
   * section 3.2 of the jax-ws spec.
   *
   * @return The calculated namespace uri.
   */
  protected String calculateNamespaceURI() {
    PackageDeclaration pkg = getPackage();
    if ((pkg == null) || ("".equals(pkg.getQualifiedName()))) {
      throw new ValidationException(getPosition(), getQualifiedName() + ": a web fault in no package must specify a target namespace.");
    }

    String[] tokens = pkg.getQualifiedName().split("\\.");
    String uri = "http://";
    for (int i = tokens.length - 1; i >= 0; i--) {
      uri += tokens[i];
      if (i != 0) {
        uri += ".";
      }
    }
    uri += "/";
    return uri;
  }

  /**
   * If there is an explicit fault bean, it will be a root schema element referencing its own type. Otherwise,
   * the type is anonymous.
   *
   * @return null.
   */
  public QName getTypeQName() {
    return null;
  }

  /**
   * This web fault defines an implicit schema element if it does not have an explicit fault bean.
   *
   * @return Whether this web fault defines an implicit schema element.
   */
  public boolean isImplicitSchemaElement() {
    return (this.explicitFaultBeanType == null);
  }

  /**
   * If this is an implicit fault bean, return the child elements.
   *
   * @return The child elements of the bean, or null if none.
   */
  public Collection<ImplicitChildElement> getChildElements() {
    if (!isImplicitSchemaElement()) {
      return Collections.emptyList();
    }

    Set<ImplicitChildElement> childElements = new TreeSet<ImplicitChildElement>(new Comparator<ImplicitChildElement>() {
      public int compare(ImplicitChildElement o1, ImplicitChildElement o2) {
        return o1.getElementName().compareTo(o2.getElementName());
      }
    });

    for (PropertyDeclaration property : getAllFaultProperties(this)) {
      String propertyName = property.getPropertyName();
      if (("cause".equals(propertyName)) || ("localizedMessage".equals(propertyName)) || ("stackTrace".equals(propertyName)) || "suppressed".equals(propertyName)) {
        continue;
      }

      childElements.add(new FaultBeanChildElement(property, this));
    }

    final WebFaultPropertyOrder propOrder = getAnnotation(WebFaultPropertyOrder.class);
    if (propOrder != null) {
      Set<ImplicitChildElement> resorted = new TreeSet<ImplicitChildElement>(new Comparator<ImplicitChildElement>() {
        public int compare(ImplicitChildElement o1, ImplicitChildElement o2) {
          int index1 = -1;
          int index2 = -1;
          for (int i = 0; i < propOrder.value().length; i++) {
            String prop = propOrder.value()[i];
            if (o1.getElementName().equals(prop)) {
              index1 = i;
            }
            if (o2.getElementName().equals(prop)) {
              index2 = i;
            }
          }


          if (index1 < 0) {
            throw new ValidationException(WebFault.this.getPosition(), WebFault.this.getQualifiedName() + ": @WebFaultPropertyOrder doesn't specify a property '" + o1.getElementName() + "'.");
          }
          else if (index2 < 0) {
            throw new ValidationException(WebFault.this.getPosition(), WebFault.this.getQualifiedName() + ": @WebFaultPropertyOrder doesn't specify a property '" + o2.getElementName() + "'.");
          }
          else {
            return index1 - index2;
          }
        }
      });
      resorted.addAll(childElements);
      childElements = resorted;
    }

    return childElements;
  }

  /**
   * Gets all properties, including properties from the superclass.
   *
   * @param declaration The declaration from which to get all properties.
   * @return All properties.
   */
  protected Collection<PropertyDeclaration> getAllFaultProperties(DecoratedClassDeclaration declaration) {
    ArrayList<PropertyDeclaration> properties = new ArrayList<PropertyDeclaration>();

    Set<String> excludedProperties = new TreeSet<String>();
    while ((declaration != null) && (!Object.class.getName().equals(declaration.getQualifiedName()))) {
      for (PropertyDeclaration property : declaration.getProperties()) {
        if (property.getGetter() != null &&
          property.getAnnotation(XmlTransient.class) == null &&
          property.getAnnotation(org.codehaus.enunciate.XmlTransient.class) == null &&
          !excludedProperties.contains(property.getPropertyName())) {
          //only the readable properties that are not marked with @XmlTransient
          properties.add(property);
        }
        else {
          excludedProperties.add(property.getPropertyName());
        }
      }

      declaration = (DecoratedClassDeclaration) declaration.getSuperclass().getDeclaration();
    }

    return properties;
  }

  /**
   * There's only one part to a web fault.
   *
   * @return this.
   */
  public Collection<WebMessagePart> getParts() {
    return new ArrayList<WebMessagePart>(Arrays.asList(this));
  }

  /**
   * @return false
   */
  public boolean isInput() {
    return false;
  }

  /**
   * @return true
   */
  public boolean isOutput() {
    return false;
  }

  /**
   * @return false
   */
  public boolean isHeader() {
    return false;
  }

  /**
   * @return true
   */
  public boolean isFault() {
    return true;
  }

  public WebMethod getWebMethod() {
    throw new UnsupportedOperationException("Web faults aren't associated with a specific web method.");
  }

  public static class FaultBeanChildElement implements Adaptable, ImplicitChildElement {

    private final PropertyDeclaration property;
    private final int minOccurs;
    private final String maxOccurs;
    private final AdapterType adaperType;
    private final WebFault webFault;

    private FaultBeanChildElement(PropertyDeclaration property, WebFault webFault) {
      DecoratedTypeMirror propertyType = (DecoratedTypeMirror) property.getPropertyType();
      this.adaperType = AdapterUtil.findAdapterType(property.getGetter());
      int minOccurs = propertyType.isPrimitive() ? 1 : 0;
      boolean unbounded = propertyType.isCollection() || propertyType.isArray();
      if (propertyType.isArray()) {
        TypeMirror componentType = ((ArrayType) propertyType).getComponentType();
        //special case for byte[]
        if ((componentType instanceof PrimitiveType) && (((PrimitiveType) componentType).getKind() == PrimitiveType.Kind.BYTE)) {
          unbounded = false;
        }
      }
      String maxOccurs = unbounded ? "unbounded" : "1";

      this.property = property;
      this.minOccurs = minOccurs;
      this.maxOccurs = maxOccurs;
      this.webFault = webFault;
    }

    public PropertyDeclaration getProperty() {
      return property;
    }

    public String getElementName() {
      return property.getPropertyName();
    }

    public String getTargetNamespace() {
      return webFault.getTargetNamespace();
    }

    public String getElementDocs() {
      String docs = property.getJavaDoc().toString();
      if (docs.trim().length() == 0) {
        docs = null;
      }
      return docs;
    }

    public XmlType getXmlType() {
      try {
        XmlType xmlType = XmlTypeFactory.findSpecifiedType(this);
        if (xmlType == null) {
          xmlType = XmlTypeFactory.getXmlType(getType());
        }
        return xmlType;
      }
      catch (XmlTypeException e) {
        throw new ValidationException(property.getPosition(), "Error with property '" + property.getPropertyName() + "' of fault '" +
          webFault.getQualifiedName() + "'. " + e.getMessage());
      }
    }

    public String getMimeType() {
      XmlMimeType mimeType = property.getAnnotation(XmlMimeType.class);
      return mimeType == null ? null : mimeType.value();
    }

    public boolean isSwaRef() {
      return property.getAnnotation(XmlAttachmentRef.class) != null;
    }

    public QName getTypeQName() {
      return getXmlType().getQname();
    }

    public int getMinOccurs() {
      return minOccurs;
    }

    public String getMaxOccurs() {
      return maxOccurs;
    }

    public TypeMirror getType() {
      TypeMirror propertyType = property.getPropertyType();
      MapType mapType = MapTypeUtil.findMapType(propertyType);
      if (mapType != null) {
        propertyType = mapType;
      }
      return propertyType;
    }

    public boolean isAdapted() {
      return this.adaperType != null;
    }

    public AdapterType getAdapterType() {
      return this.adaperType;
    }

    public SourcePosition getPosition() {
      return property.getPosition();
    }
  }

}
TOP

Related Classes of org.codehaus.enunciate.contract.jaxws.WebFault

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.