1
2
3
4
5
6
7
8
9
10 package org.mule.module.xml.transformer;
11
12 import org.mule.api.MuleMessage;
13 import org.mule.api.MuleRuntimeException;
14 import org.mule.api.lifecycle.Disposable;
15 import org.mule.api.lifecycle.Initialisable;
16 import org.mule.api.lifecycle.InitialisationException;
17 import org.mule.api.transformer.TransformerException;
18 import org.mule.config.i18n.CoreMessages;
19 import org.mule.module.xml.i18n.XmlMessages;
20 import org.mule.transformer.types.DataTypeFactory;
21 import org.mule.util.IOUtils;
22
23 import java.io.ByteArrayInputStream;
24 import java.io.InputStream;
25 import java.io.StringReader;
26 import java.util.ArrayList;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.xml.namespace.QName;
32 import javax.xml.stream.XMLStreamReader;
33
34 import net.sf.saxon.Configuration;
35 import net.sf.saxon.javax.xml.xquery.XQCommonHandler;
36 import net.sf.saxon.javax.xml.xquery.XQConnection;
37 import net.sf.saxon.javax.xml.xquery.XQDataSource;
38 import net.sf.saxon.javax.xml.xquery.XQException;
39 import net.sf.saxon.javax.xml.xquery.XQItem;
40 import net.sf.saxon.javax.xml.xquery.XQItemType;
41 import net.sf.saxon.javax.xml.xquery.XQPreparedExpression;
42 import net.sf.saxon.javax.xml.xquery.XQResultSequence;
43 import net.sf.saxon.xqj.SaxonXQDataSource;
44
45 import org.apache.commons.pool.BasePoolableObjectFactory;
46 import org.apache.commons.pool.impl.GenericObjectPool;
47 import org.dom4j.io.DOMWriter;
48 import org.dom4j.io.DocumentSource;
49 import org.w3c.dom.Document;
50 import org.w3c.dom.Element;
51 import org.w3c.dom.Node;
52 import org.xml.sax.InputSource;
53
54
55
56
57 public class XQueryTransformer extends AbstractXmlTransformer implements Disposable
58 {
59 public static final String SOURCE_DOCUMENT_NAMESPACE = "document";
60
61
62 private static final int MIN_IDLE_TRANSFORMERS = 1;
63
64 private static final int MAX_IDLE_TRANSFORMERS = 32;
65
66 private static final int MAX_ACTIVE_TRANSFORMERS = MAX_IDLE_TRANSFORMERS;
67
68 protected final GenericObjectPool transformerPool;
69
70 private volatile String xqueryFile;
71 private volatile String xquery;
72 private volatile Map contextProperties;
73 private volatile XQCommonHandler commonHandler;
74 private volatile XQConnection connection;
75 protected Configuration configuration;
76
77 public XQueryTransformer()
78 {
79 super();
80 transformerPool = new GenericObjectPool(new PooledXQueryTransformerFactory());
81 transformerPool.setMinIdle(MIN_IDLE_TRANSFORMERS);
82 transformerPool.setMaxIdle(MAX_IDLE_TRANSFORMERS);
83 transformerPool.setMaxActive(MAX_ACTIVE_TRANSFORMERS);
84
85 registerSourceType(DataTypeFactory.STRING);
86 registerSourceType(DataTypeFactory.BYTE_ARRAY);
87 registerSourceType(DataTypeFactory.create(DocumentSource.class));
88 registerSourceType(DataTypeFactory.create(org.dom4j.Document.class));
89 registerSourceType(DataTypeFactory.create(Document.class));
90 registerSourceType(DataTypeFactory.create(Element.class));
91 registerSourceType(DataTypeFactory.INPUT_STREAM);
92 setReturnDataType(DataTypeFactory.create(Element.class));
93 }
94
95 public XQueryTransformer(String xqueryFile)
96 {
97 this();
98 this.xqueryFile = xqueryFile;
99 }
100
101
102
103
104 @Override
105 public void initialise() throws InitialisationException
106 {
107
108 if (xquery != null && xqueryFile != null)
109 {
110 throw new InitialisationException(XmlMessages.canOnlySetFileOrXQuery(), this);
111 }
112
113 try
114 {
115 if (xqueryFile != null)
116 {
117 xquery = IOUtils.getResourceAsString(xqueryFile, getClass());
118 }
119 if (configuration == null)
120 {
121 configuration = new Configuration();
122 }
123
124 XQDataSource ds = new SaxonXQDataSource(configuration);
125 if (commonHandler != null)
126 {
127 ds.setCommonHandler(commonHandler);
128 }
129 connection = ds.getConnection();
130
131 transformerPool.addObject();
132
133 }
134 catch (Throwable te)
135 {
136 throw new InitialisationException(te, this);
137 }
138 }
139
140 @Override
141 public void dispose()
142 {
143 try
144 {
145 connection.close();
146 }
147 catch (XQException e)
148 {
149 logger.warn(e.getMessage());
150 }
151 }
152
153 @Override
154 public Object transformMessage(MuleMessage message, String outputEncoding) throws TransformerException
155 {
156 try
157 {
158 XQPreparedExpression transformer = null;
159 try
160 {
161 transformer = (XQPreparedExpression) transformerPool.borrowObject();
162
163 bindParameters(transformer, message);
164
165 bindDocument(message.getPayload(), transformer);
166
167 XQResultSequence result = transformer.executeQuery();
168
169 List results = new ArrayList();
170 while (result.next())
171 {
172 XQItem item = result.getItem();
173
174 Class type = returnType.getType();
175 if (Node.class.isAssignableFrom(type) || Node[].class.isAssignableFrom(type))
176 {
177 results.add(item.getNode());
178 }
179 else if (String.class.isAssignableFrom(type) || String[].class.isAssignableFrom(type))
180 {
181 results.add(item.getItemAsString());
182 }
183 else if (XMLStreamReader.class.isAssignableFrom(type) || XMLStreamReader[].class.isAssignableFrom(type))
184 {
185 try
186 {
187 results.add(item.getItemAsStream());
188 }
189 catch (XQException e)
190 {
191 throw new TransformerException(XmlMessages.streamNotAvailble(getName()));
192 }
193 }
194 else
195 {
196
197 try
198 {
199 results.add(item.getObject());
200 }
201 catch (XQException e)
202 {
203 throw new TransformerException(XmlMessages.objectNotAvailble(getName()));
204
205 }
206 }
207 if (!type.isArray())
208 {
209 break;
210 }
211 }
212 if (returnType.getType().isArray())
213 {
214 return results.toArray();
215 }
216 if (results.size() == 1)
217 {
218 return results.get(0);
219 }
220 else if (results.size() == 0)
221 {
222 return null;
223 }
224 else
225 {
226 return results.toArray();
227 }
228
229 }
230 finally
231 {
232 if (transformer != null)
233 {
234 if (transformer.getWarnings() != null)
235 {
236 logger.warn(transformer.getWarnings().getMessage(), transformer.getWarnings().fillInStackTrace());
237 }
238
239
240
241
242 transformerPool.returnObject(transformer);
243 }
244 }
245
246 }
247 catch (Exception e)
248 {
249 throw new TransformerException(this, e);
250 }
251 }
252
253 protected void bindParameters(XQPreparedExpression transformer, MuleMessage message) throws XQException, TransformerException
254 {
255
256 if (contextProperties != null)
257 {
258 for (Iterator i = contextProperties.entrySet().iterator(); i.hasNext();)
259 {
260 Map.Entry parameter = (Map.Entry) i.next();
261 String key = (String) parameter.getKey();
262 Object o = evaluateTransformParameter(key, parameter.getValue(), message);
263
264 if (o instanceof String)
265 {
266 transformer.bindAtomicValue(new QName(key), o.toString(), connection.createAtomicItemType(XQItemType.XQBASETYPE_STRING));
267 }
268 else if (o instanceof Boolean)
269 {
270 transformer.bindBoolean(new QName(key), ((Boolean) o).booleanValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_BOOLEAN));
271 }
272 else if (o instanceof Byte)
273 {
274 transformer.bindByte(new QName(key), ((Byte) o).byteValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_BYTE));
275 }
276 else if (o instanceof Short)
277 {
278 transformer.bindShort(new QName(key), ((Short) o).shortValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_SHORT));
279 }
280 else if (o instanceof Integer)
281 {
282 transformer.bindInt(new QName(key), ((Integer) o).intValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_INT));
283 }
284 else if (o instanceof Long)
285 {
286 transformer.bindLong(new QName(key), ((Long) o).longValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_LONG));
287 }
288 else if (o instanceof Float)
289 {
290 transformer.bindFloat(new QName(key), ((Float) o).floatValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_FLOAT));
291 }
292 else if (o instanceof Double)
293 {
294 transformer.bindDouble(new QName(key), ((Double) o).doubleValue(), connection.createAtomicItemType(XQItemType.XQBASETYPE_DOUBLE));
295 }
296 else
297 {
298 logger.error("Cannot bind value: " + o + " cannot be bound to the Xquery context. Not of supported type");
299 }
300 }
301 }
302 }
303
304
305
306
307
308
309
310
311
312
313
314 protected void bindDocument(Object src, XQPreparedExpression transformer) throws Exception
315 {
316 if (src instanceof byte[])
317 {
318 transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource(new ByteArrayInputStream((byte[]) src)));
319
320 }
321 else if (src instanceof InputStream)
322 {
323 transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource((InputStream) src));
324
325 }
326 else if (src instanceof String)
327 {
328 transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), new InputSource(new StringReader((String) src)));
329
330 }
331 else if (src instanceof Document)
332 {
333 transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), (Document) src, null);
334
335 }
336 else if (src instanceof Element)
337 {
338 transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), (Element) src, null);
339
340 }
341 else if (src instanceof org.dom4j.Document)
342 {
343 DOMWriter domWriter = new DOMWriter();
344 Document dom = domWriter.write((org.dom4j.Document) src);
345 transformer.bindNode(new QName(SOURCE_DOCUMENT_NAMESPACE), dom, null);
346
347 }
348 else if (src instanceof DocumentSource)
349 {
350 transformer.bindDocument(new QName(SOURCE_DOCUMENT_NAMESPACE), ((DocumentSource) src).getInputSource());
351
352 }
353 else
354 {
355 throw new IllegalArgumentException(CoreMessages.transformUnexpectedType(src.getClass(), null).getMessage());
356 }
357 }
358
359 public Configuration getConfiguration()
360 {
361 return configuration;
362 }
363
364 public void setConfiguration(Configuration configuration)
365 {
366 this.configuration = configuration;
367 }
368
369
370
371
372 public String getXqueryFile()
373 {
374 return xqueryFile;
375 }
376
377
378
379
380 public void setXqueryFile(String xqueryFile)
381 {
382 this.xqueryFile = xqueryFile;
383 }
384
385 public String getXquery()
386 {
387 return xquery;
388 }
389
390 public void setXquery(String xquery)
391 {
392 this.xquery = xquery;
393 }
394
395 public XQCommonHandler getCommonHandler()
396 {
397 return commonHandler;
398 }
399
400 public void setCommonHandler(XQCommonHandler commonHandler)
401 {
402 this.commonHandler = commonHandler;
403 }
404
405
406 protected class PooledXQueryTransformerFactory extends BasePoolableObjectFactory
407 {
408 @Override
409 public Object makeObject() throws Exception
410 {
411 return connection.prepareExpression(xquery);
412 }
413
414 @Override
415 public void destroyObject(Object o) throws Exception
416 {
417 ((XQPreparedExpression) o).close();
418 super.destroyObject(o);
419 }
420 }
421
422
423
424
425
426
427 public int getMaxActiveTransformers()
428 {
429 return transformerPool.getMaxActive();
430 }
431
432
433
434
435
436
437
438 public void setMaxActiveTransformers(int maxActiveTransformers)
439 {
440 transformerPool.setMaxActive(maxActiveTransformers);
441 }
442
443
444
445
446
447 public int getMaxIdleTransformers()
448 {
449 return transformerPool.getMaxIdle();
450 }
451
452
453
454
455
456
457 public void setMaxIdleTransformers(int maxIdleTransformers)
458 {
459 transformerPool.setMaxIdle(maxIdleTransformers);
460 }
461
462
463
464
465
466
467
468
469 public Map getContextProperties()
470 {
471 return contextProperties;
472 }
473
474
475
476
477
478
479
480
481 public void setContextProperties(Map contextProperties)
482 {
483 this.contextProperties = contextProperties;
484 }
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508 protected Object evaluateTransformParameter(String name, Object value, MuleMessage message) throws TransformerException
509 {
510 if (value instanceof String)
511 {
512 return muleContext.getExpressionManager().parse(value.toString(), message);
513 }
514 return value;
515 }
516
517 @Override
518 public Object clone() throws CloneNotSupportedException
519 {
520 Object clone = super.clone();
521 try
522 {
523 ((Initialisable) clone).initialise();
524 return clone;
525 }
526 catch (InitialisationException e)
527 {
528 throw new MuleRuntimeException(CoreMessages.failedToClone(getClass().getName()), e);
529 }
530 }
531 }