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