1
2
3
4
5
6
7
8
9
10
11 package org.mule.providers.jdbc;
12
13 import org.mule.MuleManager;
14 import org.mule.config.ExceptionHelper;
15 import org.mule.config.i18n.CoreMessages;
16 import org.mule.providers.AbstractConnector;
17 import org.mule.providers.jdbc.i18n.JdbcMessages;
18 import org.mule.providers.jdbc.xa.DataSourceWrapper;
19 import org.mule.transaction.TransactionCoordination;
20 import org.mule.umo.TransactionException;
21 import org.mule.umo.UMOComponent;
22 import org.mule.umo.UMOException;
23 import org.mule.umo.UMOTransaction;
24 import org.mule.umo.endpoint.UMOEndpoint;
25 import org.mule.umo.endpoint.UMOImmutableEndpoint;
26 import org.mule.umo.lifecycle.InitialisationException;
27 import org.mule.umo.provider.UMOMessageReceiver;
28 import org.mule.util.ClassUtils;
29 import org.mule.util.ExceptionUtils;
30 import org.mule.util.StringUtils;
31 import org.mule.util.properties.BeanPropertyExtractor;
32 import org.mule.util.properties.MapPropertyExtractor;
33 import org.mule.util.properties.MessagePropertyExtractor;
34 import org.mule.util.properties.PayloadPropertyExtractor;
35 import org.mule.util.properties.PropertyExtractor;
36
37 import java.sql.Connection;
38 import java.util.HashSet;
39 import java.util.Hashtable;
40 import java.util.Iterator;
41 import java.util.List;
42 import java.util.Map;
43 import java.util.Set;
44 import java.util.StringTokenizer;
45 import java.util.regex.Matcher;
46 import java.util.regex.Pattern;
47
48 import javax.naming.Context;
49 import javax.naming.InitialContext;
50 import javax.naming.NamingException;
51 import javax.sql.DataSource;
52 import javax.sql.XADataSource;
53
54 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentHashMap;
55 import edu.emory.mathcs.backport.java.util.concurrent.ConcurrentMap;
56 import org.apache.commons.dbutils.QueryRunner;
57 import org.apache.commons.dbutils.ResultSetHandler;
58
59 public class JdbcConnector extends AbstractConnector
60 {
61
62
63 public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
64 public static final long DEFAULT_POLLING_FREQUENCY = 1000;
65
66 private static final String DEFAULT_QUERY_RUNNER = "org.apache.commons.dbutils.QueryRunner";
67 private static final String DEFAULT_RESULTSET_HANDLER = "org.apache.commons.dbutils.handlers.MapListHandler";
68
69 private static final Pattern STATEMENT_ARGS = Pattern.compile("\\$\\{[^\\}]*\\}");
70
71
72 static
73 {
74 ExceptionHelper.registerExceptionReader(new SQLExceptionReader());
75 }
76
77 protected long pollingFrequency = 0;
78 protected DataSource dataSource;
79 protected String dataSourceJndiName;
80 protected Context jndiContext;
81 protected String jndiInitialFactory;
82 protected String jndiProviderUrl;
83 protected Map providerProperties;
84 protected Map queries;
85 protected String resultSetHandler = DEFAULT_RESULTSET_HANDLER;
86 protected String queryRunner = DEFAULT_QUERY_RUNNER;
87 protected Set queryValueExtractors;
88 protected Set propertyExtractors;
89
90 protected ConcurrentMap propertyExtractorCache = new ConcurrentHashMap();
91
92 protected void doInitialise() throws InitialisationException
93 {
94 try
95 {
96
97
98 if (dataSource == null)
99 {
100 initJndiContext();
101 createDataSource();
102 }
103
104 if (dataSource != null && dataSource instanceof XADataSource)
105 {
106 if (MuleManager.getInstance().getTransactionManager() != null)
107 {
108 dataSource = new DataSourceWrapper((XADataSource)dataSource, MuleManager.getInstance()
109 .getTransactionManager());
110 }
111 }
112
113
114 if (queryValueExtractors == null)
115 {
116
117 queryValueExtractors = new HashSet();
118 queryValueExtractors.add(MessagePropertyExtractor.class.getName());
119 queryValueExtractors.add(NowPropertyExtractor.class.getName());
120 queryValueExtractors.add(PayloadPropertyExtractor.class.getName());
121 queryValueExtractors.add(MapPropertyExtractor.class.getName());
122 queryValueExtractors.add(BeanPropertyExtractor.class.getName());
123
124 if (ClassUtils.isClassOnPath("org.mule.util.properties.Dom4jPropertyExtractor", getClass()))
125 {
126 queryValueExtractors.add("org.mule.util.properties.Dom4jPropertyExtractor");
127 }
128
129 if (ClassUtils.isClassOnPath("org.mule.util.properties.JDomPropertyExtractor", getClass()))
130 {
131 queryValueExtractors.add("org.mule.util.properties.JDomPropertyExtractor");
132 }
133 }
134
135 propertyExtractors = new HashSet();
136 for (Iterator iterator = queryValueExtractors.iterator(); iterator.hasNext();)
137 {
138 String s = (String)iterator.next();
139 propertyExtractors.add(ClassUtils.instanciateClass(s, ClassUtils.NO_ARGS));
140 }
141 }
142 catch (Exception e)
143 {
144 throw new InitialisationException(CoreMessages.failedToCreate("Jdbc Connector"), e, this);
145 }
146 }
147
148 protected void doDispose()
149 {
150
151 }
152
153 protected void doConnect() throws Exception
154 {
155
156 }
157
158 protected void doDisconnect() throws Exception
159 {
160
161 }
162
163 protected void doStart() throws UMOException
164 {
165
166 }
167
168 protected void doStop() throws UMOException
169 {
170
171 }
172
173
174
175
176
177
178 public String getProtocol()
179 {
180 return "jdbc";
181 }
182
183 public UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint) throws Exception
184 {
185 Map props = endpoint.getProperties();
186 if (props != null)
187 {
188 String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);
189 if (tempPolling != null)
190 {
191 pollingFrequency = Long.parseLong(tempPolling);
192 }
193 }
194
195 if (pollingFrequency <= 0)
196 {
197 pollingFrequency = DEFAULT_POLLING_FREQUENCY;
198 }
199
200 String[] params = getReadAndAckStatements(endpoint);
201 return getServiceDescriptor().createMessageReceiver(this, component, endpoint, params);
202 }
203
204 protected void initJndiContext() throws NamingException
205 {
206 if (this.jndiContext == null)
207 {
208 Hashtable props = new Hashtable();
209 if (this.jndiInitialFactory != null)
210 {
211 props.put(Context.INITIAL_CONTEXT_FACTORY, this.jndiInitialFactory);
212 }
213 if (this.jndiProviderUrl != null)
214 {
215 props.put(Context.PROVIDER_URL, jndiProviderUrl);
216 }
217 if (this.providerProperties != null)
218 {
219 props.putAll(this.providerProperties);
220 }
221 this.jndiContext = new InitialContext(props);
222 }
223
224 }
225
226 protected void createDataSource() throws InitialisationException, NamingException
227 {
228 Object temp = this.jndiContext.lookup(this.dataSourceJndiName);
229 if (temp instanceof DataSource)
230 {
231 dataSource = (DataSource)temp;
232 }
233 else
234 {
235 throw new InitialisationException(
236 JdbcMessages.jndiResourceNotFound(this.dataSourceJndiName), this);
237 }
238 }
239
240 public String[] getReadAndAckStatements(UMOImmutableEndpoint endpoint)
241 {
242 String str;
243
244 String readStmt;
245 if ((str = (String)endpoint.getProperty("sql")) != null)
246 {
247 readStmt = str;
248 }
249 else
250 {
251 readStmt = endpoint.getEndpointURI().getAddress();
252 }
253
254 String ackStmt;
255 if ((str = (String)endpoint.getProperty("ack")) != null)
256 {
257 ackStmt = str;
258 if ((str = getQuery(endpoint, ackStmt)) != null)
259 {
260 ackStmt = str;
261 }
262 }
263 else
264 {
265 ackStmt = readStmt + ".ack";
266 if ((str = getQuery(endpoint, ackStmt)) != null)
267 {
268 ackStmt = str;
269 }
270 else
271 {
272 ackStmt = null;
273 }
274 }
275
276 if ((str = getQuery(endpoint, readStmt)) != null)
277 {
278 readStmt = str;
279 }
280 if (readStmt == null)
281 {
282 throw new IllegalArgumentException("Read statement should not be null");
283 }
284 if (!"select".equalsIgnoreCase(readStmt.substring(0, 6)) && !"call".equalsIgnoreCase(readStmt.substring(0, 4)))
285 {
286 throw new IllegalArgumentException("Read statement should be a select sql statement or a stored procedure");
287 }
288 if (ackStmt != null)
289 {
290 if (!"insert".equalsIgnoreCase(ackStmt.substring(0, 6))
291 && !"update".equalsIgnoreCase(ackStmt.substring(0, 6))
292 && !"delete".equalsIgnoreCase(ackStmt.substring(0, 6)))
293 {
294 throw new IllegalArgumentException(
295 "Ack statement should be an insert / update / delete sql statement");
296 }
297 }
298 return new String[]{readStmt, ackStmt};
299 }
300
301 public String getQuery(UMOImmutableEndpoint endpoint, String stmt)
302 {
303 Object query = null;
304 if (endpoint != null && endpoint.getProperties() != null)
305 {
306 Object queries = endpoint.getProperties().get("queries");
307 if (queries instanceof Map)
308 {
309 query = ((Map)queries).get(stmt);
310 }
311 }
312 if (query == null)
313 {
314 if (this.queries != null)
315 {
316 query = this.queries.get(stmt);
317 }
318 }
319 return query == null ? null : query.toString();
320 }
321
322
323
324
325 public DataSource getDataSource()
326 {
327 return dataSource;
328 }
329
330
331
332
333 public void setDataSource(DataSource dataSource)
334 {
335 this.dataSource = dataSource;
336 }
337
338
339
340
341 public long getPollingFrequency()
342 {
343 return pollingFrequency;
344 }
345
346
347
348
349 public void setPollingFrequency(long pollingFrequency)
350 {
351 this.pollingFrequency = pollingFrequency;
352 }
353
354
355
356
357 public Map getQueries()
358 {
359 return queries;
360 }
361
362
363
364
365 public void setQueries(Map queries)
366 {
367 this.queries = queries;
368 }
369
370
371
372
373 public String getDataSourceJndiName()
374 {
375 return dataSourceJndiName;
376 }
377
378
379
380
381 public void setDataSourceJndiName(String dataSourceJndiName)
382 {
383 this.dataSourceJndiName = dataSourceJndiName;
384 }
385
386
387
388
389 public Context getJndiContext()
390 {
391 return jndiContext;
392 }
393
394
395
396
397 public void setJndiContext(Context jndiContext)
398 {
399 this.jndiContext = jndiContext;
400 }
401
402
403
404
405 public String getJndiInitialFactory()
406 {
407 return jndiInitialFactory;
408 }
409
410
411
412
413 public void setJndiInitialFactory(String jndiInitialFactory)
414 {
415 this.jndiInitialFactory = jndiInitialFactory;
416 }
417
418
419
420
421 public String getJndiProviderUrl()
422 {
423 return jndiProviderUrl;
424 }
425
426
427
428
429 public void setJndiProviderUrl(String jndiProviderUrl)
430 {
431 this.jndiProviderUrl = jndiProviderUrl;
432 }
433
434
435
436
437 public Map getProviderProperties()
438 {
439 return providerProperties;
440 }
441
442
443
444
445 public void setProviderProperties(Map providerProperties)
446 {
447 this.providerProperties = providerProperties;
448 }
449
450 public Connection getConnection() throws Exception
451 {
452 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
453 if (tx != null)
454 {
455 if (tx.hasResource(dataSource))
456 {
457 logger.debug("Retrieving connection from current transaction");
458 return (Connection)tx.getResource(dataSource);
459 }
460 }
461 logger.debug("Retrieving new connection from data source");
462 Connection con = dataSource.getConnection();
463
464 if (tx != null)
465 {
466 logger.debug("Binding connection to current transaction");
467 try
468 {
469 tx.bindResource(dataSource, con);
470 }
471 catch (TransactionException e)
472 {
473 JdbcUtils.close(con);
474 throw new RuntimeException("Could not bind connection to current transaction", e);
475 }
476 }
477 return con;
478 }
479
480
481
482
483 public String getResultSetHandler()
484 {
485 return this.resultSetHandler;
486 }
487
488
489
490
491 public void setResultSetHandler(String resultSetHandler)
492 {
493 this.resultSetHandler = resultSetHandler;
494 }
495
496
497
498
499
500 protected ResultSetHandler createResultSetHandler()
501 {
502 try
503 {
504 return (ResultSetHandler) ClassUtils.instanciateClass(getResultSetHandler(),
505 ClassUtils.NO_ARGS);
506 }
507 catch (Exception e)
508 {
509 throw new IllegalArgumentException("Error creating instance of the resultSetHandler class :"
510 + getResultSetHandler() + System.getProperty("line.separator")
511 + ExceptionUtils.getFullStackTrace(e));
512 }
513 }
514
515 public Set getQueryValueExtractors()
516 {
517 return queryValueExtractors;
518 }
519
520 public void setQueryValueExtractors(Set queryValueExtractors)
521 {
522 this.queryValueExtractors = queryValueExtractors;
523 }
524
525
526
527
528 public String getQueryRunner()
529 {
530 return this.queryRunner;
531 }
532
533
534
535
536 public void setQueryRunner(String queryRunner)
537 {
538 this.queryRunner = queryRunner;
539 }
540
541
542
543
544
545 protected QueryRunner createQueryRunner()
546 {
547 try
548 {
549 return (QueryRunner) ClassUtils.instanciateClass(getQueryRunner(),
550 ClassUtils.NO_ARGS);
551 }
552 catch (Exception e)
553 {
554 throw new IllegalArgumentException("Error creating instance of the queryRunner class :"
555 + getQueryRunner() + System.getProperty("line.separator")
556 + ExceptionUtils.getFullStackTrace(e));
557 }
558 }
559
560
561
562
563
564
565
566
567
568 public String parseStatement(String stmt, List params)
569 {
570 if (stmt == null)
571 {
572 return stmt;
573 }
574 Matcher m = STATEMENT_ARGS.matcher(stmt);
575 StringBuffer sb = new StringBuffer(200);
576 while (m.find())
577 {
578 String key = m.group();
579 m.appendReplacement(sb, "?");
580 params.add(key);
581 }
582 m.appendTail(sb);
583 return sb.toString();
584 }
585
586 public Object[][] getParamsTypes(List params)
587 {
588 Object[][] result = new Object[params.size()][3];
589 int k = 0;
590 for (Iterator iter = params.iterator(); iter.hasNext(); k++)
591 {
592 String param = (String) iter.next();
593 param = param.substring(2, param.length() - 1);
594 StringTokenizer tokenizer = new StringTokenizer(param, ";");
595 int i = 0;
596 while (tokenizer.hasMoreTokens())
597 {
598 result[k][i] = tokenizer.nextToken();
599 i++;
600 if (i > 2)
601 {
602 break;
603 }
604 }
605 }
606 return result;
607 }
608
609 public Object[] getParams(UMOImmutableEndpoint endpoint, List paramNames, Object message, String query)
610 throws Exception
611 {
612
613 Object[] params = new Object[paramNames.size()];
614 for (int i = 0; i < paramNames.size(); i++)
615 {
616 String param = (String)paramNames.get(i);
617 String name;
618 int idx;
619 if ((idx = param.indexOf(";")) != -1)
620 {
621 name = param.substring(2, idx);
622 }
623 else
624 {
625 name = param.substring(2, param.length() - 1);
626 }
627 Object value = null;
628
629 boolean foundValue = false;
630 if (message != null)
631 {
632 PropertyExtractor pe = (PropertyExtractor) propertyExtractorCache.get(query + param);
633 if (pe == null)
634 {
635 for (Iterator iterator = propertyExtractors.iterator(); iterator.hasNext();)
636 {
637 pe = (PropertyExtractor) iterator.next();
638 value = pe.getProperty(name, message);
639 if (value != null)
640 {
641 foundValue = true;
642 propertyExtractorCache.putIfAbsent(query + param, pe);
643 break;
644 }
645 }
646 }
647 else
648 {
649 value = pe.getProperty(name, message);
650 foundValue = true;
651 }
652 if (StringUtils.EMPTY.equals(value) && pe instanceof BeanPropertyExtractor)
653 {
654 value = null;
655 }
656 }
657 if (!foundValue)
658 {
659 value = endpoint.getProperty(name);
660 }
661
662
663
664
665
666
667
668
669
670 params[i] = value;
671 }
672 return params;
673 }
674 }