Coverage Report - org.mule.providers.jdbc.JdbcMessageDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcMessageDispatcher
74%
92/125
60%
43/72
3.714
 
 1  
 /*
 2  
  * $Id: JdbcMessageDispatcher.java 11728 2008-05-13 07:31:11Z dirk.olmes $
 3  
  * --------------------------------------------------------------------------------------
 4  
  * Copyright (c) MuleSource, Inc.  All rights reserved.  http://www.mulesource.com
 5  
  *
 6  
  * The software in this package is published under the terms of the CPAL v1.0
 7  
  * license, a copy of which has been included with this distribution in the
 8  
  * LICENSE.txt file.
 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  
  * The Jdbc Message dispatcher is responsible for executing SQL queries against a
 33  
  * database.
 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  18
     private Map typesMap = new HashMap();
 46  
 
 47  
     public JdbcMessageDispatcher(UMOImmutableEndpoint endpoint)
 48  
     {
 49  18
         super(endpoint);
 50  18
         this.connector = (JdbcConnector)endpoint.getConnector();
 51  18
         registerType("int", Types.INTEGER);
 52  18
         registerType("float", Types.FLOAT);
 53  18
         registerType("double", Types.DOUBLE);
 54  18
         registerType("string", Types.VARCHAR);
 55  18
     }
 56  
 
 57  
     protected int getType(String key)
 58  
     {
 59  30
         Integer type = (Integer) typesMap.get(key);
 60  30
         return type != null ? type.intValue() : 0;
 61  
     }
 62  
 
 63  
     protected void registerType(String type, int jdbcType)
 64  
     {
 65  72
         typesMap.put(type, new Integer(jdbcType));
 66  72
     }
 67  
 
 68  
     /*
 69  
      * (non-Javadoc)
 70  
      * 
 71  
      * @see org.mule.providers.AbstractMessageDispatcher#doDispose()
 72  
      */
 73  
     protected void doDispose()
 74  
     {
 75  
         // template method
 76  18
     }
 77  
 
 78  
     protected boolean isProcWithOutParams(Object[][] types)
 79  
     {
 80  20
         for (int i = 0; i < types.length; i++)
 81  
         {
 82  20
             String type = (String) types[i][2];
 83  20
             if (INOUT.equalsIgnoreCase(type) || OUT.equalsIgnoreCase(type))
 84  
             {
 85  10
                 return true;
 86  
             }
 87  
         }
 88  0
         return false;
 89  
     }
 90  
 
 91  
     protected UMOMessage executeWriteStatement(UMOEvent event, String writeStmt) throws Exception
 92  
     {
 93  12
         List paramNames = new ArrayList();
 94  12
         writeStmt = connector.parseStatement(writeStmt, paramNames);
 95  
 
 96  12
         Object[][] types = connector.getParamsTypes(paramNames);
 97  
 
 98  12
         Object[] paramValues = connector.getParams(endpoint, paramNames, new MuleMessage(
 99  
             event.getTransformedMessage()), this.endpoint.getEndpointURI().getAddress());
 100  
 
 101  12
         UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
 102  12
         Connection con = null;
 103  12
         UMOMessage message = event.getMessage();
 104  
         try
 105  
         {
 106  12
             con = this.connector.getConnection();
 107  12
             boolean isCall = false;
 108  12
             if ("call".equalsIgnoreCase(writeStmt.substring(0, 4)))
 109  
             {
 110  10
                 writeStmt = STORED_PROCEDURE_PREFIX + writeStmt + STORED_PROCEDURE_SUFFIX;
 111  10
                 isCall = true;
 112  
             }
 113  
 
 114  12
             if (isCall && isProcWithOutParams(types))
 115  
             {
 116  10
                 CallableStatement statement = connector.getConnection().prepareCall(writeStmt);
 117  50
                 for (int i = 0; i < types.length; i++)
 118  
                 {
 119  40
                     String dataType = (String) types[i][1];
 120  40
                     String type = (String) types[i][2];
 121  40
                     if (type == null || IN.equalsIgnoreCase(type))
 122  
                     {
 123  10
                         statement.setObject(i + 1, paramValues[i]);
 124  
                     }
 125  
                     else
 126  
                     {
 127  30
                         statement.registerOutParameter(i + 1, getType(dataType));
 128  30
                         if (INOUT.equalsIgnoreCase(type))
 129  
                         {
 130  10
                             statement.setObject(i + 1, paramValues[i]);
 131  
                         }
 132  
                     }
 133  
                 }
 134  10
                 statement.execute();
 135  10
                 Map result = new HashMap();
 136  50
                 for (int i = 0; i < types.length; i++)
 137  
                 {
 138  40
                     Object name = types[i][0];
 139  40
                     String type = (String) types[i][2];
 140  40
                     if (INOUT.equalsIgnoreCase(type) || OUT.equalsIgnoreCase(type))
 141  
                     {
 142  30
                         result.put(name, statement.getObject(i + 1));
 143  
                     }
 144  
                 }
 145  10
                 UMOMessageAdapter msgAdapter = this.connector.getMessageAdapter(result);
 146  10
                 message = new MuleMessage(msgAdapter);
 147  10
             }
 148  
             else
 149  
             {
 150  2
                 int nbRows = connector.createQueryRunner().update(con, writeStmt, paramValues);
 151  2
                 if (nbRows != 1)
 152  
                 {
 153  0
                     logger.warn("Row count for write should be 1 and not " + nbRows);
 154  
                 }
 155  
             }
 156  12
             if (tx == null)
 157  
             {
 158  12
                 JdbcUtils.commitAndClose(con);
 159  
             }
 160  12
             logger.debug("Event dispatched succesfuly");
 161  
         }
 162  0
         catch (Exception e)
 163  
         {
 164  0
             logger.debug("Error dispatching event: " + e.getMessage(), e);
 165  0
             if (tx == null)
 166  
             {
 167  0
                 JdbcUtils.rollbackAndClose(con);
 168  
             }
 169  0
             throw e;
 170  12
         }
 171  12
         return message;
 172  
     }
 173  
     
 174  
     protected String getStatement(UMOImmutableEndpoint endpoint)
 175  
     {
 176  16
         String writeStmt = endpoint.getEndpointURI().getAddress();
 177  
         String str;
 178  16
         if ((str = this.connector.getQuery(endpoint, writeStmt)) != null)
 179  
         { 
 180  16
             writeStmt = str;
 181  
         }
 182  16
         writeStmt = StringUtils.trimToEmpty(writeStmt);
 183  16
         if (StringUtils.isBlank(writeStmt))
 184  
         {
 185  0
             throw new IllegalArgumentException("Missing statement");
 186  
         }
 187  
         
 188  16
         return writeStmt;
 189  
     }
 190  
     
 191  
     protected boolean isWriteStatement(String writeStmt)
 192  
     {
 193  16
         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  4
             return false;
 200  
         }
 201  
         
 202  12
         return true;
 203  
     }
 204  
 
 205  
     /*
 206  
      * (non-Javadoc)
 207  
      * 
 208  
      * @see org.mule.providers.AbstractMessageDispatcher#doDispatch(org.mule.umo.UMOEvent)
 209  
      */
 210  
     protected void doDispatch(UMOEvent event) throws Exception
 211  
     {
 212  0
         if (logger.isDebugEnabled())
 213  
         {
 214  0
             logger.debug("Dispatch event: " + event);
 215  
         }
 216  
         
 217  0
         String writeStmt = getStatement(event.getEndpoint());
 218  
         
 219  0
         if (!isWriteStatement(writeStmt))
 220  
         {
 221  0
             throw new IllegalArgumentException(
 222  
                 "Write statement should be an insert / update / delete / merge sql statement, or a stored-procedure call");
 223  
         }
 224  
         
 225  0
         this.executeWriteStatement(event, writeStmt);
 226  
         
 227  0
     }
 228  
 
 229  
     /*
 230  
      * (non-Javadoc)
 231  
      * 
 232  
      * @see org.mule.providers.AbstractMessageDispatcher#doSend(org.mule.umo.UMOEvent)
 233  
      */
 234  
     protected UMOMessage doSend(UMOEvent event) throws Exception
 235  
     {
 236  16
         String statement = getStatement(event.getEndpoint());
 237  
         
 238  16
         if (isWriteStatement(statement))
 239  
         {
 240  12
             return executeWriteStatement(event, statement);
 241  
         }
 242  
         
 243  4
         return doReceive(event.getTimeout(),event);
 244  
         
 245  
     }
 246  
 
 247  
     /**
 248  
      * Make a specific request to the underlying transport
 249  
      * 
 250  
      * @param timeout the maximum time the operation should block before returning.
 251  
      *            The call should return immediately if there is data available. If
 252  
      *            no data becomes available before the timeout elapses, null will be
 253  
      *            returned
 254  
      * @return the result of the request wrapped in a UMOMessage object. Null will be
 255  
      *         returned if no data was available
 256  
      * @throws Exception if the call to the underlying protocol causes an exception
 257  
      */
 258  
     protected UMOMessage doReceive(long timeout) throws Exception
 259  
     {
 260  2
         return doReceive(timeout, null);
 261  
     }
 262  
 
 263  
     /**
 264  
      * Make a specific request to the underlying transport
 265  
      * Special case: The event is need when doReceive was called from doSend
 266  
      * @param timeout only for compatibility with doReceive(long timeout)
 267  
      * @param event There is a need to get params from message
 268  
      * @return the result of the request wrapped in a UMOMessage object. Null will be
 269  
      *         returned if no data was available
 270  
      * @throws Exception if the call to the underlying protocol causes an exception
 271  
      */
 272  
     protected UMOMessage doReceive(long timeout, UMOEvent event) throws Exception
 273  
     {
 274  6
         if (logger.isDebugEnabled())
 275  
         {
 276  0
             logger.debug("Trying to receive a message with a timeout of " + timeout);
 277  
         }
 278  
 
 279  6
         String[] stmts = this.connector.getReadAndAckStatements(endpoint);
 280  6
         String readStmt = stmts[0];
 281  6
         String ackStmt = stmts[1];
 282  6
         List readParams = new ArrayList();
 283  6
         List ackParams = new ArrayList();
 284  6
         readStmt = connector.parseStatement(readStmt, readParams);
 285  6
         ackStmt = connector.parseStatement(ackStmt, ackParams);
 286  
 
 287  6
         Connection con = null;
 288  6
         long t0 = System.currentTimeMillis();
 289  
         try
 290  
         {
 291  6
             con = this.connector.getConnection();
 292  6
             if (timeout < 0)
 293  
             {
 294  0
                 timeout = Long.MAX_VALUE;
 295  
             }
 296  
             Object result;
 297  
             do
 298  
             {
 299  6
                 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  6
                 if (result != null)
 306  
                 {
 307  6
                     if (logger.isDebugEnabled())
 308  
                     {
 309  0
                         logger.debug("Received: " + result);
 310  
                     }
 311  
                     break;
 312  
                 }
 313  0
                 long sleep = Math.min(this.connector.getPollingFrequency(),
 314  
                                       timeout - (System.currentTimeMillis() - t0));
 315  0
                 if (sleep > 0)
 316  
                 {
 317  0
                     if (logger.isDebugEnabled())
 318  
                     {
 319  0
                         logger.debug("No results, sleeping for " + sleep);
 320  
                     }
 321  0
                     Thread.sleep(sleep);
 322  
                 }
 323  
                 else
 324  
                 {
 325  0
                     logger.debug("Timeout");
 326  0
                     JdbcUtils.rollbackAndClose(con);
 327  0
                     return null;
 328  
                 }
 329  
             }
 330  0
             while (true);
 331  6
             if (ackStmt != null)
 332  
             {
 333  0
                 int nbRows = connector.createQueryRunner().update(con, ackStmt,
 334  
                                                                   connector.getParams(endpoint, ackParams, result, ackStmt));
 335  0
                 if (nbRows != 1)
 336  
                 {
 337  0
                     logger.warn("Row count for ack should be 1 and not " + nbRows);
 338  
                 }
 339  
             }
 340  6
             UMOMessageAdapter msgAdapter = this.connector.getMessageAdapter(result);
 341  6
             UMOMessage message = new MuleMessage(msgAdapter);
 342  6
             JdbcUtils.commitAndClose(con);
 343  6
             return message;
 344  
         }
 345  0
         catch (Exception e)
 346  
         {
 347  0
             JdbcUtils.rollbackAndClose(con);
 348  0
             throw e;
 349  
         }
 350  
 
 351  
     }
 352  
 
 353  
 
 354  
     protected void doConnect() throws Exception
 355  
     {
 356  
         // template method
 357  18
     }
 358  
 
 359  
     protected void doDisconnect() throws Exception
 360  
     {
 361  
         // template method
 362  18
     }
 363  
 
 364  
 }