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.method;
18  
19  import javax.xml.namespace.QName;
20  import javax.xml.transform.Source;
21  import javax.xml.transform.TransformerException;
22  import javax.xml.transform.dom.DOMResult;
23  import javax.xml.xpath.XPath;
24  import javax.xml.xpath.XPathConstants;
25  import javax.xml.xpath.XPathExpressionException;
26  import javax.xml.xpath.XPathFactory;
27  
28  import org.springframework.core.MethodParameter;
29  import org.springframework.core.convert.ConversionService;
30  import org.springframework.core.convert.support.ConversionServiceFactory;
31  import org.springframework.ws.context.MessageContext;
32  import org.springframework.ws.server.endpoint.annotation.XPathParam;
33  import org.springframework.ws.server.endpoint.support.NamespaceUtils;
34  import org.springframework.xml.transform.TransformerHelper;
35  
36  import org.w3c.dom.Document;
37  import org.w3c.dom.Element;
38  import org.w3c.dom.Node;
39  import org.w3c.dom.NodeList;
40  
41  /**
42   * Implementation of {@link MethodArgumentResolver} that supports the {@link XPathParam @XPathParam} annotation.
43   * <p/>
44   * This resolver supports parameters annotated with {@link XPathParam @XPathParam} that specifies the XPath expression
45   * that should be bound to that parameter. The parameter can either a "natively supported" XPath type ({@link Boolean
46   * boolean}, {@link Double double}, {@link String}, {@link Node}, or {@link NodeList}), or a type that is {@linkplain
47   * ConversionService#canConvert(Class, Class) supported} by the {@link ConversionService}.
48   *
49   * @author Arjen Poutsma
50   * @since 2.0
51   */
52  public class XPathParamMethodArgumentResolver implements MethodArgumentResolver {
53  
54      private final XPathFactory xpathFactory = createXPathFactory();
55  
56      private TransformerHelper transformerHelper = new TransformerHelper();
57  
58      private ConversionService conversionService = ConversionServiceFactory.createDefaultConversionService();
59  
60      /**
61       * Sets the conversion service to use.
62       * <p/>
63       * Defaults to the {@linkplain ConversionServiceFactory#createDefaultConversionService() default conversion
64       * service}.
65       */
66      public void setConversionService(ConversionService conversionService) {
67          this.conversionService = conversionService;
68      }
69  
70      public void setTransformerHelper(TransformerHelper transformerHelper) {
71          this.transformerHelper = transformerHelper;
72      }
73  
74      public boolean supportsParameter(MethodParameter parameter) {
75          if (parameter.getParameterAnnotation(XPathParam.class) == null) {
76              return false;
77          }
78          Class<?> parameterType = parameter.getParameterType();
79          if (Boolean.class.equals(parameterType) || Boolean.TYPE.equals(parameterType) ||
80                  Double.class.equals(parameterType) || Double.TYPE.equals(parameterType) ||
81                  Node.class.isAssignableFrom(parameterType) || NodeList.class.isAssignableFrom(parameterType) ||
82                  String.class.isAssignableFrom(parameterType)) {
83              return true;
84          }
85          else {
86              return conversionService.canConvert(String.class, parameterType);
87          }
88      }
89  
90      public Object resolveArgument(MessageContext messageContext, MethodParameter parameter)
91              throws TransformerException, XPathExpressionException {
92          Class<?> parameterType = parameter.getParameterType();
93          QName evaluationReturnType = getReturnType(parameterType);
94          boolean useConversionService = false;
95          if (evaluationReturnType == null) {
96              evaluationReturnType = XPathConstants.STRING;
97              useConversionService = true;
98          }
99  
100         XPath xpath = createXPath();
101         xpath.setNamespaceContext(NamespaceUtils.getNamespaceContext(parameter.getMethod()));
102 
103         Element rootElement = getRootElement(messageContext.getRequest().getPayloadSource());
104         String expression = parameter.getParameterAnnotation(XPathParam.class).value();
105         Object result = xpath.evaluate(expression, rootElement, evaluationReturnType);
106         return useConversionService ? conversionService.convert(result, parameterType) : result;
107     }
108 
109     private QName getReturnType(Class<?> parameterType) {
110         if (Boolean.class.equals(parameterType) || Boolean.TYPE.equals(parameterType)) {
111             return XPathConstants.BOOLEAN;
112         }
113         else if (Double.class.equals(parameterType) || Double.TYPE.equals(parameterType)) {
114             return XPathConstants.NUMBER;
115         }
116         else if (Node.class.equals(parameterType)) {
117             return XPathConstants.NODE;
118         }
119         else if (NodeList.class.equals(parameterType)) {
120             return XPathConstants.NODESET;
121         }
122         else if (String.class.equals(parameterType)) {
123             return XPathConstants.STRING;
124         }
125         else {
126             return null;
127         }
128     }
129 
130     private XPath createXPath() {
131         synchronized (xpathFactory) {
132             return xpathFactory.newXPath();
133         }
134     }
135 
136     private Element getRootElement(Source source) throws TransformerException {
137         DOMResult domResult = new DOMResult();
138         transformerHelper.transform(source, domResult);
139         Document document = (Document) domResult.getNode();
140         return document.getDocumentElement();
141     }
142 
143     /**
144      * Create a {@code XPathFactory} that this resolver will use to create {@link XPath} objects.
145      * <p/>
146      * Can be overridden in subclasses, adding further initialization of the factory. The resulting factory is cached,
147      * so this method will only be called once.
148      *
149      * @return the created factory
150      */
151     protected XPathFactory createXPathFactory() {
152         return XPathFactory.newInstance();
153     }
154 
155 
156 }