Java tutorial
/* * 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_client; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.codehaus.enunciate.modules.xfire_client.annotations.RequestWrapperAnnotation; import org.codehaus.enunciate.modules.xfire_client.annotations.ResponseWrapperAnnotation; import org.codehaus.xfire.MessageContext; import org.codehaus.xfire.aegis.AegisBindingProvider; import org.codehaus.xfire.aegis.stax.ElementReader; import org.codehaus.xfire.aegis.stax.ElementWriter; import org.codehaus.xfire.aegis.type.Type; import org.codehaus.xfire.annotations.WebParamAnnotation; import org.codehaus.xfire.annotations.soap.SOAPBindingAnnotation; 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.service.OperationInfo; import org.codehaus.xfire.service.Service; import org.codehaus.xfire.util.ClassLoaderUtils; import javax.xml.stream.XMLStreamWriter; 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.util.ArrayList; import java.util.List; /** * The binding for a JAXWS operation. * <p/> * This operation binding can makes the special assumption that its operations conform to one of the following schemes: * <p/> * <ul> * <li>The operation is document/literal BARE. In this case, the parameters are JAXB root elements and are used * as the in and out messages</li> * <li>The operation is WRAPPED. In this case, the operations (including the RPC operations) have request/response * beans as defined by JAXWS. However, the added constraint for the request/response beans is that they must be * {@link org.codehaus.enunciate.modules.xfire_client.GeneratedWrapperBean}s so they can be correctly (de)serialized.</li> * </ul> * * @author Ryan Heaton */ public class EnunciatedClientOperationBinding implements MessageSerializer { private static final Log LOG = LogFactory.getLog(EnunciatedClientOperationBinding.class); private final OperationBeanInfo requestInfo; private final OperationBeanInfo responseInfo; private final ExplicitWebAnnotations annotations; /** * Construct an operation binding for the specified operation info. Annotations are needed to * determine how to read and write the operation's message. * * @param annotations The metadata to use for (de)serializing the message. * @param op The operation. */ public EnunciatedClientOperationBinding(ExplicitWebAnnotations annotations, OperationInfo op) throws XFireFault { this.annotations = annotations; this.requestInfo = getRequestInfo(op); this.responseInfo = getResponseInfo(op); } /** * Construct an operation binding with the specified metadata. * * @param annotations The annotations. * @param requestInfo The request info. * @param responseInfo The response info. */ protected EnunciatedClientOperationBinding(ExplicitWebAnnotations annotations, OperationBeanInfo requestInfo, OperationBeanInfo responseInfo) { this.annotations = annotations; this.requestInfo = requestInfo; this.responseInfo = responseInfo; } /** * Loads the set of input properties for the specified operation. * * @param op The operation. * @return The input properties, or null if the request info wasn't found. */ protected OperationBeanInfo getRequestInfo(OperationInfo op) throws XFireFault { Method method = op.getMethod(); Class ei = method.getDeclaringClass(); Package pckg = ei.getPackage(); if ((this.annotations.hasSOAPBindingAnnotation(method)) && (this.annotations.getSOAPBindingAnnotation(method) .getParameterStyle() == SOAPBindingAnnotation.PARAMETER_STYLE_BARE)) { //bare method... return new OperationBeanInfo(method.getParameterTypes()[0], null); } else { String requestWrapperClassName; RequestWrapperAnnotation requestWrapperInfo = this.annotations.getRequestWrapperAnnotation(method); if ((requestWrapperInfo != null) && (requestWrapperInfo.className() != null) && (requestWrapperInfo.className().length() > 0)) { requestWrapperClassName = requestWrapperInfo.className(); } else { StringBuffer builder = new StringBuffer(pckg == null ? "" : pckg.getName()); if (builder.length() > 0) { builder.append("."); } builder.append("jaxws."); String methodName = method.getName(); builder.append(PropertyUtil.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 send..."); return null; } return new OperationBeanInfo(wrapperClass, loadOrderedProperties(wrapperClass)); } } /** * 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(); if ((this.annotations.hasSOAPBindingAnnotation(method)) && (this.annotations.getSOAPBindingAnnotation(method) .getParameterStyle() == SOAPBindingAnnotation.PARAMETER_STYLE_BARE)) { //bare method... return new OperationBeanInfo(method.getReturnType(), null); } else { String responseWrapperClassName; ResponseWrapperAnnotation responseWrapperInfo = annotations.getResponseWrapperAnnotation(method); if ((responseWrapperInfo != null) && (responseWrapperInfo.className() != null) && (responseWrapperInfo.className().length() > 0)) { responseWrapperClassName = responseWrapperInfo.className(); } else { StringBuffer builder = new StringBuffer(pckg == null ? "" : pckg.getName()); if (builder.length() > 0) { builder.append("."); } builder.append("jaxws."); String methodName = method.getName(); builder.append(PropertyUtil.capitalize(methodName)).append("Response"); responseWrapperClassName = builder.toString(); } Class wrapperClass; try { wrapperClass = ClassLoaderUtils.loadClass(responseWrapperClassName, getClass()); } catch (ClassNotFoundException e) { LOG.debug("Unabled to find response wrapper class " + responseWrapperClassName + "... Operation " + op.getQName() + " will not be able to recieve..."); return null; } return new OperationBeanInfo(wrapperClass, loadOrderedProperties(wrapperClass)); } } /** * 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 { String[] propOrder = annotations.getPropertyOrder(wrapperClass); if (propOrder == null) { throw new XFireFault( "Unable use use " + wrapperClass.getName() + " as a wrapper class: no propOrder specified.", XFireFault.RECEIVER); } BeanInfo responseBeanInfo; try { responseBeanInfo = Introspector.getBeanInfo(wrapperClass, Object.class); } catch (IntrospectionException e) { throw new XFireFault("Unable to introspect " + wrapperClass.getName(), e, XFireFault.RECEIVER); } return PropertyUtil.sortProperties(wrapperClass, responseBeanInfo.getPropertyDescriptors(), propOrder); } public void readMessage(InMessage message, MessageContext context) throws XFireFault { if (this.responseInfo == null) { throw new XFireFault("Message cannot be read: no response info was found.", XFireFault.RECEIVER); } Service service = context.getService(); AegisBindingProvider provider = (AegisBindingProvider) service.getBindingProvider(); Class beanClass = this.responseInfo.getBeanClass(); Type type = provider.getType(service, beanClass); Object bean = type.readObject(new ElementReader(message.getXMLStreamReader()), context); List parameters = new ArrayList(); if (this.responseInfo.isBare()) { //if it's bare, the bean doesn't need to be unwrapped. parameters.add(bean); } else { //The operation is not bare, unwrap the bean. PropertyDescriptor[] pds = this.responseInfo.getPropertyOrder(); for (int i = 0; i < pds.length; i++) { PropertyDescriptor descriptor = pds[i]; try { parameters.add(descriptor.getReadMethod().invoke(bean, null)); } catch (IllegalAccessException e) { throw new XFireFault( "Problem with property " + descriptor.getName() + " on " + beanClass.getName() + ".", e, XFireFault.RECEIVER); } catch (InvocationTargetException e) { throw new XFireFault( "Problem with property " + descriptor.getName() + " on " + beanClass.getName() + ".", e, XFireFault.RECEIVER); } } } message.setBody(parameters); } public void writeMessage(OutMessage message, XMLStreamWriter writer, MessageContext context) throws XFireFault { if (this.requestInfo == null) { throw new XFireFault("Message cannot be sent: no request info was found.", XFireFault.RECEIVER); } Class beanClass = this.requestInfo.getBeanClass(); Object[] params = (Object[]) message.getBody(); //strip out all the header parameters... ArrayList filteredParams = new ArrayList(); if (params != null) { for (int i = 0; i < params.length; i++) { Object param = params[i]; OperationInfo operationInfo = context.getExchange().getOperation(); if (operationInfo != null) { WebParamAnnotation annotation = annotations.getWebParamAnnotation(operationInfo.getMethod(), i); if ((annotation != null) && (annotation.isHeader())) { //skip the headers.... continue; } } filteredParams.add(param); } } params = filteredParams.toArray(); Object bean; if (this.requestInfo.isBare()) { //if the operation is bare, we don't need to wrap it up. bean = params[0]; } else { //the operation is not bare, we need to wrap it up. try { bean = beanClass.newInstance(); } catch (Exception e) { throw new XFireFault("Problem instantiating response wrapper " + beanClass.getName() + ".", e, XFireFault.RECEIVER); } PropertyDescriptor[] properties = this.requestInfo.getPropertyOrder(); if (properties.length > 0) { //no properties implies a no-arg method. if (properties.length != params.length) { throw new XFireFault("There are " + params.length + " parameters to the out message but " + properties.length + " properties on " + beanClass.getName(), XFireFault.RECEIVER); } for (int i = 0; i < properties.length; i++) { PropertyDescriptor descriptor = properties[i]; try { descriptor.getWriteMethod().invoke(bean, new Object[] { params[i] }); } catch (IllegalAccessException e) { throw new XFireFault("Problem with property " + descriptor.getName() + " on " + beanClass.getName() + ".", e, XFireFault.RECEIVER); } catch (InvocationTargetException e) { throw new XFireFault("Problem with property " + descriptor.getName() + " on " + beanClass.getName() + ".", e, XFireFault.RECEIVER); } } } } Service service = context.getService(); AegisBindingProvider provider = (AegisBindingProvider) service.getBindingProvider(); Type type = provider.getType(service, beanClass); ElementWriter elementWriter = newElementWriter(writer); type.writeObject(bean, elementWriter, context); elementWriter.close(); } /** * Create a new element writer. * * @param writer The writer. * @return The element writer. */ protected ElementWriter newElementWriter(XMLStreamWriter writer) { return new ElementWriter(writer); } /** * A simple bean info for an operation bean class. */ public static class OperationBeanInfo { private final Class beanClass; private final PropertyDescriptor[] propertyOrder; public OperationBeanInfo(Class wrapperClass, PropertyDescriptor[] properties) { this.beanClass = wrapperClass; this.propertyOrder = properties; } /** * The bean class. * * @return The bean class. */ public Class getBeanClass() { return beanClass; } /** * Whether the method is bare. * * @return Whether the method is bare. */ public boolean isBare() { return propertyOrder == null; } /** * The ordered list of bean properties. * * @return The ordered list of bean properties, or null if the operation is bare. */ public PropertyDescriptor[] getPropertyOrder() { return propertyOrder; } } }