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<Object>();
98 this.readStmt = this.connector.parseStatement(readStmt, this.readParams);
99 this.ackParams = new ArrayList<Object>();
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 MuleMessage muleMessage = createMuleMessage(message, endpoint.getEncoding());
129 routeMessage(muleMessage);
130 if (hasAckStatement())
131 {
132 con = this.connector.getConnection();
133
134 if (aggregateResult)
135 {
136 List<MuleMessage> messages = createMuleMessages((List) message);
137 int[] nbRows = executeBatchAckStatement(con, messages);
138
139 if (nbRows[0] == 0)
140 {
141 logger.warn(".ack statement did not update any rows");
142 }
143
144 aggregateResult = false;
145 }
146 else
147 {
148 int nbRows = executeAckStatement(con, muleMessage);
149 if (nbRows == 0)
150 {
151 logger.warn(".ack statement did not update any rows");
152 }
153 }
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)
169 {
170
171
172
173 JdbcUtils.close(con);
174 }
175 }
176 }
177
178 protected boolean hasAckStatement()
179 {
180 return this.ackStmt != null;
181 }
182
183
184
185
186
187
188
189 protected List<MuleMessage> createMuleMessages(List<Object> records)
190 {
191 List<MuleMessage> messages = new LinkedList<MuleMessage>();
192 for (Object record : records)
193 {
194 messages.add(new DefaultMuleMessage(record, connector.getMuleContext()));
195 }
196
197 return messages;
198 }
199
200
201
202
203
204
205
206
207
208 protected int executeAckStatement(Connection con, MuleMessage muleMessage)
209 throws Exception
210 {
211 Object[] paramValues = connector.getParams(endpoint, this.ackParams, muleMessage, this.endpoint.getEndpointURI().getAddress());
212 if (logger.isDebugEnabled())
213 {
214 logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(paramValues));
215 }
216 int nbRows = connector.getQueryRunnerFor(endpoint).update(con, this.ackStmt, paramValues);
217 return nbRows;
218 }
219
220
221
222
223
224
225
226
227
228 protected int[] executeBatchAckStatement(Connection con, List<MuleMessage> messages)
229 throws Exception
230 {
231 Object[][] paramValuesArray = new Object[messages.size()][];
232
233 for (int i = 0; i < messages.size(); i++)
234 {
235 MuleMessage message = messages.get(i);
236 paramValuesArray[i] = connector.getParams(endpoint, this.ackParams, message, this.endpoint.getEndpointURI().getAddress());
237 }
238
239 if (logger.isDebugEnabled())
240 {
241 logger.debug("SQL UPDATE: " + ackStmt + ", params = " + ArrayUtils.toString(ackParams));
242 }
243
244 int[] nbRows = connector.getQueryRunnerFor(endpoint).batch(con, this.ackStmt, paramValuesArray);
245
246 return nbRows;
247 }
248
249 @Override
250 public List getMessages() throws Exception
251 {
252 if (!flowConstruct.getMuleContext().isPrimaryPollingInstance())
253 {
254 return null;
255 }
256 Connection con = null;
257 try
258 {
259 con = this.connector.getConnection();
260
261 List resultList = executeReadStatement(con);
262 if (resultList != null && resultList.size() > 1 && isReceiveMessagesInTransaction() && !receiveMessagesInXaTransaction)
263 {
264 aggregateResult = true;
265 logger.warn(JdbcMessages.moreThanOneMessageInTransaction(RECEIVE_MESSAGE_IN_TRANSCTION, RECEIVE_MESSAGES_IN_XA_TRANSCTION));
266 List singleResultList = new ArrayList(1);
267 singleResultList.add(resultList);
268 return singleResultList;
269 }
270
271 return resultList;
272 }
273 finally
274 {
275 if (TransactionCoordination.getInstance().getTransaction() == null)
276 {
277 JdbcUtils.close(con);
278 }
279 }
280 }
281
282
283
284
285
286
287
288
289 protected List executeReadStatement(Connection con) throws Exception
290 {
291 Object[] readParams = connector.getParams(endpoint, this.readParams, null, this.endpoint.getEndpointURI().getAddress());
292 if (logger.isDebugEnabled())
293 {
294 logger.debug("SQL QUERY: " + readStmt + ", params = " + ArrayUtils.toString(readParams));
295 }
296 Object results = connector.getQueryRunnerFor(endpoint).query(con, this.readStmt, readParams,
297 connector.getResultSetHandler());
298
299 return (List) results;
300 }
301 }