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