1
2
3
4
5
6
7
8
9
10
11 package org.mule.transport.jdbc;
12
13 import org.mule.DefaultMuleMessage;
14 import org.mule.api.MuleMessage;
15 import org.mule.api.construct.FlowConstruct;
16 import org.mule.api.endpoint.InboundEndpoint;
17 import org.mule.api.lifecycle.CreateException;
18 import org.mule.api.transaction.Transaction;
19 import org.mule.api.transport.Connector;
20 import org.mule.transaction.TransactionCoordination;
21 import org.mule.transaction.XaTransactionFactory;
22 import org.mule.transport.TransactedPollingMessageReceiver;
23 import org.mule.transport.jdbc.i18n.JdbcMessages;
24 import org.mule.util.ArrayUtils;
25 import org.mule.util.MapUtils;
26
27 import java.sql.Connection;
28 import java.util.ArrayList;
29 import java.util.LinkedList;
30 import java.util.List;
31
32
33
34
35
36 public class JdbcMessageReceiver extends TransactedPollingMessageReceiver
37 {
38
39 public static final String RECEIVE_MESSAGE_IN_TRANSCTION = "receiveMessageInTransaction";
40 public static final String RECEIVE_MESSAGES_IN_XA_TRANSCTION = "receiveMessagesInXaTransaction";
41
42 protected JdbcConnector connector;
43 protected String readStmt;
44 protected String ackStmt;
45 protected List readParams;
46 protected List ackParams;
47 public boolean receiveMessagesInXaTransaction = false;
48 private volatile boolean aggregateResult;
49
50 public JdbcMessageReceiver(Connector connector,
51 FlowConstruct flowConstruct,
52 InboundEndpoint endpoint,
53 String readStmt,
54 String ackStmt) throws CreateException
55 {
56 super(connector, flowConstruct, endpoint);
57 this.setFrequency(((JdbcConnector) connector).getPollingFrequency());
58
59 boolean transactedEndpoint = endpoint.getTransactionConfig().isTransacted();
60 boolean xaTransactedEndpoint = (transactedEndpoint &&
61 endpoint.getTransactionConfig().getFactory() instanceof XaTransactionFactory);
62
63 boolean receiveMessageInTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
64 RECEIVE_MESSAGE_IN_TRANSCTION, false);
65 this.setReceiveMessagesInTransaction(receiveMessageInTransaction && transactedEndpoint);
66 if (receiveMessageInTransaction && !transactedEndpoint)
67 {
68 logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, "transaction"));
69 receiveMessageInTransaction = false;
70 }
71
72 receiveMessagesInXaTransaction = MapUtils.getBooleanValue(endpoint.getProperties(),
73 RECEIVE_MESSAGES_IN_XA_TRANSCTION, false);
74 if (receiveMessagesInXaTransaction && !receiveMessageInTransaction)
75 {
76 logger.warn(JdbcMessages.forceProperty(RECEIVE_MESSAGES_IN_XA_TRANSCTION, RECEIVE_MESSAGE_IN_TRANSCTION));
77 receiveMessagesInXaTransaction = false;
78 }
79 else if (receiveMessagesInXaTransaction && isReceiveMessagesInTransaction() && !xaTransactedEndpoint)
80 {
81 logger.warn(JdbcMessages.forcePropertyNoTransaction(RECEIVE_MESSAGES_IN_XA_TRANSCTION, "XA transaction"));
82 receiveMessagesInXaTransaction = false;
83 }
84
85 this.connector = (JdbcConnector) connector;
86 this.setReceiveMessagesInTransaction(endpoint.getTransactionConfig().isTransacted()
87 && !this.connector.isTransactionPerMessage());
88
89 parseStatements(readStmt, ackStmt);
90 }
91
92
93
94
95 protected void parseStatements(String readStmt, String ackStmt)
96 {
97 this.readParams = new ArrayList();
98 this.readStmt = this.connector.parseStatement(readStmt, this.readParams);
99 this.ackParams = new ArrayList();
100 this.ackStmt = this.connector.parseStatement(ackStmt, this.ackParams);
101 }
102
103 @Override
104 protected void doDispose()
105 {
106
107 }
108
109 @Override
110 protected void doConnect() throws Exception
111 {
112
113 }
114
115 @Override
116 protected void doDisconnect() throws Exception
117 {
118
119 }
120
121 @Override
122 public void processMessage(Object message) throws Exception
123 {
124 Connection con = null;
125 Transaction tx = TransactionCoordination.getInstance().getTransaction();
126 try
127 {
128 con = this.connector.getConnection();
129 MuleMessage muleMessage = createMuleMessage(message, endpoint.getEncoding());
130 if (hasAckStatement())
131 {
132 if (aggregateResult)
133 {
134 List<MuleMessage> messages = createMuleMessages((List) message);
135 int[] nbRows = executeBatchAckStatement(con, messages);
136
137 if (nbRows[0] == 0)
138 {
139 logger.warn(".ack statement did not update any rows");
140 }
141
142 aggregateResult = false;
143 }
144 else
145 {
146 int nbRows = executeAckStatement(con, muleMessage);
147 if (nbRows == 0)
148 {
149 logger.warn(".ack statement did not update any rows");
150 }
151 }
152 }
153 routeMessage(muleMessage);
154
155 }
156 catch (Exception ex)
157 {
158 if (tx != null)
159 {
160 tx.setRollbackOnly();
161 }
162
163
164 throw ex;
165 }
166 finally
167 {
168 if (tx == null || tx.isXA())
169 {
170
171
172
173
174
175
176
177
178
179
180
181 JdbcUtils.close(con);
182 }
183 }
184 }
185
186 protected boolean hasAckStatement()
187 {
188 return this.ackStmt != null;
189 }
190
191
192
193
194
195
196
197 protected List<MuleMessage> createMuleMessages(List<Object> records)
198 {
199 List<MuleMessage> messages = new LinkedList<MuleMessage>();
200 for (Object record : records)
201 {
202 messages.add(new DefaultMuleMessage(record, connector.getMuleContext()));
203 }
204
205 return messages;
206 }
207
208
209
210
211
212
213
214
215
216 protected int executeAckStatement(Connection con, MuleMessage muleMessage)
217 throws Exception
218 {
219 Object[] paramValues = connector.getParams(endpoint, this.ackParams, muleMessage, this.endpoint.getEndpointURI().getAddress());
220 if (logger.isDebugEnabled())
221 {
222 logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(paramValues));
223 }
224 int nbRows = connector.getQueryRunnerFor(endpoint).update(con, this.ackStmt, paramValues);
225 return nbRows;
226 }
227
228
229
230
231
232
233
234
235
236 protected int[] executeBatchAckStatement(Connection con, List<MuleMessage> messages)
237 throws Exception
238 {
239 Object[][] paramValuesArray = new Object[messages.size()][];
240
241 for (int i = 0; i < messages.size(); i++)
242 {
243 MuleMessage message = messages.get(i);
244 paramValuesArray[i] = connector.getParams(endpoint, this.ackParams, message, this.endpoint.getEndpointURI().getAddress());
245 }
246
247 if (logger.isDebugEnabled())
248 {
249 logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(ackParams));
250 }
251
252 int[] nbRows = connector.getQueryRunnerFor(endpoint).batch(con, this.ackStmt, paramValuesArray);
253
254 return nbRows;
255 }
256
257 public List getMessages() throws Exception
258 {
259 Connection con = null;
260 try
261 {
262 con = this.connector.getConnection();
263
264 List resultList = executeReadStatement(con);
265 if (resultList != null && resultList.size() > 1 && isReceiveMessagesInTransaction() && !receiveMessagesInXaTransaction)
266 {
267 aggregateResult = true;
268 logger.warn(JdbcMessages.moreThanOneMessageInTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, RECEIVE_MESSAGES_IN_XA_TRANSCTION));
269 List singleResultList = new ArrayList(1);
270 singleResultList.add(resultList);
271 return singleResultList;
272 }
273
274 return resultList;
275 }
276 finally
277 {
278 if (TransactionCoordination.getInstance().getTransaction() == null)
279 {
280 JdbcUtils.close(con);
281 }
282 }
283 }
284
285
286
287
288
289
290
291
292 protected List executeReadStatement(Connection con) throws Exception
293 {
294 Object[] readParams = connector.getParams(endpoint, this.readParams, null, this.endpoint.getEndpointURI().getAddress());
295 if (logger.isDebugEnabled())
296 {
297 logger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(readParams));
298 }
299 Object results = connector.getQueryRunnerFor(endpoint).query(con, this.readStmt, readParams,
300 connector.getResultSetHandler());
301
302 return (List) results;
303 }
304 }