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     */
017    package org.apache.camel.model.language;
018    
019    import javax.xml.bind.annotation.XmlAccessType;
020    import javax.xml.bind.annotation.XmlAccessorType;
021    import javax.xml.bind.annotation.XmlAttribute;
022    import javax.xml.bind.annotation.XmlRootElement;
023    import javax.xml.bind.annotation.XmlTransient;
024    import javax.xml.xpath.XPathFactory;
025    
026    import org.apache.camel.CamelContext;
027    import org.apache.camel.Expression;
028    import org.apache.camel.Predicate;
029    import org.apache.camel.builder.xml.XPathBuilder;
030    import org.apache.camel.util.ObjectHelper;
031    
032    /**
033     * For XPath expressions and predicates
034     */
035    @XmlRootElement(name = "xpath")
036    @XmlAccessorType(XmlAccessType.FIELD)
037    public class XPathExpression extends NamespaceAwareExpression {
038        @XmlAttribute(name = "resultType")
039        private String resultTypeName;
040        @XmlAttribute(name = "saxon")
041        private Boolean saxon;
042        @XmlAttribute(name = "factoryRef")
043        private String factoryRef;
044        @XmlAttribute(name = "objectModel")
045        private String objectModel;
046        @XmlAttribute(name = "logNamespaces")
047        private Boolean logNamespaces;
048        @XmlAttribute(name = "headerName")
049        private String headerName;
050        
051        @XmlTransient
052        private Class<?> resultType;
053        @XmlTransient
054        private XPathFactory xpathFactory;
055        
056    
057        public XPathExpression() {
058        }
059    
060        public XPathExpression(String expression) {
061            super(expression);
062        }
063    
064        public XPathExpression(Expression expression) {
065            setExpressionValue(expression);
066        }
067    
068        public String getLanguage() {
069            return "xpath";
070        }
071    
072        public Class<?> getResultType() {
073            return resultType;
074        }
075    
076        public void setResultType(Class<?> resultType) {
077            this.resultType = resultType;
078        }
079    
080        public String getResultTypeName() {
081            return resultTypeName;
082        }
083    
084        public void setResultTypeName(String resultTypeName) {
085            this.resultTypeName = resultTypeName;
086        }
087    
088        public void setSaxon(Boolean saxon) {
089            this.saxon = saxon;
090        }
091    
092        public Boolean getSaxon() {
093            return saxon;
094        }
095    
096        public boolean isSaxon() {
097            return saxon != null && saxon;
098        }
099    
100        public void setFactoryRef(String factoryRef) {
101            this.factoryRef = factoryRef;
102        }
103    
104        public String getFactoryRef() {
105            return factoryRef;
106        }
107    
108        public void setObjectModel(String objectModel) {
109            this.objectModel = objectModel;
110        }
111    
112        public String getObjectModel() {
113            return objectModel;
114        }
115    
116        public void setLogNamespaces(Boolean logNamespaces) {
117            this.logNamespaces = logNamespaces;
118        }
119    
120        public Boolean getLogNamespaces() {
121            return logNamespaces;
122        }
123    
124        public boolean isLogNamespaces() {
125            return logNamespaces != null && logNamespaces;
126        }
127        
128        public String getHeaderName() {
129            return headerName;
130        }
131    
132        public void setHeaderName(String headerName) {
133            this.headerName = headerName;
134        }
135    
136        @Override
137        public Expression createExpression(CamelContext camelContext) {
138            if (resultType == null && resultTypeName != null) {
139                try {
140                    resultType = camelContext.getClassResolver().resolveMandatoryClass(resultTypeName);
141                } catch (ClassNotFoundException e) {
142                    throw ObjectHelper.wrapRuntimeCamelException(e);
143                }
144            }
145            resolveXPathFactory(camelContext);
146            return super.createExpression(camelContext);
147        }
148    
149        @Override
150        public Predicate createPredicate(CamelContext camelContext) {
151            resolveXPathFactory(camelContext);
152            return super.createPredicate(camelContext);
153        }
154    
155        @Override
156        protected void configureExpression(CamelContext camelContext, Expression expression) {
157            if (resultType != null) {
158                setProperty(expression, "resultType", resultType);
159            }
160            if (isSaxon()) {
161                ObjectHelper.cast(XPathBuilder.class, expression).enableSaxon();
162            }
163            if (xpathFactory != null) {
164                setProperty(expression, "xPathFactory", xpathFactory);
165            }
166            if (objectModel != null) {
167                setProperty(expression, "objectModelUri", objectModel);
168            }
169            if (isLogNamespaces()) {
170                ObjectHelper.cast(XPathBuilder.class, expression).setLogNamespaces(true);
171            }
172            if (ObjectHelper.isNotEmpty(getHeaderName())) {
173                ObjectHelper.cast(XPathBuilder.class, expression).setHeaderName(getHeaderName());
174            }
175            // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
176            super.configureExpression(camelContext, expression);
177    
178        }
179    
180        @Override
181        protected void configurePredicate(CamelContext camelContext, Predicate predicate) {
182            if (resultType != null) {
183                setProperty(predicate, "resultType", resultType);
184            }
185            if (isSaxon()) {
186                ObjectHelper.cast(XPathBuilder.class, predicate).enableSaxon();
187            }
188            if (xpathFactory != null) {
189                setProperty(predicate, "xPathFactory", xpathFactory);
190            }
191            if (objectModel != null) {
192                setProperty(predicate, "objectModelUri", objectModel);
193            }
194            if (isLogNamespaces()) {
195                ObjectHelper.cast(XPathBuilder.class, predicate).setLogNamespaces(true);
196            }
197            if (ObjectHelper.isNotEmpty(getHeaderName())) {
198                ObjectHelper.cast(XPathBuilder.class, predicate).setHeaderName(getHeaderName());
199            }
200            // moved the super configuration to the bottom so that the namespace init picks up the newly set XPath Factory
201            super.configurePredicate(camelContext, predicate);
202        }
203    
204        private void resolveXPathFactory(CamelContext camelContext) {
205            // Factory and Object Model can be set simultaneously. The underlying XPathBuilder allows for setting Saxon too, as it is simply a shortcut for
206            // 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 
207            if ((ObjectHelper.isNotEmpty(factoryRef) || ObjectHelper.isNotEmpty(objectModel)) && (saxon != null)) {
208                throw new IllegalArgumentException("The saxon attribute cannot be set on the xpath element if any of the following is also set: factory, objectModel" + this);
209            }
210    
211            // Validate the factory class
212            if (ObjectHelper.isNotEmpty(factoryRef)) {
213                xpathFactory = camelContext.getRegistry().lookupByNameAndType(factoryRef, XPathFactory.class);
214                if (xpathFactory == null) {
215                    throw new IllegalArgumentException("The provided XPath Factory is invalid; either it cannot be resolved or it is not an XPathFactory instance");
216                }
217            }
218        }
219    }