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 }