1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jdbc;
12
13 import org.mule.api.MuleContext;
14 import org.mule.api.MuleException;
15 import org.mule.api.MuleMessage;
16 import org.mule.api.construct.FlowConstruct;
17 import org.mule.api.endpoint.ImmutableEndpoint;
18 import org.mule.api.endpoint.InboundEndpoint;
19 import org.mule.api.expression.ExpressionManager;
20 import org.mule.api.lifecycle.InitialisationException;
21 import org.mule.api.retry.RetryContext;
22 import org.mule.api.transaction.Transaction;
23 import org.mule.api.transaction.TransactionException;
24 import org.mule.api.transport.MessageReceiver;
25 import org.mule.config.ExceptionHelper;
26 import org.mule.config.i18n.MessageFactory;
27 import org.mule.transaction.TransactionCoordination;
28 import org.mule.transport.AbstractConnector;
29 import org.mule.transport.ConnectException;
30 import org.mule.transport.jdbc.sqlstrategy.DefaultSqlStatementStrategyFactory;
31 import org.mule.transport.jdbc.sqlstrategy.SqlStatementStrategyFactory;
32 import org.mule.transport.jdbc.xa.DataSourceWrapper;
33 import org.mule.util.StringUtils;
34 import org.mule.util.TemplateParser;
35
36 import java.sql.Connection;
37 import java.text.MessageFormat;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.regex.Matcher;
41 import java.util.regex.Pattern;
42
43 import javax.sql.DataSource;
44 import javax.sql.XADataSource;
45
46 import org.apache.commons.dbutils.QueryRunner;
47 import org.apache.commons.dbutils.ResultSetHandler;
48
49 public class JdbcConnector extends AbstractConnector
50 {
51 public static final String JDBC = "jdbc";
52
53
54
55 public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
56 public static final long DEFAULT_POLLING_FREQUENCY = 1000;
57
58 private static final Pattern STATEMENT_ARGS = TemplateParser.WIGGLY_MULE_TEMPLATE_PATTERN;
59
60 private SqlStatementStrategyFactory sqlStatementStrategyFactory = new DefaultSqlStatementStrategyFactory();
61
62
63
64 static
65 {
66 ExceptionHelper.registerExceptionReader(new SQLExceptionReader());
67 }
68
69 protected long pollingFrequency = 0;
70 protected Map queries;
71
72 protected DataSource dataSource;
73 protected ResultSetHandler resultSetHandler;
74 protected QueryRunner queryRunner;
75
76 private int queryTimeout;
77
78
79
80
81
82 protected boolean transactionPerMessage = true;
83
84 public JdbcConnector(MuleContext context)
85 {
86 super(context);
87 }
88
89 protected void doInitialise() throws InitialisationException
90 {
91 createMultipleTransactedReceivers = false;
92
93 if (dataSource == null)
94 {
95 throw new InitialisationException(MessageFactory.createStaticMessage("Missing data source"), this);
96 }
97 if (resultSetHandler == null)
98 {
99 resultSetHandler = new org.apache.commons.dbutils.handlers.MapListHandler();
100 }
101 if (queryRunner == null)
102 {
103 if (this.queryTimeout >= 0)
104 {
105 queryRunner = new ExtendedQueryRunner(dataSource, this.queryTimeout);
106 }
107 else
108 {
109 queryRunner = new QueryRunner();
110 }
111 }
112 }
113
114 public MessageReceiver createReceiver(FlowConstruct flowConstruct, InboundEndpoint endpoint) throws Exception
115 {
116 Map props = endpoint.getProperties();
117 if (props != null)
118 {
119 String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);
120 if (tempPolling != null)
121 {
122 pollingFrequency = Long.parseLong(tempPolling);
123 }
124 }
125
126 if (pollingFrequency <= 0)
127 {
128 pollingFrequency = DEFAULT_POLLING_FREQUENCY;
129 }
130
131 String[] params = getReadAndAckStatements(endpoint);
132 return getServiceDescriptor().createMessageReceiver(this, flowConstruct, endpoint, params);
133 }
134
135 public String[] getReadAndAckStatements(ImmutableEndpoint endpoint)
136 {
137 String str;
138
139
140 String readStmt;
141 if ((str = (String) endpoint.getProperty("sql")) != null)
142 {
143 readStmt = str;
144 }
145 else
146 {
147 readStmt = endpoint.getEndpointURI().getAddress();
148 }
149
150
151 String ackStmt;
152 if ((str = (String) endpoint.getProperty("ack")) != null)
153 {
154 ackStmt = str;
155 if ((str = getQuery(endpoint, ackStmt)) != null)
156 {
157 ackStmt = str;
158 }
159 ackStmt = ackStmt.trim();
160 }
161 else
162 {
163 ackStmt = readStmt + ".ack";
164 if ((str = getQuery(endpoint, ackStmt)) != null)
165 {
166 ackStmt = str.trim();
167 }
168 else
169 {
170 ackStmt = null;
171 }
172 }
173
174
175 if ((str = getQuery(endpoint, readStmt)) != null)
176 {
177 readStmt = str;
178 }
179 if (readStmt == null)
180 {
181 throw new IllegalArgumentException("Read statement should not be null");
182 }
183 else
184 {
185
186 readStmt = readStmt.trim();
187 }
188
189 if (!"select".equalsIgnoreCase(readStmt.substring(0, 6)) && !"call".equalsIgnoreCase(readStmt.substring(0, 4)))
190 {
191 throw new IllegalArgumentException("Read statement should be a select sql statement or a stored procedure");
192 }
193 if (ackStmt != null)
194 {
195 if (!"insert".equalsIgnoreCase(ackStmt.substring(0, 6))
196 && !"update".equalsIgnoreCase(ackStmt.substring(0, 6))
197 && !"delete".equalsIgnoreCase(ackStmt.substring(0, 6)))
198 {
199 throw new IllegalArgumentException(
200 "Ack statement should be an insert / update / delete sql statement");
201 }
202 }
203 return new String[]{readStmt, ackStmt};
204 }
205
206 public String getQuery(ImmutableEndpoint endpoint, String stmt)
207 {
208 Object query = null;
209 if (endpoint != null && endpoint.getProperties() != null)
210 {
211 Object queries = endpoint.getProperties().get("queries");
212 if (queries instanceof Map)
213 {
214 query = ((Map) queries).get(stmt);
215 }
216 }
217 if (query == null)
218 {
219 if (this.queries != null)
220 {
221 query = this.queries.get(stmt);
222 }
223 }
224 return query == null ? null : query.toString();
225 }
226
227 public Connection getConnection() throws Exception
228 {
229 Transaction tx = TransactionCoordination.getInstance().getTransaction();
230 if (tx != null)
231 {
232 if (tx.hasResource(dataSource))
233 {
234 logger.debug("Retrieving connection from current transaction: " + tx);
235 return (Connection) tx.getResource(dataSource);
236 }
237 }
238 logger.debug("Retrieving new connection from data source");
239
240 Connection con;
241 try
242 {
243 con = dataSource.getConnection();
244 }
245 catch (Exception e)
246 {
247 throw new ConnectException(e, this);
248 }
249
250 if (tx != null)
251 {
252 logger.debug("Binding connection " + con + " to current transaction: " + tx);
253 try
254 {
255 tx.bindResource(dataSource, con);
256 }
257 catch (TransactionException e)
258 {
259 JdbcUtils.close(con);
260 throw new RuntimeException("Could not bind connection to current transaction: " + tx, e);
261 }
262 }
263 return con;
264 }
265
266 public boolean isTransactionPerMessage()
267 {
268 return transactionPerMessage;
269 }
270
271 public void setTransactionPerMessage(boolean transactionPerMessage)
272 {
273 this.transactionPerMessage = transactionPerMessage;
274 if (!transactionPerMessage)
275 {
276 logger.warn("transactionPerMessage property is set to false so setting createMultipleTransactedReceivers " +
277 "to false also to prevent creation of multiple JdbcMessageReceivers");
278 setCreateMultipleTransactedReceivers(transactionPerMessage);
279 }
280 }
281
282
283
284
285
286
287
288
289 public String parseStatement(String stmt, List params)
290 {
291 if (stmt == null)
292 {
293 return stmt;
294 }
295 Matcher m = STATEMENT_ARGS.matcher(stmt);
296 StringBuffer sb = new StringBuffer(200);
297 while (m.find())
298 {
299 String key = m.group();
300 m.appendReplacement(sb, "?");
301
302 if (key.equals("#[payload]"))
303 {
304
305 logger.error("invalid expression template #[payload]. It should be replaced with #[payload:] to conform with the correct expression syntax. Mule has replaced this for you, but may not in future versions.");
306 key = "#[payload:]";
307 }
308 params.add(key);
309 }
310 m.appendTail(sb);
311 return sb.toString();
312 }
313
314 public Object[] getParams(ImmutableEndpoint endpoint, List paramNames, MuleMessage message, String query)
315 throws Exception
316 {
317 Object[] params = new Object[paramNames.size()];
318 for (int i = 0; i < paramNames.size(); i++)
319 {
320 String param = (String) paramNames.get(i);
321 Object value = getParamValue(endpoint, message, param);
322
323 params[i] = value;
324 }
325 return params;
326 }
327
328 protected Object getParamValue(ImmutableEndpoint endpoint, MuleMessage message, String param)
329 {
330 Object value = null;
331
332 boolean foundValue = false;
333 boolean validExpression = muleContext.getExpressionManager().isValidExpression(param);
334
335
336 if (message != null && validExpression)
337 {
338 value = muleContext.getExpressionManager().evaluate(param, message);
339 foundValue = value != null;
340 }
341 if (!foundValue)
342 {
343 String name = getNameFromParam(param);
344
345 if (!validExpression)
346 {
347 logger.warn(MessageFormat.format("Config is using the legacy param format {0} (no evaluator defined)." +
348 " This expression can be replaced with {1}header:{2}{3}",
349 param, ExpressionManager.DEFAULT_EXPRESSION_PREFIX,
350 name, ExpressionManager.DEFAULT_EXPRESSION_POSTFIX));
351 }
352 value = endpoint.getProperty(name);
353 }
354 return value;
355 }
356
357 protected String getNameFromParam(String param)
358 {
359 return param.substring(2, param.length() - 1);
360 }
361
362 protected void doDispose()
363 {
364
365 }
366
367 protected void doConnect() throws Exception
368 {
369
370 }
371
372
373
374
375
376 public RetryContext validateConnection(RetryContext retryContext)
377 {
378 Connection con;
379 try
380 {
381 con = getConnection();
382 if (con != null)
383 {
384 con.close();
385 }
386 retryContext.setOk();
387 }
388 catch (Exception ex)
389 {
390 retryContext.setFailed(ex);
391 }
392 finally
393 {
394 con = null;
395 }
396
397 return retryContext;
398 }
399
400 protected void doDisconnect() throws Exception
401 {
402
403 }
404
405 protected void doStart() throws MuleException
406 {
407
408 }
409
410 protected void doStop() throws MuleException
411 {
412
413 }
414
415
416
417
418
419 public String getProtocol()
420 {
421 return JDBC;
422 }
423
424 public DataSource getDataSource()
425 {
426 return dataSource;
427 }
428
429 public void setDataSource(DataSource dataSource)
430 {
431 if (dataSource instanceof XADataSource)
432 {
433 this.dataSource = new DataSourceWrapper((XADataSource) dataSource);
434 }
435 else
436 {
437 this.dataSource = dataSource;
438 }
439 }
440
441 public ResultSetHandler getResultSetHandler()
442 {
443 return resultSetHandler;
444 }
445
446 public void setResultSetHandler(ResultSetHandler resultSetHandler)
447 {
448 this.resultSetHandler = resultSetHandler;
449 }
450
451 public QueryRunner getQueryRunnerFor(ImmutableEndpoint endpoint)
452 {
453 String queryTimeoutAsString = (String) endpoint.getProperty("queryTimeout");
454 Integer queryTimeout = -1;
455
456 try
457 {
458 queryTimeout = Integer.valueOf(queryTimeoutAsString);
459 }
460 catch (NumberFormatException e)
461 {
462
463 }
464
465 if (queryTimeout >= 0)
466 {
467 ExtendedQueryRunner extendedQueryRunner = new ExtendedQueryRunner(
468 this.queryRunner.getDataSource(), queryTimeout);
469 return extendedQueryRunner;
470 }
471 else
472 {
473 return queryRunner;
474 }
475 }
476
477 public QueryRunner getQueryRunner()
478 {
479 return queryRunner;
480 }
481
482 public void setQueryRunner(QueryRunner queryRunner)
483 {
484 this.queryRunner = queryRunner;
485 }
486
487
488
489
490 public long getPollingFrequency()
491 {
492 return pollingFrequency;
493 }
494
495
496
497
498 public void setPollingFrequency(long pollingFrequency)
499 {
500 this.pollingFrequency = pollingFrequency;
501 }
502
503
504
505
506 public Map getQueries()
507 {
508 return queries;
509 }
510
511
512
513
514 public void setQueries(Map queries)
515 {
516 this.queries = queries;
517 }
518
519 public SqlStatementStrategyFactory getSqlStatementStrategyFactory()
520 {
521 return sqlStatementStrategyFactory;
522 }
523
524 public void setSqlStatementStrategyFactory(SqlStatementStrategyFactory sqlStatementStrategyFactory)
525 {
526 this.sqlStatementStrategyFactory = sqlStatementStrategyFactory;
527 }
528
529 public String getStatement(ImmutableEndpoint endpoint)
530 {
531 String writeStmt = endpoint.getEndpointURI().getAddress();
532 String str;
533 if ((str = getQuery(endpoint, writeStmt)) != null)
534 {
535 writeStmt = str;
536 }
537 writeStmt = StringUtils.trimToEmpty(writeStmt);
538 if (StringUtils.isBlank(writeStmt))
539 {
540 throw new IllegalArgumentException("Missing statement");
541 }
542
543 return writeStmt;
544 }
545
546 public int getQueryTimeout()
547 {
548 return queryTimeout;
549 }
550
551 public void setQueryTimeout(int queryTimeout)
552 {
553 this.queryTimeout = queryTimeout;
554 }
555 }