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 java.util.HashMap; 020import java.util.Map; 021 022import javax.xml.bind.annotation.XmlAccessType; 023import javax.xml.bind.annotation.XmlAccessorType; 024import javax.xml.bind.annotation.XmlAttribute; 025import javax.xml.bind.annotation.XmlRootElement; 026import javax.xml.bind.annotation.XmlTransient; 027import javax.xml.xpath.XPathFactory; 028 029import org.apache.camel.CamelContext; 030import org.apache.camel.Expression; 031import org.apache.camel.Predicate; 032import org.apache.camel.RuntimeCamelException; 033import org.apache.camel.spi.Metadata; 034import org.apache.camel.util.ObjectHelper; 035 036/** 037 * To use XPath (XML) in Camel expressions or predicates. 038 */ 039@Metadata(firstVersion = "1.1.0", label = "language,core,xml", title = "XPath") 040@XmlRootElement(name = "xpath") 041@XmlAccessorType(XmlAccessType.FIELD) 042public class XPathExpression extends NamespaceAwareExpression { 043 044 @XmlAttribute(name = "documentType") 045 private String documentTypeName; 046 @XmlAttribute(name = "resultType") 047 @Metadata(defaultValue = "NODESET", enums = "NUMBER,STRING,BOOLEAN,NODESET,NODE") 048 private String resultTypeName; 049 @XmlAttribute 050 @Metadata(javaType = "java.lang.Boolean") 051 private String saxon; 052 @XmlAttribute 053 @Metadata(label = "advanced") 054 private String factoryRef; 055 @XmlAttribute 056 @Metadata(label = "advanced") 057 private String objectModel; 058 @XmlAttribute 059 @Metadata(javaType = "java.lang.Boolean") 060 private String logNamespaces; 061 @XmlAttribute 062 private String headerName; 063 @XmlTransient 064 private Class<?> documentType; 065 @XmlTransient 066 private Class<?> resultType; 067 @XmlTransient 068 private XPathFactory xpathFactory; 069 @XmlAttribute 070 @Metadata(label = "advanced", javaType = "java.lang.Boolean") 071 private String threadSafety; 072 073 public XPathExpression() { 074 } 075 076 public XPathExpression(String expression) { 077 super(expression); 078 } 079 080 public XPathExpression(Expression expression) { 081 setExpressionValue(expression); 082 } 083 084 @Override 085 public String getLanguage() { 086 return "xpath"; 087 } 088 089 public Class<?> getDocumentType() { 090 return documentType; 091 } 092 093 /** 094 * Class for document type to use 095 * <p/> 096 * The default value is org.w3c.dom.Document 097 */ 098 public void setDocumentType(Class<?> documentType) { 099 this.documentType = documentType; 100 } 101 102 public String getDocumentTypeName() { 103 return documentTypeName; 104 } 105 106 /** 107 * Name of class for document type 108 * <p/> 109 * The default value is org.w3c.dom.Document 110 */ 111 public void setDocumentTypeName(String documentTypeName) { 112 this.documentTypeName = documentTypeName; 113 } 114 115 public Class<?> getResultType() { 116 return resultType; 117 } 118 119 /** 120 * Sets the class of the result type (type from output). 121 * <p/> 122 * The default result type is NodeSet 123 */ 124 public void setResultType(Class<?> resultType) { 125 this.resultType = resultType; 126 } 127 128 public String getResultTypeName() { 129 return resultTypeName; 130 } 131 132 /** 133 * Sets the class name of the result type (type from output) 134 * <p/> 135 * The default result type is NodeSet 136 */ 137 public void setResultTypeName(String resultTypeName) { 138 this.resultTypeName = resultTypeName; 139 } 140 141 /** 142 * Whether to use Saxon. 143 */ 144 public void setSaxon(String saxon) { 145 this.saxon = saxon; 146 } 147 148 public String getSaxon() { 149 return saxon; 150 } 151 152 /** 153 * References to a custom XPathFactory to lookup in the registry 154 */ 155 public void setFactoryRef(String factoryRef) { 156 this.factoryRef = factoryRef; 157 } 158 159 public String getFactoryRef() { 160 return factoryRef; 161 } 162 163 /** 164 * The XPath object model to use 165 */ 166 public void setObjectModel(String objectModel) { 167 this.objectModel = objectModel; 168 } 169 170 public String getObjectModel() { 171 return objectModel; 172 } 173 174 /** 175 * Whether to log namespaces which can assist during trouble shooting 176 */ 177 public void setLogNamespaces(String logNamespaces) { 178 this.logNamespaces = logNamespaces; 179 } 180 181 public String getLogNamespaces() { 182 return logNamespaces; 183 } 184 185 public String getHeaderName() { 186 return headerName; 187 } 188 189 /** 190 * Name of header to use as input, instead of the message body 191 */ 192 public void setHeaderName(String headerName) { 193 this.headerName = headerName; 194 } 195 196 public XPathFactory getXPathFactory() { 197 return xpathFactory; 198 } 199 200 public void setXPathFactory(XPathFactory xpathFactory) { 201 this.xpathFactory = xpathFactory; 202 } 203 204 public String getThreadSafety() { 205 return threadSafety; 206 } 207 208 /** 209 * Whether to enable thread-safety for the returned result of the xpath 210 * expression. This applies to when using NODESET as the result type, and 211 * the returned set has multiple elements. In this situation there can be 212 * thread-safety issues if you process the NODESET concurrently such as from 213 * a Camel Splitter EIP in parallel processing mode. This option prevents 214 * concurrency issues by doing defensive copies of the nodes. 215 * <p/> 216 * It is recommended to turn this option on if you are using camel-saxon or 217 * Saxon in your application. Saxon has thread-safety issues which can be 218 * prevented by turning this option on. 219 */ 220 public void setThreadSafety(String threadSafety) { 221 this.threadSafety = threadSafety; 222 } 223 224 private void resolveXPathFactory(CamelContext camelContext) { 225 // Factory and Object Model can be set simultaneously. The underlying 226 // XPathBuilder allows for setting Saxon too, as it is simply a shortcut 227 // for 228 // setting the appropriate Object Model, it is not wise to allow this in 229 // XML because the order of invocation of the setters by JAXB may cause 230 // undeterministic behaviour 231 if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) { 232 throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this); 233 } 234 235 // Validate the factory class 236 if (ObjectHelper.isNotEmpty(factoryRef)) { 237 xpathFactory = camelContext.getRegistry().lookupByNameAndType(factoryRef, XPathFactory.class); 238 if (xpathFactory == null) { 239 throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance"); 240 } 241 } 242 } 243}