View Javadoc

1   /*
2    * Copyright 2005-2010 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.springframework.ws.server.endpoint.adapter;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Method;
21  import java.util.Map;
22  import javax.xml.namespace.QName;
23  import javax.xml.transform.Source;
24  import javax.xml.transform.TransformerException;
25  import javax.xml.transform.dom.DOMResult;
26  import javax.xml.xpath.XPath;
27  import javax.xml.xpath.XPathConstants;
28  import javax.xml.xpath.XPathExpressionException;
29  import javax.xml.xpath.XPathFactory;
30  
31  import org.springframework.beans.factory.InitializingBean;
32  import org.springframework.ws.WebServiceMessage;
33  import org.springframework.ws.context.MessageContext;
34  import org.springframework.ws.server.endpoint.MethodEndpoint;
35  import org.springframework.ws.server.endpoint.annotation.XPathParam;
36  import org.springframework.xml.namespace.SimpleNamespaceContext;
37  
38  import org.w3c.dom.Document;
39  import org.w3c.dom.Element;
40  import org.w3c.dom.Node;
41  import org.w3c.dom.NodeList;
42  
43  /**
44   * Adapter that supports endpoint methods that use XPath expressions. Supports methods with the following signature:
45   * <pre>
46   * void handleMyMessage(@XPathParam("/root/child/text")String param);
47   * </pre>
48   * or
49   * <pre>
50   * Source handleMyMessage(@XPathParam("/root/child/text")String param1, @XPathParam("/root/child/number")double
51   * param2);
52   * </pre>
53   * I.e. methods that return either <code>void</code> or a [email protected] Source}, and have parameters annotated with [email protected]
54   * XPathParam} that specify the XPath expression that should be bound to that parameter. The parameter can be of the
55   * following types: <ul> <li><code>boolean</code>, or [email protected] Boolean}</li> <li><code>double</code>, or [email protected]
56   * Double}</li> <li>[email protected] String}</li> <li>[email protected] Node}</li> <li>[email protected] NodeList}</li> </ul>
57   *
58   * @author Arjen Poutsma
59   * @since 1.0.0
60   * @deprecated as of Spring Web Services 2.0, in favor of [email protected] DefaultMethodEndpointAdapter} and [email protected]
61   *             org.springframework.ws.server.endpoint.adapter.method.XPathParamMethodArgumentResolver
62   *             XPathParamMethodArgumentResolver}.
63   */
64  @Deprecated
65  public class XPathParamAnnotationMethodEndpointAdapter extends AbstractMethodEndpointAdapter
66          implements InitializingBean {
67  
68      private XPathFactory xpathFactory;
69  
70      private Map<String, String> namespaces;
71  
72      /** Sets namespaces used in the XPath expression. Maps prefixes to namespaces. */
73      public void setNamespaces(Map<String, String> namespaces) {
74          this.namespaces = namespaces;
75      }
76  
77      public void afterPropertiesSet() throws Exception {
78          xpathFactory = XPathFactory.newInstance();
79      }
80  
81      /** Supports methods with @XPathParam parameters, and return either <code>Source</code> or nothing. */
82      @Override
83      protected boolean supportsInternal(MethodEndpoint methodEndpoint) {
84          Method method = methodEndpoint.getMethod();
85          if (!(Source.class.isAssignableFrom(method.getReturnType()) || Void.TYPE.equals(method.getReturnType()))) {
86              return false;
87          }
88          Class<?>[] parameterTypes = method.getParameterTypes();
89          for (int i = 0; i < parameterTypes.length; i++) {
90              if (getXPathParamAnnotation(method, i) == null || !isSupportedType(parameterTypes[i])) {
91                  return false;
92              }
93          }
94          return true;
95      }
96  
97      private XPathParam getXPathParamAnnotation(Method method, int paramIdx) {
98          Annotation[][] paramAnnotations = method.getParameterAnnotations();
99          for (int annIdx = 0; annIdx < paramAnnotations[paramIdx].length; annIdx++) {
100             if (paramAnnotations[paramIdx][annIdx].annotationType().equals(XPathParam.class)) {
101                 return (XPathParam) paramAnnotations[paramIdx][annIdx];
102             }
103         }
104         return null;
105     }
106 
107     private boolean isSupportedType(Class<?> clazz) {
108         return Boolean.class.isAssignableFrom(clazz) || Boolean.TYPE.isAssignableFrom(clazz) ||
109                 Double.class.isAssignableFrom(clazz) || Double.TYPE.isAssignableFrom(clazz) ||
110                 Node.class.isAssignableFrom(clazz) || NodeList.class.isAssignableFrom(clazz) ||
111                 String.class.isAssignableFrom(clazz);
112     }
113 
114     @Override
115     protected void invokeInternal(MessageContext messageContext, MethodEndpoint methodEndpoint) throws Exception {
116         Element payloadElement = getRootElement(messageContext.getRequest().getPayloadSource());
117         Object[] args = getMethodArguments(payloadElement, methodEndpoint.getMethod());
118         Object result = methodEndpoint.invoke(args);
119         if (result != null && result instanceof Source) {
120             Source responseSource = (Source) result;
121             WebServiceMessage response = messageContext.getResponse();
122             transform(responseSource, response.getPayloadResult());
123         }
124     }
125 
126     private Object[] getMethodArguments(Element payloadElement, Method method) throws XPathExpressionException {
127         Class<?>[] parameterTypes = method.getParameterTypes();
128         XPath xpath = createXPath();
129         Object[] args = new Object[parameterTypes.length];
130         for (int i = 0; i < parameterTypes.length; i++) {
131             String expression = getXPathParamAnnotation(method, i).value();
132             QName conversionType;
133             if (Boolean.class.isAssignableFrom(parameterTypes[i]) || Boolean.TYPE.isAssignableFrom(parameterTypes[i])) {
134                 conversionType = XPathConstants.BOOLEAN;
135             }
136             else
137             if (Double.class.isAssignableFrom(parameterTypes[i]) || Double.TYPE.isAssignableFrom(parameterTypes[i])) {
138                 conversionType = XPathConstants.NUMBER;
139             }
140             else if (Node.class.isAssignableFrom(parameterTypes[i])) {
141                 conversionType = XPathConstants.NODE;
142             }
143             else if (NodeList.class.isAssignableFrom(parameterTypes[i])) {
144                 conversionType = XPathConstants.NODESET;
145             }
146             else if (String.class.isAssignableFrom(parameterTypes[i])) {
147                 conversionType = XPathConstants.STRING;
148             }
149             else {
150                 throw new IllegalArgumentException("Invalid parameter type [" + parameterTypes[i] + "]. " +
151                         "Supported are: Boolean, Double, Node, NodeList, and String.");
152             }
153             args[i] = xpath.evaluate(expression, payloadElement, conversionType);
154         }
155         return args;
156     }
157 
158     private synchronized XPath createXPath() {
159         XPath xpath = xpathFactory.newXPath();
160         if (namespaces != null) {
161             SimpleNamespaceContext namespaceContext = new SimpleNamespaceContext();
162             namespaceContext.setBindings(namespaces);
163             xpath.setNamespaceContext(namespaceContext);
164         }
165         return xpath;
166     }
167 
168     /**
169      * Returns the root element of the given source.
170      *
171      * @param source the source to get the root element from
172      * @return the root element
173      */
174     private Element getRootElement(Source source) throws TransformerException {
175         DOMResult domResult = new DOMResult();
176         transform(source, domResult);
177         Document document = (Document) domResult.getNode();
178         return document.getDocumentElement();
179     }
180 
181 
182 }