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.builder.saxon;
018
019 import net.sf.saxon.Configuration;
020 import net.sf.saxon.om.DocumentInfo;
021 import net.sf.saxon.query.DynamicQueryContext;
022 import net.sf.saxon.query.StaticQueryContext;
023 import net.sf.saxon.query.XQueryExpression;
024 import net.sf.saxon.trans.XPathException;
025 import org.apache.camel.Exchange;
026 import org.apache.camel.Expression;
027 import org.apache.camel.Predicate;
028 import org.apache.camel.RuntimeExpressionException;
029 import org.apache.camel.converter.IOConverter;
030 import org.apache.camel.converter.jaxp.BytesSource;
031 import org.apache.camel.converter.jaxp.StringSource;
032 import org.apache.camel.converter.jaxp.XmlConverter;
033 import org.apache.camel.util.ObjectHelper;
034 import org.w3c.dom.Node;
035
036 import javax.xml.transform.Result;
037 import javax.xml.transform.Source;
038 import javax.xml.transform.dom.DOMResult;
039 import javax.xml.transform.stream.StreamResult;
040 import java.io.ByteArrayOutputStream;
041 import java.io.File;
042 import java.io.FileNotFoundException;
043 import java.io.IOException;
044 import java.io.InputStream;
045 import java.io.Reader;
046 import java.io.StringWriter;
047 import java.net.URL;
048 import java.util.HashMap;
049 import java.util.List;
050 import java.util.Map;
051 import java.util.Properties;
052 import java.util.Set;
053
054 /**
055 * Creates an XQuery builder
056 *
057 * @version $Revision: 564167 $
058 */
059 public abstract class XQueryBuilder<E extends Exchange> implements Expression<E>, Predicate<E> {
060 private Configuration configuration;
061 private XQueryExpression expression;
062 private StaticQueryContext staticQueryContext;
063 private Map<String, Object> parameters = new HashMap<String, Object>();
064 private XmlConverter converter = new XmlConverter();
065 private ResultFormat resultsFormat = ResultFormat.DOM;
066 private Properties properties = new Properties();
067
068 @Override
069 public String toString() {
070 return "XQuery[" + expression + "]";
071 }
072
073 public Object evaluate(E exchange) {
074 try {
075 switch (resultsFormat) {
076 case Bytes:
077 return evaluateAsBytes(exchange);
078 case BytesSource:
079 return evaluateAsBytesSource(exchange);
080 case DOM:
081 return evaluateAsDOM(exchange);
082 case List:
083 return evaluateAsList(exchange);
084 case StringSource:
085 return evaluateAsStringSource(exchange);
086 case String:
087 default:
088 return evaluateAsString(exchange);
089 }
090 } catch (Exception e) {
091 throw new RuntimeExpressionException(e);
092 }
093 }
094
095 public List evaluateAsList(E exchange) throws Exception {
096 return getExpression().evaluate(createDynamicContext(exchange));
097 }
098
099 public Object evaluateAsStringSource(E exchange) throws Exception {
100 String text = evaluateAsString(exchange);
101 return new StringSource(text);
102 }
103
104 public Object evaluateAsBytesSource(E exchange) throws Exception {
105 byte[] bytes = evaluateAsBytes(exchange);
106 return new BytesSource(bytes);
107 }
108
109 public Node evaluateAsDOM(E exchange) throws Exception {
110 DOMResult result = new DOMResult();
111 getExpression().pull(createDynamicContext(exchange), result, properties);
112 return result.getNode();
113 }
114
115 public byte[] evaluateAsBytes(E exchange) throws Exception {
116 ByteArrayOutputStream buffer = new ByteArrayOutputStream();
117 Result result = new StreamResult(buffer);
118 getExpression().pull(createDynamicContext(exchange), result, properties);
119 byte[] bytes = buffer.toByteArray();
120 return bytes;
121 }
122
123 public String evaluateAsString(E exchange) throws Exception {
124 StringWriter buffer = new StringWriter();
125 Result result = new StreamResult(buffer);
126 getExpression().pull(createDynamicContext(exchange), result, properties);
127 String text = buffer.toString();
128 return text;
129 }
130
131 public boolean matches(E exchange) {
132 try {
133 List list = evaluateAsList(exchange);
134 return matches(exchange, list);
135 } catch (Exception e) {
136 throw new RuntimeExpressionException(e);
137 }
138 }
139
140 public void assertMatches(String text, E exchange) throws AssertionError {
141 try {
142 List list = evaluateAsList(exchange);
143 if (!matches(exchange, list)) {
144 throw new AssertionError(this + " failed on " + exchange + " as evaluated: " + list);
145 }
146 } catch (Exception e) {
147 throw new AssertionError(e);
148 }
149 }
150
151 // Static helper methods
152 //-------------------------------------------------------------------------
153 public static <E extends Exchange> XQueryBuilder<E> xquery(final String queryText) {
154 return new XQueryBuilder<E>() {
155 protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException {
156 return staticQueryContext.compileQuery(queryText);
157 }
158 };
159 }
160
161 public static <E extends Exchange> XQueryBuilder<E> xquery(final Reader reader) {
162 return new XQueryBuilder<E>() {
163 protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException {
164 return staticQueryContext.compileQuery(reader);
165 }
166 };
167 }
168
169 public static <E extends Exchange> XQueryBuilder<E> xquery(final InputStream in, final String characterSet) {
170 return new XQueryBuilder<E>() {
171 protected XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException {
172 return staticQueryContext.compileQuery(in, characterSet);
173 }
174 };
175 }
176
177 public static <E extends Exchange> XQueryBuilder<E> xquery(File file, String characterSet) throws FileNotFoundException {
178 return xquery(IOConverter.toInputStream(file), characterSet);
179 }
180
181 public static <E extends Exchange> XQueryBuilder<E> xquery(URL url, String characterSet) throws IOException {
182 return xquery(IOConverter.toInputStream(url), characterSet);
183 }
184
185 public static <E extends Exchange> XQueryBuilder<E> xquery(File file) throws FileNotFoundException {
186 return xquery(IOConverter.toInputStream(file), ObjectHelper.getDefaultCharacterSet());
187 }
188
189 public static <E extends Exchange> XQueryBuilder<E> xquery(URL url) throws IOException {
190 return xquery(IOConverter.toInputStream(url), ObjectHelper.getDefaultCharacterSet());
191 }
192
193
194 // Fluent API
195 // -------------------------------------------------------------------------
196 public XQueryBuilder<E> asBytes() {
197 setResultsFormat(ResultFormat.Bytes);
198 return this;
199 }
200
201 public XQueryBuilder<E> asBytesSource() {
202 setResultsFormat(ResultFormat.BytesSource);
203 return this;
204 }
205
206 public XQueryBuilder<E> asDOM() {
207 setResultsFormat(ResultFormat.DOM);
208 return this;
209 }
210
211 public XQueryBuilder<E> asDOMSource() {
212 setResultsFormat(ResultFormat.DOMSource);
213 return this;
214 }
215
216 public XQueryBuilder<E> asList() {
217 setResultsFormat(ResultFormat.List);
218 return this;
219 }
220
221 public XQueryBuilder<E> asString() {
222 setResultsFormat(ResultFormat.String);
223 return this;
224 }
225
226 public XQueryBuilder<E> asStringSource() {
227 setResultsFormat(ResultFormat.StringSource);
228 return this;
229 }
230
231 public XQueryBuilder<E> parameter(String name, Object value) {
232 parameters.put(name, value);
233 return this;
234 }
235
236 // Properties
237 // -------------------------------------------------------------------------
238
239 public XQueryExpression getExpression() throws IOException, XPathException {
240 if (expression == null) {
241 expression = createQueryExpression(getStaticQueryContext());
242 clearBuilderReferences();
243 }
244 return expression;
245 }
246
247 public Configuration getConfiguration() {
248 if (configuration == null) {
249 configuration = new Configuration();
250 configuration.setHostLanguage(Configuration.XQUERY);
251 }
252 return configuration;
253 }
254
255 public void setConfiguration(Configuration configuration) {
256 this.configuration = configuration;
257 }
258
259 public StaticQueryContext getStaticQueryContext() {
260 if (staticQueryContext == null) {
261 staticQueryContext = new StaticQueryContext(getConfiguration());
262 }
263 return staticQueryContext;
264 }
265
266 public void setStaticQueryContext(StaticQueryContext staticQueryContext) {
267 this.staticQueryContext = staticQueryContext;
268 }
269
270 public Map<String, Object> getParameters() {
271 return parameters;
272 }
273
274 public void setParameters(Map<String, Object> parameters) {
275 this.parameters = parameters;
276 }
277
278 public Properties getProperties() {
279 return properties;
280 }
281
282 public void setProperties(Properties properties) {
283 this.properties = properties;
284 }
285
286 public ResultFormat getResultsFormat() {
287 return resultsFormat;
288 }
289
290 public void setResultsFormat(ResultFormat resultsFormat) {
291 this.resultsFormat = resultsFormat;
292 }
293
294 // Implementation methods
295 // -------------------------------------------------------------------------
296
297 /**
298 * A factory method to create the XQuery expression
299 */
300 protected abstract XQueryExpression createQueryExpression(StaticQueryContext staticQueryContext) throws XPathException, IOException;
301
302 /**
303 * Creates a dynamic context for the given exchange
304 */
305 protected DynamicQueryContext createDynamicContext(E exchange) throws Exception {
306 Configuration config = getConfiguration();
307 DynamicQueryContext dynamicQueryContext = new DynamicQueryContext(config);
308
309 Source source = exchange.getIn().getBody(Source.class);
310 if (source == null) {
311 source = converter.toSource(converter.createDocument());
312 }
313
314 DocumentInfo doc = getStaticQueryContext().buildDocument(source);
315 dynamicQueryContext.setContextItem(doc);
316 configureQuery(dynamicQueryContext, exchange);
317 return dynamicQueryContext;
318 }
319
320 /**
321 * Configures the dynamic context with exchange specific parameters
322 *
323 * @param dynamicQueryContext
324 * @param exchange
325 * @throws Exception
326 */
327 protected void configureQuery(DynamicQueryContext dynamicQueryContext, Exchange exchange) throws Exception {
328 addParameters(dynamicQueryContext, exchange.getProperties());
329 addParameters(dynamicQueryContext, exchange.getIn().getHeaders());
330 addParameters(dynamicQueryContext, getParameters());
331
332 dynamicQueryContext.setParameter("exchange", exchange);
333 dynamicQueryContext.setParameter("in", exchange.getIn());
334 dynamicQueryContext.setParameter("out", exchange.getOut());
335 }
336
337 protected void addParameters(DynamicQueryContext dynamicQueryContext, Map<String, Object> map) {
338 Set<Map.Entry<String, Object>> propertyEntries = map.entrySet();
339 for (Map.Entry<String, Object> entry : propertyEntries) {
340 dynamicQueryContext.setParameter(entry.getKey(), entry.getValue());
341 }
342 }
343
344 /**
345 * To avoid keeping around any unnecessary objects after the expresion has
346 * been created lets nullify references here
347 */
348 protected void clearBuilderReferences() {
349 staticQueryContext = null;
350 configuration = null;
351 }
352
353 protected boolean matches(E exchange, List results) {
354 return ObjectHelper.matches(results);
355 }
356 }