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", 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 resolveXPathFactory(camelContext); 206 return super.createPredicate(camelContext); 207 } 208 209 @Override 210 protected void configureExpression(CamelContext camelContext, Expression expression) { 211 boolean isSaxon = getSaxon() != null && getSaxon(); 212 boolean isLogNamespaces = getLogNamespaces() != null && getLogNamespaces(); 213 214 if (documentType != null) { 215 setProperty(expression, "documentType", documentType); 216 } 217 if (resultType != null) { 218 setProperty(expression, "resultType", resultType); 219 } 220 if (isSaxon) { 221 ObjectHelper.cast(XPathBuilder.class, expression).enableSaxon(); 222 } 223 if (xpathFactory != null) { 224 setProperty(expression, "xPathFactory", xpathFactory); 225 } 226 if (objectModel != null) { 227 setProperty(expression, "objectModelUri", objectModel); 228 } 229 if (isLogNamespaces) { 230 ObjectHelper.cast(XPathBuilder.class, expression).setLogNamespaces(true); 231 } 232 if (ObjectHelper.isNotEmpty(getHeaderName())) { 233 ObjectHelper.cast(XPathBuilder.class, expression).setHeaderName(getHeaderName()); 234 } 235 // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory 236 super.configureExpression(camelContext, expression); 237 238 } 239 240 @Override 241 protected void configurePredicate(CamelContext camelContext, Predicate predicate) { 242 boolean isSaxon = getSaxon() != null && getSaxon(); 243 boolean isLogNamespaces = getLogNamespaces() != null && getLogNamespaces(); 244 245 if (documentType != null) { 246 setProperty(predicate, "documentType", documentType); 247 } 248 if (resultType != null) { 249 setProperty(predicate, "resultType", resultType); 250 } 251 if (isSaxon) { 252 ObjectHelper.cast(XPathBuilder.class, predicate).enableSaxon(); 253 } 254 if (xpathFactory != null) { 255 setProperty(predicate, "xPathFactory", xpathFactory); 256 } 257 if (objectModel != null) { 258 setProperty(predicate, "objectModelUri", objectModel); 259 } 260 if (isLogNamespaces) { 261 ObjectHelper.cast(XPathBuilder.class, predicate).setLogNamespaces(true); 262 } 263 if (ObjectHelper.isNotEmpty(getHeaderName())) { 264 ObjectHelper.cast(XPathBuilder.class, predicate).setHeaderName(getHeaderName()); 265 } 266 // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory 267 super.configurePredicate(camelContext, predicate); 268 } 269 270 private void resolveXPathFactory(CamelContext camelContext) { 271 // Factory and Object Model can be set simultaneously. The underlying XPathBuilder allows for setting Saxon too, as it is simply a shortcut for 272 // 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 273 if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) { 274 throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this); 275 } 276 277 // Validate the factory class 278 if (ObjectHelper.isNotEmpty(factoryRef)) { 279 xpathFactory = camelContext.getRegistry().lookupByNameAndType(factoryRef, XPathFactory.class); 280 if (xpathFactory == null) { 281 throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance"); 282 } 283 } 284 } 285}