001 /** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one or more 004 * contributor license agreements. See the NOTICE file distributed with 005 * this work for additional information regarding copyright ownership. 006 * The ASF licenses this file to You under the Apache License, Version 2.0 007 * (the "License"); you may not use this file except in compliance with 008 * the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 package org.apache.camel.builder.xpath; 019 020 import static org.apache.camel.converter.ObjectConverter.toBoolean; 021 import org.apache.camel.Exchange; 022 import org.apache.camel.Expression; 023 import org.apache.camel.Predicate; 024 import org.apache.camel.RuntimeExpressionException; 025 import org.apache.camel.Message; 026 import org.apache.camel.converter.ObjectConverter; 027 import org.w3c.dom.Document; 028 import org.xml.sax.InputSource; 029 030 import javax.xml.namespace.QName; 031 import javax.xml.xpath.XPath; 032 import javax.xml.xpath.XPathConstants; 033 import javax.xml.xpath.XPathExpression; 034 import javax.xml.xpath.XPathExpressionException; 035 import javax.xml.xpath.XPathFactory; 036 import javax.xml.xpath.XPathFactoryConfigurationException; 037 import javax.xml.xpath.XPathFunctionResolver; 038 import java.io.StringReader; 039 040 /** 041 * Creates an XPath expression builder 042 * 043 * @version $Revision: 531854 $ 044 */ 045 public class XPathBuilder<E extends Exchange> implements Expression<E>, Predicate<E> { 046 private final String text; 047 private XPathFactory xpathFactory; 048 private Class documentType = Document.class; 049 private QName resultType = null; 050 private String objectModelUri = null; 051 private DefaultNamespaceContext namespaceContext; 052 private XPathFunctionResolver functionResolver; 053 private XPathExpression expression; 054 private MessageVariableResolver variableResolver = new MessageVariableResolver(); 055 056 public static XPathBuilder xpath(String text) { 057 return new XPathBuilder(text); 058 } 059 060 public XPathBuilder(String text) { 061 this.text = text; 062 } 063 064 @Override 065 public String toString() { 066 return "XPath: " + text; 067 } 068 069 public boolean matches(E exchange) { 070 Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN); 071 return toBoolean(booleanResult); 072 } 073 074 public void assertMatches(String text, E exchange) throws AssertionError { 075 Object booleanResult = evaluateAs(exchange, XPathConstants.BOOLEAN); 076 if (!toBoolean(booleanResult)) { 077 throw new AssertionError(this + " failed on " + exchange + " as returned <" + booleanResult + ">"); 078 } 079 } 080 081 public Object evaluate(E exchange) { 082 return evaluateAs(exchange, resultType); 083 } 084 085 086 // Builder methods 087 //------------------------------------------------------------------------- 088 089 /** 090 * Sets the expression result type to boolean 091 * 092 * @return the current builder 093 */ 094 public XPathBuilder<E> booleanResult() { 095 resultType = XPathConstants.BOOLEAN; 096 return this; 097 } 098 099 /** 100 * Sets the expression result type to boolean 101 * 102 * @return the current builder 103 */ 104 public XPathBuilder<E> nodeResult() { 105 resultType = XPathConstants.NODE; 106 return this; 107 } 108 109 /** 110 * Sets the expression result type to boolean 111 * 112 * @return the current builder 113 */ 114 public XPathBuilder<E> nodeSetResult() { 115 resultType = XPathConstants.NODESET; 116 return this; 117 } 118 119 /** 120 * Sets the expression result type to boolean 121 * 122 * @return the current builder 123 */ 124 public XPathBuilder<E> numberResult() { 125 resultType = XPathConstants.NUMBER; 126 return this; 127 } 128 129 /** 130 * Sets the expression result type to boolean 131 * 132 * @return the current builder 133 */ 134 public XPathBuilder<E> stringResult() { 135 resultType = XPathConstants.STRING; 136 return this; 137 } 138 139 /** 140 * Sets the object model URI to use 141 * 142 * @return the current builder 143 */ 144 public XPathBuilder<E> objectModel(String uri) { 145 this.objectModelUri = uri; 146 return this; 147 } 148 149 /** 150 * Sets the {@link XPathFunctionResolver} instance to use on these XPath expressions 151 * 152 * @return the current builder 153 */ 154 public XPathBuilder<E> functionResolver(XPathFunctionResolver functionResolver) { 155 this.functionResolver = functionResolver; 156 return this; 157 } 158 159 /** 160 * Registers the namespace prefix and URI with the builder so that the prefix can be used in XPath expressions 161 * 162 * @param prefix is the namespace prefix that can be used in the XPath expressions 163 * @param uri is the namespace URI to which the prefix refers 164 * @return the current builder 165 */ 166 public XPathBuilder<E> namespace(String prefix, String uri) { 167 getNamespaceContext().add(prefix, uri); 168 return this; 169 } 170 171 /** 172 * Registers a variable (in the global namespace) which can be referred to from XPath expressions 173 */ 174 public XPathBuilder<E> variable(String name, Object value) { 175 variableResolver.addVariable(name, value); 176 return this; 177 } 178 179 180 // Properties 181 //------------------------------------------------------------------------- 182 public XPathFactory getXPathFactory() throws XPathFactoryConfigurationException { 183 if (xpathFactory == null) { 184 if (objectModelUri != null) { 185 xpathFactory = XPathFactory.newInstance(objectModelUri); 186 } 187 xpathFactory = XPathFactory.newInstance(); 188 } 189 return xpathFactory; 190 } 191 192 public void setXPathFactory(XPathFactory xpathFactory) { 193 this.xpathFactory = xpathFactory; 194 } 195 196 public Class getDocumentType() { 197 return documentType; 198 } 199 200 public void setDocumentType(Class documentType) { 201 this.documentType = documentType; 202 } 203 204 public String getText() { 205 return text; 206 } 207 208 public QName getResultType() { 209 return resultType; 210 } 211 212 public DefaultNamespaceContext getNamespaceContext() { 213 if (namespaceContext == null) { 214 try { 215 namespaceContext = new DefaultNamespaceContext(getXPathFactory()); 216 } 217 catch (XPathFactoryConfigurationException e) { 218 throw new RuntimeExpressionException(e); 219 } 220 } 221 return namespaceContext; 222 } 223 224 public void setNamespaceContext(DefaultNamespaceContext namespaceContext) { 225 this.namespaceContext = namespaceContext; 226 } 227 228 public XPathFunctionResolver getFunctionResolver() { 229 return functionResolver; 230 } 231 232 public void setFunctionResolver(XPathFunctionResolver functionResolver) { 233 this.functionResolver = functionResolver; 234 } 235 236 public XPathExpression getExpression() throws XPathFactoryConfigurationException, XPathExpressionException { 237 if (expression == null) { 238 expression = createXPathExpression(); 239 } 240 return expression; 241 } 242 243 // Implementation methods 244 //------------------------------------------------------------------------- 245 246 247 /** 248 * Evaluates the expression as the given result type 249 */ 250 protected synchronized Object evaluateAs(E exchange, QName resultType) { 251 variableResolver.setExchange(exchange); 252 try { 253 Object document = getDocument(exchange); 254 if (resultType != null) { 255 if (document instanceof InputSource) { 256 InputSource inputSource = (InputSource) document; 257 return getExpression().evaluate(inputSource, resultType); 258 } 259 else { 260 return getExpression().evaluate(document, resultType); 261 } 262 } 263 else { 264 if (document instanceof InputSource) { 265 InputSource inputSource = (InputSource) document; 266 return getExpression().evaluate(inputSource); 267 } 268 else { 269 return getExpression().evaluate(document); 270 } 271 } 272 } 273 catch (XPathExpressionException e) { 274 throw new InvalidXPathExpression(getText(), e); 275 } 276 catch (XPathFactoryConfigurationException e) { 277 throw new InvalidXPathExpression(getText(), e); 278 } 279 } 280 281 protected XPathExpression createXPathExpression() throws XPathExpressionException, XPathFactoryConfigurationException { 282 XPath xPath = getXPathFactory().newXPath(); 283 284 // lets now clear any factory references to avoid keeping them around 285 xpathFactory = null; 286 287 xPath.setNamespaceContext(getNamespaceContext()); 288 xPath.setXPathVariableResolver(variableResolver); 289 if (functionResolver != null) { 290 xPath.setXPathFunctionResolver(functionResolver); 291 } 292 return xPath.compile(text); 293 } 294 295 /** 296 * Strategy method to extract the document from the exchange 297 */ 298 protected Object getDocument(E exchange) { 299 Message in = exchange.getIn(); 300 Class type = getDocumentType(); 301 Object answer = null; 302 if (type != null) { 303 answer = in.getBody(type); 304 } 305 if (answer == null) { 306 answer = in.getBody(); 307 } 308 309 // lets try coerce some common types into something JAXP can deal with 310 if (answer instanceof String) { 311 answer = new InputSource(new StringReader(answer.toString())); 312 } 313 return answer; 314 } 315 316 317 }