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