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

com.webcohesion.enunciate.modules.jaxws.model.WebFault Maven / Gradle / Ivy

There is a newer version: 2.18.1
Show newest version
/**
 * Copyright © 2006-2016 Web Cohesion ([email protected])
 *
 * 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 com.webcohesion.enunciate.modules.jaxws.model;

import com.webcohesion.enunciate.EnunciateException;
import com.webcohesion.enunciate.facets.Facet;
import com.webcohesion.enunciate.facets.HasFacets;
import com.webcohesion.enunciate.javac.decorations.TypeMirrorDecorator;
import com.webcohesion.enunciate.javac.decorations.element.DecoratedTypeElement;
import com.webcohesion.enunciate.javac.decorations.element.PropertyElement;
import com.webcohesion.enunciate.javac.decorations.type.DecoratedTypeMirror;
import com.webcohesion.enunciate.metadata.ClientName;
import com.webcohesion.enunciate.modules.jaxb.EnunciateJaxbContext;
import com.webcohesion.enunciate.modules.jaxb.model.ElementDeclaration;
import com.webcohesion.enunciate.modules.jaxb.model.ImplicitChildElement;
import com.webcohesion.enunciate.modules.jaxb.model.ImplicitRootElement;
import com.webcohesion.enunciate.modules.jaxb.model.adapters.Adaptable;
import com.webcohesion.enunciate.modules.jaxb.model.adapters.AdapterType;
import com.webcohesion.enunciate.modules.jaxb.model.types.XmlType;
import com.webcohesion.enunciate.modules.jaxb.model.types.XmlTypeFactory;
import com.webcohesion.enunciate.modules.jaxb.model.util.JAXBUtil;
import com.webcohesion.enunciate.modules.jaxb.model.util.MapType;
import com.webcohesion.enunciate.modules.jaxws.EnunciateJaxwsContext;
import com.webcohesion.enunciate.util.HasClientConvertibleType;
import com.webcohesion.enunciate.util.IgnoreUtils;

import javax.lang.model.element.*;
import javax.lang.model.type.ArrayType;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
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 DecoratedTypeElement implements WebMessage, WebMessagePart, ImplicitRootElement, HasFacets {

  private final javax.xml.ws.WebFault annotation;
  private final DeclaredType explicitFaultBeanType;
  private final Set facets = new TreeSet();
  private final EnunciateJaxwsContext context;
  private final DecoratedTypeMirror reference;

  public WebFault(TypeElement delegate, DecoratedTypeMirror reference, EnunciateJaxwsContext context) {
    super(delegate, context.getContext().getProcessingEnvironment());
    this.context = context;

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

    DeclaredType explicitFaultBeanType = null;
    List properties = getProperties();
    PropertyElement faultInfoProperty = null;
    for (PropertyElement propertyDeclaration : properties) {
      if ("faultInfo".equals(propertyDeclaration.getPropertyName())) {
        faultInfoProperty = propertyDeclaration;
        break;
      }
    }

    if ((faultInfoProperty != null) && (faultInfoProperty.getPropertyType() instanceof DeclaredType)) {
      DeclaredType faultInfoType = (DeclaredType) faultInfoProperty.getPropertyType();
      if (faultInfoType.asElement() == null) {
        throw new EnunciateException(getQualifiedName() + ": class not found: " + faultInfoType + ".");
      }

      boolean messageConstructorFound = false;
      boolean messageAndThrowableConstructorFound = false;
      List constructors = getConstructors();
      for (ExecutableElement constructor : constructors) {
        if (constructor.getModifiers().contains(Modifier.PUBLIC)) {
          VariableElement[] parameters = constructor.getParameters().toArray(new VariableElement[constructor.getParameters().size()]);
          if (parameters.length >= 2) {
            DecoratedTypeMirror param0Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[0].asType(), this.env);
            DecoratedTypeMirror param1Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[1].asType(), this.env);
            if (parameters.length == 2) {
              messageConstructorFound |= param0Type.isInstanceOf(String.class.getName()) && param1Type.isInstanceOf(faultInfoType);
            }
            else if (parameters.length == 3) {
              DecoratedTypeMirror param2Type = (DecoratedTypeMirror) TypeMirrorDecorator.decorate(parameters[2].asType(), this.env);
              messageAndThrowableConstructorFound |= param0Type.isInstanceOf(String.class.getName())
                && param1Type.isInstanceOf(faultInfoType)
                && param2Type.isInstanceOf(Throwable.class);
            }
          }
        }
      }

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

    if (faultInfoProperty != null && explicitFaultBeanType == null) {
      throw new EnunciateException("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, context.getContext()));
  }

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

  /**
   * 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 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 = getParticleName();
    }

    return name;
  }

  private String getParticleName() {
    String name;
    name = getSimpleName().toString();

    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().toString();
    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().toString();
  }

  /**
   * @return Description of the conditions under which this fault will be thrown.
   */
  public String getPartDocs() {
    return getConditions();
  }

  /**
   * @return Description of the conditions under which this fault will be thrown.
   */
  public String getConditions() {
    return this.reference.getDocValue();
  }

  /**
   * 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:
   * 

*

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

*

    *
  1. A getFaultInfo method that returns the bean instance of a class type. *
  2. A constructor taking a message and bean instance. *
  3. A constructor taking a message, a bean instance, and a cause. *
* * @return The explicit fault bean of this web fault, if exists, or null otherwise. */ public ElementDeclaration findExplicitFaultBean() { if (this.explicitFaultBeanType == null || this.explicitFaultBeanType.asElement() == null) { return null; } return this.context.getJaxbContext().findElementDeclaration(this.explicitFaultBeanType.asElement()); } /** * @return {@link 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(), getParticleName()); } } /** * 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() { PackageElement pkg = getPackage(); if ((pkg == null) || ("".equals(pkg.getQualifiedName().toString()))) { throw new EnunciateException(getQualifiedName() + ": a web fault in no package must specify a target namespace."); } String[] tokens = pkg.getQualifiedName().toString().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 getChildElements() { if (!isImplicitSchemaElement()) { return Collections.emptyList(); } Set childElements = new TreeSet(new Comparator() { public int compare(ImplicitChildElement o1, ImplicitChildElement o2) { return o1.getElementName().compareTo(o2.getElementName()); } }); for (PropertyElement 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, context.getJaxbContext())); } 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 getAllFaultProperties(DecoratedTypeElement declaration) { ArrayList properties = new ArrayList(); Set excludedProperties = new TreeSet(); while ((declaration != null) && (!Object.class.getName().equals(declaration.getQualifiedName().toString()))) { for (PropertyElement property : declaration.getProperties()) { if (property.getGetter() != null && property.getAnnotation(XmlTransient.class) == null && !IgnoreUtils.isIgnored(property) && !excludedProperties.contains(property.getPropertyName())) { //only the readable properties that are not marked with @XmlTransient properties.add(property); } else { excludedProperties.add(property.getPropertyName()); } } declaration = (DecoratedTypeElement) ((DeclaredType)declaration.getSuperclass()).asElement(); } return properties; } /** * There's only one part to a web fault. * * @return this. */ public Collection getParts() { return new ArrayList(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 EnunciateJaxwsContext getContext() { return context; } public static class FaultBeanChildElement implements Adaptable, ImplicitChildElement, HasClientConvertibleType { private final EnunciateJaxbContext context; private final PropertyElement property; private final int minOccurs; private final String maxOccurs; private final AdapterType adaperType; private final WebFault webFault; private FaultBeanChildElement(PropertyElement property, WebFault webFault, EnunciateJaxbContext context) { this.context = context; DecoratedTypeMirror propertyType = (DecoratedTypeMirror) property.getPropertyType(); this.adaperType = JAXBUtil.findAdapterType(property.getGetter(), context); 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.getKind() == TypeKind.BYTE) { unbounded = false; } } String maxOccurs = unbounded ? "unbounded" : "1"; this.property = property; this.minOccurs = minOccurs; this.maxOccurs = maxOccurs; this.webFault = webFault; } public PropertyElement 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() { XmlType xmlType = XmlTypeFactory.findSpecifiedType(this, context); if (xmlType == null) { xmlType = XmlTypeFactory.getXmlType(getType(), context); } return xmlType; } 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 = MapType.findMapType(propertyType, context); if (mapType != null) { propertyType = mapType; } return propertyType; } public boolean isAdapted() { return this.adaperType != null; } public AdapterType getAdapterType() { return this.adaperType; } @Override public TypeMirror getClientConvertibleType() { return getType(); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy