1
2
3
4
5
6
7
8
9
10
11 package org.mule.providers.jdbc;
12
13 import org.mule.impl.MuleMessage;
14 import org.mule.providers.AbstractMessageDispatcher;
15 import org.mule.transaction.TransactionCoordination;
16 import org.mule.umo.UMOEvent;
17 import org.mule.umo.UMOMessage;
18 import org.mule.umo.UMOTransaction;
19 import org.mule.umo.endpoint.UMOImmutableEndpoint;
20 import org.mule.umo.provider.UMOMessageAdapter;
21 import org.mule.util.StringUtils;
22
23 import java.sql.CallableStatement;
24 import java.sql.Connection;
25 import java.sql.Types;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31
32
33
34
35 public class JdbcMessageDispatcher extends AbstractMessageDispatcher
36 {
37
38 private JdbcConnector connector;
39 private static final String STORED_PROCEDURE_PREFIX = "{ ";
40 private static final String STORED_PROCEDURE_SUFFIX = " }";
41 private static final String IN = "in";
42 private static final String OUT = "out";
43 private static final String INOUT = "inout";
44
45 private Map typesMap = new HashMap();
46
47 public JdbcMessageDispatcher(UMOImmutableEndpoint endpoint)
48 {
49 super(endpoint);
50 this.connector = (JdbcConnector)endpoint.getConnector();
51 registerType("int", Types.INTEGER);
52 registerType("float", Types.FLOAT);
53 registerType("double", Types.DOUBLE);
54 registerType("string", Types.VARCHAR);
55 }
56
57 protected int getType(String key)
58 {
59 Integer type = (Integer) typesMap.get(key);
60 return type != null ? type.intValue() : 0;
61 }
62
63 protected void registerType(String type, int jdbcType)
64 {
65 typesMap.put(type, new Integer(jdbcType));
66 }
67
68
69
70
71
72
73 protected void doDispose()
74 {
75
76 }
77
78 protected boolean isProcWithOutParams(Object[][] types)
79 {
80 for (int i = 0; i < types.length; i++)
81 {
82 String type = (String) types[i][2];
83 if (INOUT.equalsIgnoreCase(type) || OUT.equalsIgnoreCase(type))
84 {
85 return true;
86 }
87 }
88 return false;
89 }
90
91 protected UMOMessage executeWriteStatement(UMOEvent event, String writeStmt) throws Exception
92 {
93 List paramNames = new ArrayList();
94 writeStmt = connector.parseStatement(writeStmt, paramNames);
95
96 Object[][] types = connector.getParamsTypes(paramNames);
97
98 Object[] paramValues = connector.getParams(endpoint, paramNames, new MuleMessage(
99 event.getTransformedMessage()), this.endpoint.getEndpointURI().getAddress());
100
101 UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
102 Connection con = null;
103 UMOMessage message = event.getMessage();
104 try
105 {
106 con = this.connector.getConnection();
107 boolean isCall = false;
108 if ("call".equalsIgnoreCase(writeStmt.substring(0, 4)))
109 {
110 writeStmt = STORED_PROCEDURE_PREFIX + writeStmt + STORED_PROCEDURE_SUFFIX;
111 isCall = true;
112 }
113
114 if (isCall && isProcWithOutParams(types))
115 {
116 CallableStatement statement = connector.getConnection().prepareCall(writeStmt);
117 for (int i = 0; i < types.length; i++)
118 {
119 String dataType = (String) types[i][1];
120 String type = (String) types[i][2];
121 if (type == null || IN.equalsIgnoreCase(type))
122 {
123 statement.setObject(i + 1, paramValues[i]);
124 }
125 else
126 {
127 statement.registerOutParameter(i + 1, getType(dataType));
128 if (INOUT.equalsIgnoreCase(type))
129 {
130 statement.setObject(i + 1, paramValues[i]);
131 }
132 }
133 }
134 statement.execute();
135 Map result = new HashMap();
136 for (int i = 0; i < types.length; i++)
137 {
138 Object name = types[i][0];
139 String type = (String) types[i][2];
140 if (INOUT.equalsIgnoreCase(type) || OUT.equalsIgnoreCase(type))
141 {
142 result.put(name, statement.getObject(i + 1));
143 }
144 }
145 UMOMessageAdapter msgAdapter = this.connector.getMessageAdapter(result);
146 message = new MuleMessage(msgAdapter);
147 }
148 else
149 {
150 int nbRows = connector.createQueryRunner().update(con, writeStmt, paramValues);
151 if (nbRows != 1)
152 {
153 logger.warn("Row count for write should be 1 and not " + nbRows);
154 }
155 }
156 if (tx == null)
157 {
158 JdbcUtils.commitAndClose(con);
159 }
160 logger.debug("Event dispatched succesfuly");
161 }
162 catch (Exception e)
163 {
164 logger.debug("Error dispatching event: " + e.getMessage(), e);
165 if (tx == null)
166 {
167 JdbcUtils.rollbackAndClose(con);
168 }
169 throw e;
170 }
171 return message;
172 }
173
174 protected String getStatement(UMOImmutableEndpoint endpoint)
175 {
176 String writeStmt = endpoint.getEndpointURI().getAddress();
177 String str;
178 if ((str = this.connector.getQuery(endpoint, writeStmt)) != null)
179 {
180 writeStmt = str;
181 }
182 writeStmt = StringUtils.trimToEmpty(writeStmt);
183 if (StringUtils.isBlank(writeStmt))
184 {
185 throw new IllegalArgumentException("Missing statement");
186 }
187
188 return writeStmt;
189 }
190
191 protected boolean isWriteStatement(String writeStmt)
192 {
193 if (!"insert".equalsIgnoreCase(writeStmt.substring(0, 6))
194 && !"update".equalsIgnoreCase(writeStmt.substring(0, 6))
195 && !"delete".equalsIgnoreCase(writeStmt.substring(0, 6))
196 && !"merge".equalsIgnoreCase(writeStmt.substring(0, 5))
197 && !"call".equalsIgnoreCase(writeStmt.substring(0, 4)))
198 {
199 return false;
200 }
201
202 return true;
203 }
204
205
206
207
208
209
210 protected void doDispatch(UMOEvent event) throws Exception
211 {
212 if (logger.isDebugEnabled())
213 {
214 logger.debug("Dispatch event: " + event);
215 }
216
217 String writeStmt = getStatement(event.getEndpoint());
218
219 if (!isWriteStatement(writeStmt))
220 {
221 throw new IllegalArgumentException(
222 "Write statement should be an insert / update / delete / merge sql statement, or a stored-procedure call");
223 }
224
225 this.executeWriteStatement(event, writeStmt);
226
227 }
228
229
230
231
232
233
234 protected UMOMessage doSend(UMOEvent event) throws Exception
235 {
236 String statement = getStatement(event.getEndpoint());
237
238 if (isWriteStatement(statement))
239 {
240 return executeWriteStatement(event, statement);
241 }
242
243 return doReceive(event.getTimeout(),event);
244
245 }
246
247
248
249
250
251
252
253
254
255
256
257
258 protected UMOMessage doReceive(long timeout) throws Exception
259 {
260 return doReceive(timeout, null);
261 }
262
263
264
265
266
267
268
269
270
271
272 protected UMOMessage doReceive(long timeout, UMOEvent event) throws Exception
273 {
274 if (logger.isDebugEnabled())
275 {
276 logger.debug("Trying to receive a message with a timeout of " + timeout);
277 }
278
279 String[] stmts = this.connector.getReadAndAckStatements(endpoint);
280 String readStmt = stmts[0];
281 String ackStmt = stmts[1];
282 List readParams = new ArrayList();
283 List ackParams = new ArrayList();
284 readStmt = connector.parseStatement(readStmt, readParams);
285 ackStmt = connector.parseStatement(ackStmt, ackParams);
286
287 Connection con = null;
288 long t0 = System.currentTimeMillis();
289 try
290 {
291 con = this.connector.getConnection();
292 if (timeout < 0)
293 {
294 timeout = Long.MAX_VALUE;
295 }
296 Object result;
297 do
298 {
299 result = connector.createQueryRunner().query(con, readStmt,
300 connector.getParams(endpoint,
301 readParams,
302 event!=null ? event.getMessage() : null,
303 this.endpoint.getEndpointURI().getAddress()),
304 connector.createResultSetHandler());
305 if (result != null)
306 {
307 if (logger.isDebugEnabled())
308 {
309 logger.debug("Received: " + result);
310 }
311 break;
312 }
313 long sleep = Math.min(this.connector.getPollingFrequency(),
314 timeout - (System.currentTimeMillis() - t0));
315 if (sleep > 0)
316 {
317 if (logger.isDebugEnabled())
318 {
319 logger.debug("No results, sleeping for " + sleep);
320 }
321 Thread.sleep(sleep);
322 }
323 else
324 {
325 logger.debug("Timeout");
326 JdbcUtils.rollbackAndClose(con);
327 return null;
328 }
329 }
330 while (true);
331 if (ackStmt != null)
332 {
333 int nbRows = connector.createQueryRunner().update(con, ackStmt,
334 connector.getParams(endpoint, ackParams, result, ackStmt));
335 if (nbRows != 1)
336 {
337 logger.warn("Row count for ack should be 1 and not " + nbRows);
338 }
339 }
340 UMOMessageAdapter msgAdapter = this.connector.getMessageAdapter(result);
341 UMOMessage message = new MuleMessage(msgAdapter);
342 JdbcUtils.commitAndClose(con);
343 return message;
344 }
345 catch (Exception e)
346 {
347 JdbcUtils.rollbackAndClose(con);
348 throw e;
349 }
350
351 }
352
353
354 protected void doConnect() throws Exception
355 {
356
357 }
358
359 protected void doDisconnect() throws Exception
360 {
361
362 }
363
364 }