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}