
org.codehaus.enunciate.modules.xfire.EnunciatedJAXWSOperationBinding 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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.xfire.MessageContext;
import org.codehaus.xfire.XFireRuntimeException;
import org.codehaus.xfire.exchange.InMessage;
import org.codehaus.xfire.exchange.MessageSerializer;
import org.codehaus.xfire.exchange.OutMessage;
import org.codehaus.xfire.fault.XFireFault;
import org.codehaus.xfire.jaxb2.AttachmentMarshaller;
import org.codehaus.xfire.jaxb2.AttachmentUnmarshaller;
import org.codehaus.xfire.service.OperationInfo;
import org.codehaus.xfire.service.MessageInfo;
import org.codehaus.xfire.util.ClassLoaderUtils;
import org.xml.sax.SAXException;
import javax.jws.soap.SOAPBinding;
import javax.jws.WebParam;
import javax.xml.bind.*;
import javax.xml.bind.helpers.DefaultValidationEventHandler;
import javax.xml.bind.annotation.XmlType;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.XMLInputFactory;
import javax.xml.ws.RequestWrapper;
import javax.xml.ws.ResponseWrapper;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.dom.DOMResult;
import javax.xml.transform.TransformerFactory;
import javax.xml.parsers.DocumentBuilderFactory;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.io.IOException;
/**
* The binding for a JAXWS operation.
*
* @author Ryan Heaton
*/
public class EnunciatedJAXWSOperationBinding implements MessageSerializer {
private static final Log LOG = LogFactory.getLog(EnunciatedJAXWSOperationBinding.class);
private final JAXBContext jaxbContext;
private final OperationBeanInfo requestInfo;
private final OperationBeanInfo responseInfo;
private ValidationEventHandler validationEventHandler = new DefaultValidationEventHandler();
public EnunciatedJAXWSOperationBinding(OperationInfo op) throws XFireFault {
this.requestInfo = getRequestInfo(op);
this.responseInfo = getResponseInfo(op);
ArrayList contextClasses = new ArrayList(2);
if (this.requestInfo != null) {
contextClasses.add(this.requestInfo.getBeanClass());
}
if (this.responseInfo != null) {
contextClasses.add(this.responseInfo.getBeanClass());
}
try {
this.jaxbContext = JAXBContext.newInstance(contextClasses.toArray(new Class[contextClasses.size()]));
}
catch (JAXBException e) {
throw new XFireFault("Unable to create a binding for " + op.getMethod() + ".", e, XFireFault.RECEIVER);
}
}
/**
* Loads the set of input properties for the specified operation.
*
* @param op The operation.
* @return The input properties, or null if none were found.
*/
protected OperationBeanInfo getRequestInfo(OperationInfo op) throws XFireFault {
Method method = op.getMethod();
Class ei = method.getDeclaringClass();
Package pckg = ei.getPackage();
SOAPBinding.ParameterStyle paramStyle = SOAPBinding.ParameterStyle.WRAPPED;
if (method.isAnnotationPresent(SOAPBinding.class)) {
SOAPBinding annotation = method.getAnnotation(SOAPBinding.class);
paramStyle = annotation.parameterStyle();
}
else if (ei.isAnnotationPresent(SOAPBinding.class)) {
SOAPBinding annotation = ((SOAPBinding) ei.getAnnotation(SOAPBinding.class));
paramStyle = annotation.parameterStyle();
}
boolean schemaValidate = method.isAnnotationPresent(SchemaValidate.class) || ei.isAnnotationPresent(SchemaValidate.class) || pckg.isAnnotationPresent(SchemaValidate.class);
if (paramStyle == SOAPBinding.ParameterStyle.BARE) {
//return a bare operation info.
//it's not necessarily the first parameter type! there could be a header or OUT parameter...
int paramIndex;
WebParam annotation = null;
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
if (parameterAnnotations.length == 0) {
throw new IllegalStateException("A BARE web service must have input parameters.");
}
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) {
paramIndex = 0;
}
return new OperationBeanInfo(method.getParameterTypes()[paramIndex], null, op.getInputMessage(), schemaValidate);
}
else {
String requestWrapperClassName;
RequestWrapper requestWrapperInfo = method.getAnnotation(RequestWrapper.class);
if ((requestWrapperInfo != null) && (requestWrapperInfo.className() != null) && (requestWrapperInfo.className().length() > 0)) {
requestWrapperClassName = requestWrapperInfo.className();
}
else {
StringBuilder builder = new StringBuilder(pckg == null ? "" : pckg.getName());
if (builder.length() > 0) {
builder.append(".");
}
builder.append("jaxws.");
String methodName = method.getName();
builder.append(capitalize(methodName));
requestWrapperClassName = builder.toString();
}
Class wrapperClass;
try {
wrapperClass = ClassLoaderUtils.loadClass(requestWrapperClassName, getClass());
}
catch (ClassNotFoundException e) {
LOG.error("Unabled to find request wrapper class " + requestWrapperClassName + "... Operation " + op.getQName() + " will not be able to recieve...");
return null;
}
return new OperationBeanInfo(wrapperClass, loadOrderedProperties(wrapperClass), op.getInputMessage(), schemaValidate);
}
}
/**
* Loads the set of output properties for the specified operation.
*
* @param op The operation.
* @return The output properties.
*/
protected OperationBeanInfo getResponseInfo(OperationInfo op) throws XFireFault {
Method method = op.getMethod();
Class ei = method.getDeclaringClass();
Package pckg = ei.getPackage();
SOAPBinding.ParameterStyle paramStyle = SOAPBinding.ParameterStyle.WRAPPED;
if (method.isAnnotationPresent(SOAPBinding.class)) {
SOAPBinding annotation = method.getAnnotation(SOAPBinding.class);
paramStyle = annotation.parameterStyle();
}
else if (ei.isAnnotationPresent(SOAPBinding.class)) {
SOAPBinding annotation = ((SOAPBinding) ei.getAnnotation(SOAPBinding.class));
paramStyle = annotation.parameterStyle();
}
if (paramStyle == SOAPBinding.ParameterStyle.BARE) {
//bare return type.
//todo: it's not necessarily the return type! it could be an OUT parameter...
return new OperationBeanInfo(method.getReturnType(), null, op.getOutputMessage());
}
else {
String responseWrapperClassName;
ResponseWrapper responseWrapperInfo = method.getAnnotation(ResponseWrapper.class);
if ((responseWrapperInfo != null) && (responseWrapperInfo.className() != null) && (responseWrapperInfo.className().length() > 0)) {
responseWrapperClassName = responseWrapperInfo.className();
}
else {
StringBuilder builder = new StringBuilder(pckg == null ? "" : pckg.getName());
if (builder.length() > 0) {
builder.append(".");
}
builder.append("jaxws.");
String methodName = method.getName();
builder.append(capitalize(methodName)).append("Response");
responseWrapperClassName = builder.toString();
}
Class wrapperClass;
try {
wrapperClass = ClassLoaderUtils.loadClass(responseWrapperClassName, getClass());
}
catch (ClassNotFoundException e) {
LOG.debug("Unabled to find request wrapper class " + responseWrapperClassName + "... Operation " + op.getQName() + " will not be able to send...");
return null;
}
return new OperationBeanInfo(wrapperClass, loadOrderedProperties(wrapperClass), op.getOutputMessage());
}
}
/**
* Loads the property descriptors for the ordered properties of the specified class.
*
* @param wrapperClass The wrapper class.
* @return The ordered property descriptors.
*/
protected PropertyDescriptor[] loadOrderedProperties(Class wrapperClass) throws XFireFault {
XmlType typeInfo = (XmlType) wrapperClass.getAnnotation(XmlType.class);
if ((typeInfo == null) || (typeInfo.propOrder() == null) || ((typeInfo.propOrder().length == 1) && "".equals(typeInfo.propOrder()[0]))) {
throw new XFireFault("Unable use use " + wrapperClass.getName() + " as a wrapper class: no propOrder specified.", XFireFault.RECEIVER);
}
String[] propOrder = typeInfo.propOrder();
BeanInfo beanInfo;
try {
beanInfo = Introspector.getBeanInfo(wrapperClass, Object.class);
}
catch (IntrospectionException e) {
throw new XFireFault("Unable to introspect " + wrapperClass.getName(), e, XFireFault.RECEIVER);
}
PropertyDescriptor[] pds = beanInfo.getPropertyDescriptors();
PropertyDescriptor[] props = new PropertyDescriptor[propOrder.length];
RESPONSE_PROPERTY_LOOP :
for (int i = 0; i < propOrder.length; i++) {
String property = propOrder[i];
if ((property.length() > 1) && (!Character.isLowerCase(property.charAt(1)))) {
//if the second letter is uppercase, javabean spec says the first character of the property is also to be kept uppercase.
property = capitalize(property);
}
for (PropertyDescriptor descriptor : pds) {
if (descriptor.getName().equals(property)) {
props[i] = descriptor;
continue RESPONSE_PROPERTY_LOOP;
}
}
throw new XFireFault("Unknown property " + property + " on wrapper " + wrapperClass.getName(), XFireFault.RECEIVER);
}
return props;
}
public void readMessage(InMessage message, MessageContext context) throws XFireFault {
if (this.requestInfo == null) {
throw new XFireFault("Unable to read message: no request info was found!", XFireFault.RECEIVER);
}
Object bean;
try {
Unmarshaller unmarshaller = this.jaxbContext.createUnmarshaller();
XMLStreamReader streamReader = message.getXMLStreamReader();
if (this.requestInfo.isSchemaValidate()) {
try {
TransformerFactory xformFactory = TransformerFactory.newInstance();
DOMResult domResult = new DOMResult();
xformFactory.newTransformer().transform(new StAXSource(streamReader, true), domResult);
unmarshaller.getSchema().newValidator().validate(new DOMSource(domResult.getNode()));
streamReader = XMLInputFactory.newInstance().createXMLStreamReader(new DOMSource(domResult.getNode()));
}
catch (Exception e) {
throw new XFireRuntimeException("Unable to validate the request against the schema.");
}
}
unmarshaller.setEventHandler(getValidationEventHandler());
unmarshaller.setAttachmentUnmarshaller(new AttachmentUnmarshaller(context));
bean = unmarshaller.unmarshal(streamReader, this.requestInfo.getBeanClass()).getValue();
}
catch (JAXBException e) {
throw new XFireRuntimeException("Unable to unmarshal type.", e);
}
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy