com.webcohesion.enunciate.modules.jaxws.model.WebFault Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of enunciate-jaxws Show documentation
Show all versions of enunciate-jaxws Show documentation
Model processing for JAX-WS API.
/**
* 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:
*
*
* - A getFaultInfo method that returns the bean instance of a class type.
*
- A constructor taking a message and bean instance.
*
- 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:
*
*
* - A getFaultInfo method that returns the bean instance of a class type.
*
- A constructor taking a message and bean instance.
*
- 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();
}
}
}