001/** 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.camel.model.language; 018 019import javax.xml.bind.annotation.XmlAccessType; 020import javax.xml.bind.annotation.XmlAccessorType; 021import javax.xml.bind.annotation.XmlAttribute; 022import javax.xml.bind.annotation.XmlRootElement; 023import javax.xml.bind.annotation.XmlTransient; 024import javax.xml.xpath.XPathFactory; 025 026import org.apache.camel.CamelContext; 027import org.apache.camel.Expression; 028import org.apache.camel.Predicate; 029import org.apache.camel.builder.xml.XPathBuilder; 030import org.apache.camel.spi.Metadata; 031import org.apache.camel.util.ObjectHelper; 032 033/** 034 * For XPath expressions and predicates 035 */ 036@Metadata(label = "language,core,xml", title = "XPath") 037@XmlRootElement(name = "xpath") 038@XmlAccessorType(XmlAccessType.FIELD) 039public class XPathExpression extends NamespaceAwareExpression { 040 @XmlAttribute(name = "documentType") 041 private String documentTypeName; 042 @XmlAttribute(name = "resultType") 043 private String resultTypeName; 044 @XmlAttribute 045 private Boolean saxon; 046 @XmlAttribute 047 private String factoryRef; 048 @XmlAttribute 049 private String objectModel; 050 @XmlAttribute 051 private Boolean logNamespaces; 052 @XmlAttribute 053 private String headerName; 054 @XmlTransient 055 private Class<?> documentType; 056 @XmlTransient 057 private Class<?> resultType; 058 @XmlTransient 059 private XPathFactory xpathFactory; 060 061 public XPathExpression() { 062 } 063 064 public XPathExpression(String expression) { 065 super(expression); 066 } 067 068 public XPathExpression(Expression expression) { 069 setExpressionValue(expression); 070 } 071 072 public String getLanguage() { 073 return "xpath"; 074 } 075 076 public Class<?> getDocumentType() { 077 return documentType; 078 } 079 080 /** 081 * Class for document type to use 082 * <p/> 083 * The default value is org.w3c.dom.Document 084 */ 085 public void setDocumentType(Class<?> documentType) { 086 this.documentType = documentType; 087 } 088 089 public String getDocumentTypeName() { 090 return documentTypeName; 091 } 092 093 /** 094 * Name of class for document type 095 * <p/> 096 * The default value is org.w3c.dom.Document 097 */ 098 public void setDocumentTypeName(String documentTypeName) { 099 this.documentTypeName = documentTypeName; 100 } 101 102 public Class<?> getResultType() { 103 return resultType; 104 } 105 106 /** 107 * Sets the class of the result type (type from output). 108 * <p/> 109 * The default result type is NodeSet 110 */ 111 public void setResultType(Class<?> resultType) { 112 this.resultType = resultType; 113 } 114 115 public String getResultTypeName() { 116 return resultTypeName; 117 } 118 119 /** 120 * Sets the class name of the result type (type from output) 121 * <p/> 122 * The default result type is NodeSet 123 */ 124 public void setResultTypeName(String resultTypeName) { 125 this.resultTypeName = resultTypeName; 126 } 127 128 /** 129 * Whether to use Saxon. 130 */ 131 public void setSaxon(Boolean saxon) { 132 this.saxon = saxon; 133 } 134 135 public Boolean getSaxon() { 136 return saxon; 137 } 138 139 /** 140 * References to a custom XPathFactory to lookup in the registry 141 */ 142 public void setFactoryRef(String factoryRef) { 143 this.factoryRef = factoryRef; 144 } 145 146 public String getFactoryRef() { 147 return factoryRef; 148 } 149 150 /** 151 * The XPath object model to use 152 */ 153 public void setObjectModel(String objectModel) { 154 this.objectModel = objectModel; 155 } 156 157 public String getObjectModel() { 158 return objectModel; 159 } 160 161 /** 162 * Whether to log namespaces which can assist during trouble shooting 163 */ 164 public void setLogNamespaces(Boolean logNamespaces) { 165 this.logNamespaces = logNamespaces; 166 } 167 168 public Boolean getLogNamespaces() { 169 return logNamespaces; 170 } 171 172 public String getHeaderName() { 173 return headerName; 174 } 175 176 /** 177 * Name of header to use as input, instead of the message body 178 */ 179 public void setHeaderName(String headerName) { 180 this.headerName = headerName; 181 } 182 183 @Override 184 public Expression createExpression(CamelContext camelContext) { 185 if (documentType == null && documentTypeName != null) { 186 try { 187 documentType = camelContext.getClassResolver().resolveMandatoryClass(documentTypeName); 188 } catch (ClassNotFoundException e) { 189 throw ObjectHelper.wrapRuntimeCamelException(e); 190 } 191 } 192 if (resultType == null && resultTypeName != null) { 193 try { 194 resultType = camelContext.getClassResolver().resolveMandatoryClass(resultTypeName); 195 } catch (ClassNotFoundException e) { 196 throw ObjectHelper.wrapRuntimeCamelException(e); 197 } 198 } 199 resolveXPathFactory(camelContext); 200 return super.createExpression(camelContext); 201 } 202 203 @Override 204 public Predicate createPredicate(CamelContext camelContext) { 205 if (documentType == null && documentTypeName != null) { 206 try { 207 documentType = camelContext.getClassResolver().resolveMandatoryClass(documentTypeName); 208 } catch (ClassNotFoundException e) { 209 throw ObjectHelper.wrapRuntimeCamelException(e); 210 } 211 } 212 resolveXPathFactory(camelContext); 213 return super.createPredicate(camelContext); 214 } 215 216 @Override 217 protected void configureExpression(CamelContext camelContext, Expression expression) { 218 boolean isSaxon = getSaxon() != null && getSaxon(); 219 boolean isLogNamespaces = getLogNamespaces() != null && getLogNamespaces(); 220 221 if (documentType != null) { 222 setProperty(expression, "documentType", documentType); 223 } 224 if (resultType != null) { 225 setProperty(expression, "resultType", resultType); 226 } 227 if (isSaxon) { 228 ObjectHelper.cast(XPathBuilder.class, expression).enableSaxon(); 229 } 230 if (xpathFactory != null) { 231 setProperty(expression, "xPathFactory", xpathFactory); 232 } 233 if (objectModel != null) { 234 setProperty(expression, "objectModelUri", objectModel); 235 } 236 if (isLogNamespaces) { 237 ObjectHelper.cast(XPathBuilder.class, expression).setLogNamespaces(true); 238 } 239 if (ObjectHelper.isNotEmpty(getHeaderName())) { 240 ObjectHelper.cast(XPathBuilder.class, expression).setHeaderName(getHeaderName()); 241 } 242 // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory 243 super.configureExpression(camelContext, expression); 244 } 245 246 @Override 247 protected void configurePredicate(CamelContext camelContext, Predicate predicate) { 248 boolean isSaxon = getSaxon() != null && getSaxon(); 249 boolean isLogNamespaces = getLogNamespaces() != null && getLogNamespaces(); 250 251 if (documentType != null) { 252 setProperty(predicate, "documentType", documentType); 253 } 254 if (resultType != null) { 255 setProperty(predicate, "resultType", resultType); 256 } 257 if (isSaxon) { 258 ObjectHelper.cast(XPathBuilder.class, predicate).enableSaxon(); 259 } 260 if (xpathFactory != null) { 261 setProperty(predicate, "xPathFactory", xpathFactory); 262 } 263 if (objectModel != null) { 264 setProperty(predicate, "objectModelUri", objectModel); 265 } 266 if (isLogNamespaces) { 267 ObjectHelper.cast(XPathBuilder.class, predicate).setLogNamespaces(true); 268 } 269 if (ObjectHelper.isNotEmpty(getHeaderName())) { 270 ObjectHelper.cast(XPathBuilder.class, predicate).setHeaderName(getHeaderName()); 271 } 272 // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory 273 super.configurePredicate(camelContext, predicate); 274 } 275 276 private void resolveXPathFactory(CamelContext camelContext) { 277 // Factory and Object Model can be set simultaneously. The underlying XPathBuilder allows for setting Saxon too, as it is simply a shortcut for 278 // setting the appropriate Object Model, it is not wise to allow this in XML because the order of invocation of the setters by JAXB may cause undeterministic behaviour 279 if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) { 280 throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this); 281 } 282 283 // Validate the factory class 284 if (ObjectHelper.isNotEmpty(factoryRef)) { 285 xpathFactory = camelContext.getRegistry().lookupByNameAndType(factoryRef, XPathFactory.class); 286 if (xpathFactory == null) { 287 throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance"); 288 } 289 } 290 } 291}