Coverage Report - org.mule.providers.jdbc.JdbcConnector
 
Classes in this File Line Coverage Branch Coverage Complexity
JdbcConnector
0%
0/171
0%
0/43
2.359
 
 1  
 /*
 2  
  * $Id: JdbcConnector.java 7976 2007-08-21 14:26:13Z 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.config.ExceptionHelper;
 14  
 import org.mule.config.i18n.CoreMessages;
 15  
 import org.mule.providers.AbstractConnector;
 16  
 import org.mule.providers.jdbc.i18n.JdbcMessages;
 17  
 import org.mule.transaction.TransactionCoordination;
 18  
 import org.mule.umo.TransactionException;
 19  
 import org.mule.umo.UMOComponent;
 20  
 import org.mule.umo.UMOException;
 21  
 import org.mule.umo.UMOTransaction;
 22  
 import org.mule.umo.endpoint.UMOEndpoint;
 23  
 import org.mule.umo.endpoint.UMOImmutableEndpoint;
 24  
 import org.mule.umo.lifecycle.InitialisationException;
 25  
 import org.mule.umo.provider.UMOMessageReceiver;
 26  
 import org.mule.util.ClassUtils;
 27  
 import org.mule.util.ExceptionUtils;
 28  
 import org.mule.util.StringUtils;
 29  
 import org.mule.util.properties.BeanPropertyExtractor;
 30  
 import org.mule.util.properties.MapPropertyExtractor;
 31  
 import org.mule.util.properties.MessagePropertyExtractor;
 32  
 import org.mule.util.properties.PayloadPropertyExtractor;
 33  
 import org.mule.util.properties.PropertyExtractor;
 34  
 
 35  
 import java.sql.Connection;
 36  
 import java.util.HashSet;
 37  
 import java.util.Hashtable;
 38  
 import java.util.Iterator;
 39  
 import java.util.List;
 40  
 import java.util.Map;
 41  
 import java.util.Set;
 42  
 import java.util.regex.Matcher;
 43  
 import java.util.regex.Pattern;
 44  
 
 45  
 import javax.naming.Context;
 46  
 import javax.naming.InitialContext;
 47  
 import javax.naming.NamingException;
 48  
 import javax.sql.DataSource;
 49  
 
 50  
 import org.apache.commons.dbutils.QueryRunner;
 51  
 import org.apache.commons.dbutils.ResultSetHandler;
 52  
 
 53  0
 public class JdbcConnector extends AbstractConnector
 54  
 {
 55  
     // These are properties that can be overridden on the Receiver by the endpoint
 56  
     // declaration
 57  
     public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
 58  
     public static final long DEFAULT_POLLING_FREQUENCY = 1000;
 59  
 
 60  
     private static final String DEFAULT_QUERY_RUNNER = "org.apache.commons.dbutils.QueryRunner";
 61  
     private static final String DEFAULT_RESULTSET_HANDLER = "org.apache.commons.dbutils.handlers.MapListHandler";
 62  
 
 63  0
     private static final Pattern STATEMENT_ARGS = Pattern.compile("\\$\\{[^\\}]*\\}");
 64  
 
 65  
     /* Register the SQL Exception reader if this class gets loaded */
 66  
     static
 67  
     {
 68  0
         ExceptionHelper.registerExceptionReader(new SQLExceptionReader());
 69  0
     }
 70  
 
 71  0
     protected long pollingFrequency = 0;
 72  
     protected DataSource dataSource;
 73  
     protected String dataSourceJndiName;
 74  
     protected Context jndiContext;
 75  
     protected String jndiInitialFactory;
 76  
     protected String jndiProviderUrl;
 77  
     protected Map providerProperties;
 78  
     protected Map queries;
 79  0
     protected String resultSetHandler = DEFAULT_RESULTSET_HANDLER;
 80  0
     protected String queryRunner = DEFAULT_QUERY_RUNNER;
 81  
     protected Set queryValueExtractors;
 82  
     protected Set propertyExtractors;
 83  
 
 84  
     protected void doInitialise() throws InitialisationException
 85  
     {
 86  
         try
 87  
         {
 88  
             // If we have a dataSource, there is no need to initialise
 89  
             // the JndiContext
 90  0
             if (dataSource == null)
 91  
             {
 92  0
                 initJndiContext();
 93  0
                 createDataSource();
 94  
             }
 95  
             // setup property Extractors for queries
 96  0
             if (queryValueExtractors == null)
 97  
             {
 98  
                 // Add defaults
 99  0
                 queryValueExtractors = new HashSet();
 100  0
                 queryValueExtractors.add(MessagePropertyExtractor.class.getName());
 101  0
                 queryValueExtractors.add(NowPropertyExtractor.class.getName());
 102  0
                 queryValueExtractors.add(PayloadPropertyExtractor.class.getName());
 103  0
                 queryValueExtractors.add(MapPropertyExtractor.class.getName());
 104  0
                 queryValueExtractors.add(BeanPropertyExtractor.class.getName());
 105  
 
 106  0
                 if (ClassUtils.isClassOnPath("org.mule.util.properties.Dom4jPropertyExtractor", getClass()))
 107  
                 {
 108  0
                     queryValueExtractors.add("org.mule.util.properties.Dom4jPropertyExtractor");
 109  
                 }
 110  
 
 111  0
                 if (ClassUtils.isClassOnPath("org.mule.util.properties.JDomPropertyExtractor", getClass()))
 112  
                 {
 113  0
                     queryValueExtractors.add("org.mule.util.properties.JDomPropertyExtractor");
 114  
                 }
 115  
             }
 116  
 
 117  0
             propertyExtractors = new HashSet();
 118  0
             for (Iterator iterator = queryValueExtractors.iterator(); iterator.hasNext();)
 119  
             {
 120  0
                 String s = (String)iterator.next();
 121  0
                 propertyExtractors.add(ClassUtils.instanciateClass(s, ClassUtils.NO_ARGS));
 122  
             }
 123  
         }
 124  0
         catch (Exception e)
 125  
         {
 126  0
             throw new InitialisationException(CoreMessages.failedToCreate("Jdbc Connector"), e, this);
 127  0
         }
 128  0
     }
 129  
 
 130  
     protected void doDispose()
 131  
     {
 132  
         // template method
 133  0
     }
 134  
 
 135  
     protected void doConnect() throws Exception
 136  
     {
 137  
         // template method
 138  0
     }
 139  
 
 140  
     protected void doDisconnect() throws Exception
 141  
     {
 142  
         // template method
 143  0
     }
 144  
 
 145  
     protected void doStart() throws UMOException
 146  
     {
 147  
         // template method
 148  0
     }
 149  
 
 150  
     protected void doStop() throws UMOException
 151  
     {
 152  
         // template method
 153  0
     }
 154  
 
 155  
     /*
 156  
      * (non-Javadoc)
 157  
      * 
 158  
      * @see org.mule.umo.provider.UMOConnector#getProtocol()
 159  
      */
 160  
     public String getProtocol()
 161  
     {
 162  0
         return "jdbc";
 163  
     }
 164  
 
 165  
     public UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint) throws Exception
 166  
     {
 167  0
         Map props = endpoint.getProperties();
 168  0
         if (props != null)
 169  
         {
 170  0
             String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);
 171  0
             if (tempPolling != null)
 172  
             {
 173  0
                 pollingFrequency = Long.parseLong(tempPolling);
 174  
             }
 175  
         }
 176  
 
 177  0
         if (pollingFrequency <= 0)
 178  
         {
 179  0
             pollingFrequency = DEFAULT_POLLING_FREQUENCY;
 180  
         }
 181  
 
 182  0
         String[] params = getReadAndAckStatements(endpoint);
 183  0
         return getServiceDescriptor().createMessageReceiver(this, component, endpoint, params);
 184  
     }
 185  
 
 186  
     protected void initJndiContext() throws NamingException
 187  
     {
 188  0
         if (this.jndiContext == null)
 189  
         {
 190  0
             Hashtable props = new Hashtable();
 191  0
             if (this.jndiInitialFactory != null)
 192  
             {
 193  0
                 props.put(Context.INITIAL_CONTEXT_FACTORY, this.jndiInitialFactory);
 194  
             }
 195  0
             if (this.jndiProviderUrl != null)
 196  
             {
 197  0
                 props.put(Context.PROVIDER_URL, jndiProviderUrl);
 198  
             }
 199  0
             if (this.providerProperties != null)
 200  
             {
 201  0
                 props.putAll(this.providerProperties);
 202  
             }
 203  0
             this.jndiContext = new InitialContext(props);
 204  
         }
 205  
 
 206  0
     }
 207  
 
 208  
     protected void createDataSource() throws InitialisationException, NamingException
 209  
     {
 210  0
         Object temp = this.jndiContext.lookup(this.dataSourceJndiName);
 211  0
         if (temp instanceof DataSource)
 212  
         {
 213  0
             dataSource = (DataSource)temp;
 214  
         }
 215  
         else
 216  
         {
 217  0
             throw new InitialisationException(
 218  
                 JdbcMessages.jndiResourceNotFound(this.dataSourceJndiName), this);
 219  
         }
 220  0
     }
 221  
 
 222  
     public String[] getReadAndAckStatements(UMOImmutableEndpoint endpoint)
 223  
     {
 224  
         String str;
 225  
         // Find read statement
 226  
         String readStmt;
 227  0
         if ((str = (String)endpoint.getProperty("sql")) != null)
 228  
         {
 229  0
             readStmt = str;
 230  
         }
 231  
         else
 232  
         {
 233  0
             readStmt = endpoint.getEndpointURI().getAddress();
 234  
         }
 235  
         // Find ack statement
 236  
         String ackStmt;
 237  0
         if ((str = (String)endpoint.getProperty("ack")) != null)
 238  
         {
 239  0
             ackStmt = str;
 240  0
             if ((str = getQuery(endpoint, ackStmt)) != null)
 241  
             {
 242  0
                 ackStmt = str;
 243  
             }
 244  
         }
 245  
         else
 246  
         {
 247  0
             ackStmt = readStmt + ".ack";
 248  0
             if ((str = getQuery(endpoint, ackStmt)) != null)
 249  
             {
 250  0
                 ackStmt = str;
 251  
             }
 252  
             else
 253  
             {
 254  0
                 ackStmt = null;
 255  
             }
 256  
         }
 257  
         // Translate both using queries map
 258  0
         if ((str = getQuery(endpoint, readStmt)) != null)
 259  
         {
 260  0
             readStmt = str;
 261  
         }
 262  0
         if (readStmt == null)
 263  
         {
 264  0
             throw new IllegalArgumentException("Read statement should not be null");
 265  
         }
 266  0
         if (!"select".equalsIgnoreCase(readStmt.substring(0, 6)))
 267  
         {
 268  0
             throw new IllegalArgumentException("Read statement should be a select sql statement");
 269  
         }
 270  0
         if (ackStmt != null)
 271  
         {
 272  0
             if (!"insert".equalsIgnoreCase(ackStmt.substring(0, 6))
 273  
                 && !"update".equalsIgnoreCase(ackStmt.substring(0, 6))
 274  
                 && !"delete".equalsIgnoreCase(ackStmt.substring(0, 6)))
 275  
             {
 276  0
                 throw new IllegalArgumentException(
 277  
                     "Ack statement should be an insert / update / delete sql statement");
 278  
             }
 279  
         }
 280  0
         return new String[]{readStmt, ackStmt};
 281  
     }
 282  
 
 283  
     public String getQuery(UMOImmutableEndpoint endpoint, String stmt)
 284  
     {
 285  0
         Object query = null;
 286  0
         if (endpoint != null && endpoint.getProperties() != null)
 287  
         {
 288  0
             Object queries = endpoint.getProperties().get("queries");
 289  0
             if (queries instanceof Map)
 290  
             {
 291  0
                 query = ((Map)queries).get(stmt);
 292  
             }
 293  
         }
 294  0
         if (query == null)
 295  
         {
 296  0
             if (this.queries != null)
 297  
             {
 298  0
                 query = this.queries.get(stmt);
 299  
             }
 300  
         }
 301  0
         return query == null ? null : query.toString();
 302  
     }
 303  
 
 304  
     /**
 305  
      * @return Returns the dataSource.
 306  
      */
 307  
     public DataSource getDataSource()
 308  
     {
 309  0
         return dataSource;
 310  
     }
 311  
 
 312  
     /**
 313  
      * @param dataSource The dataSource to set.
 314  
      */
 315  
     public void setDataSource(DataSource dataSource)
 316  
     {
 317  0
         this.dataSource = dataSource;
 318  0
     }
 319  
 
 320  
     /**
 321  
      * @return Returns the pollingFrequency.
 322  
      */
 323  
     public long getPollingFrequency()
 324  
     {
 325  0
         return pollingFrequency;
 326  
     }
 327  
 
 328  
     /**
 329  
      * @param pollingFrequency The pollingFrequency to set.
 330  
      */
 331  
     public void setPollingFrequency(long pollingFrequency)
 332  
     {
 333  0
         this.pollingFrequency = pollingFrequency;
 334  0
     }
 335  
 
 336  
     /**
 337  
      * @return Returns the queries.
 338  
      */
 339  
     public Map getQueries()
 340  
     {
 341  0
         return queries;
 342  
     }
 343  
 
 344  
     /**
 345  
      * @param queries The queries to set.
 346  
      */
 347  
     public void setQueries(Map queries)
 348  
     {
 349  0
         this.queries = queries;
 350  0
     }
 351  
 
 352  
     /**
 353  
      * @return Returns the dataSourceJndiName.
 354  
      */
 355  
     public String getDataSourceJndiName()
 356  
     {
 357  0
         return dataSourceJndiName;
 358  
     }
 359  
 
 360  
     /**
 361  
      * @param dataSourceJndiName The dataSourceJndiName to set.
 362  
      */
 363  
     public void setDataSourceJndiName(String dataSourceJndiName)
 364  
     {
 365  0
         this.dataSourceJndiName = dataSourceJndiName;
 366  0
     }
 367  
 
 368  
     /**
 369  
      * @return Returns the jndiContext.
 370  
      */
 371  
     public Context getJndiContext()
 372  
     {
 373  0
         return jndiContext;
 374  
     }
 375  
 
 376  
     /**
 377  
      * @param jndiContext The jndiContext to set.
 378  
      */
 379  
     public void setJndiContext(Context jndiContext)
 380  
     {
 381  0
         this.jndiContext = jndiContext;
 382  0
     }
 383  
 
 384  
     /**
 385  
      * @return Returns the jndiInitialFactory.
 386  
      */
 387  
     public String getJndiInitialFactory()
 388  
     {
 389  0
         return jndiInitialFactory;
 390  
     }
 391  
 
 392  
     /**
 393  
      * @param jndiInitialFactory The jndiInitialFactory to set.
 394  
      */
 395  
     public void setJndiInitialFactory(String jndiInitialFactory)
 396  
     {
 397  0
         this.jndiInitialFactory = jndiInitialFactory;
 398  0
     }
 399  
 
 400  
     /**
 401  
      * @return Returns the jndiProviderUrl.
 402  
      */
 403  
     public String getJndiProviderUrl()
 404  
     {
 405  0
         return jndiProviderUrl;
 406  
     }
 407  
 
 408  
     /**
 409  
      * @param jndiProviderUrl The jndiProviderUrl to set.
 410  
      */
 411  
     public void setJndiProviderUrl(String jndiProviderUrl)
 412  
     {
 413  0
         this.jndiProviderUrl = jndiProviderUrl;
 414  0
     }
 415  
 
 416  
     /**
 417  
      * @return Returns the providerProperties.
 418  
      */
 419  
     public Map getProviderProperties()
 420  
     {
 421  0
         return providerProperties;
 422  
     }
 423  
 
 424  
     /**
 425  
      * @param providerProperties The providerProperties to set.
 426  
      */
 427  
     public void setProviderProperties(Map providerProperties)
 428  
     {
 429  0
         this.providerProperties = providerProperties;
 430  0
     }
 431  
 
 432  
     public Connection getConnection() throws Exception
 433  
     {
 434  0
         UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();
 435  0
         if (tx != null)
 436  
         {
 437  0
             if (tx.hasResource(dataSource))
 438  
             {
 439  0
                 logger.debug("Retrieving connection from current transaction");
 440  0
                 return (Connection)tx.getResource(dataSource);
 441  
             }
 442  
         }
 443  0
         logger.debug("Retrieving new connection from data source");
 444  0
         Connection con = dataSource.getConnection();
 445  
 
 446  0
         if (tx != null)
 447  
         {
 448  0
             logger.debug("Binding connection to current transaction");
 449  
             try
 450  
             {
 451  0
                 tx.bindResource(dataSource, con);
 452  
             }
 453  0
             catch (TransactionException e)
 454  
             {
 455  0
                 throw new RuntimeException("Could not bind connection to current transaction", e);
 456  0
             }
 457  
         }
 458  0
         return con;
 459  
     }
 460  
 
 461  
     /**
 462  
      * @return Returns the resultSetHandler.
 463  
      */
 464  
     public String getResultSetHandler()
 465  
     {
 466  0
         return this.resultSetHandler;
 467  
     }
 468  
 
 469  
     /**
 470  
      * @param resultSetHandler The resultSetHandler class name to set.
 471  
      */
 472  
     public void setResultSetHandler(String resultSetHandler)
 473  
     {
 474  0
         this.resultSetHandler = resultSetHandler;
 475  0
     }
 476  
 
 477  
     /**
 478  
      * @return a new instance of the ResultSetHandler class as defined in the
 479  
      *         JdbcConnector
 480  
      */
 481  
     protected ResultSetHandler createResultSetHandler()
 482  
     {
 483  
         try
 484  
         {
 485  0
             return (ResultSetHandler) ClassUtils.instanciateClass(getResultSetHandler(),
 486  
                                                                   ClassUtils.NO_ARGS);
 487  
         }
 488  0
         catch (Exception e)
 489  
         {
 490  0
             throw new IllegalArgumentException("Error creating instance of the resultSetHandler class :"
 491  
                                                + getResultSetHandler() + System.getProperty("line.separator")
 492  
                                                + ExceptionUtils.getFullStackTrace(e));
 493  
         }
 494  
     }
 495  
 
 496  
     public Set getQueryValueExtractors()
 497  
     {
 498  0
         return queryValueExtractors;
 499  
     }
 500  
 
 501  
     public void setQueryValueExtractors(Set queryValueExtractors)
 502  
     {
 503  0
         this.queryValueExtractors = queryValueExtractors;
 504  0
     }
 505  
 
 506  
     /**
 507  
      * @return Returns the queryRunner.
 508  
      */
 509  
     public String getQueryRunner()
 510  
     {
 511  0
         return this.queryRunner;
 512  
     }
 513  
 
 514  
     /**
 515  
      * @param queryRunner The QueryRunner class name to set.
 516  
      */
 517  
     public void setQueryRunner(String queryRunner)
 518  
     {
 519  0
         this.queryRunner = queryRunner;
 520  0
     }
 521  
 
 522  
     /**
 523  
      * @return a new instance of the QueryRunner class as defined in the
 524  
      *         JdbcConnector
 525  
      */
 526  
     protected QueryRunner createQueryRunner()
 527  
     {
 528  
         try
 529  
         {
 530  0
             return (QueryRunner) ClassUtils.instanciateClass(getQueryRunner(),
 531  
                                                              ClassUtils.NO_ARGS);
 532  
         }
 533  0
         catch (Exception e)
 534  
         {
 535  0
             throw new IllegalArgumentException("Error creating instance of the queryRunner class :"
 536  
                                                + getQueryRunner() + System.getProperty("line.separator")
 537  
                                                + ExceptionUtils.getFullStackTrace(e));
 538  
         }
 539  
     }
 540  
 
 541  
     /**
 542  
      * Parse the given statement filling the parameter list and return the ready to
 543  
      * use statement.
 544  
      * 
 545  
      * @param stmt
 546  
      * @param params
 547  
      * @return
 548  
      */
 549  
     public String parseStatement(String stmt, List params)
 550  
     {
 551  0
         if (stmt == null)
 552  
         {
 553  0
             return stmt;
 554  
         }
 555  0
         Matcher m = STATEMENT_ARGS.matcher(stmt);
 556  0
         StringBuffer sb = new StringBuffer(200);
 557  0
         while (m.find())
 558  
         {
 559  0
             String key = m.group();
 560  0
             m.appendReplacement(sb, "?");
 561  0
             params.add(key);
 562  
         }
 563  0
         m.appendTail(sb);
 564  0
         return sb.toString();
 565  
     }
 566  
 
 567  
     public Object[] getParams(UMOImmutableEndpoint endpoint, List paramNames, Object message)
 568  
         throws Exception
 569  
     {
 570  0
         Object[] params = new Object[paramNames.size()];
 571  0
         for (int i = 0; i < paramNames.size(); i++)
 572  
         {
 573  0
             String param = (String)paramNames.get(i);
 574  0
             String name = param.substring(2, param.length() - 1);
 575  0
             Object value = null;
 576  
             // If we find a value and it happens to be null, thats acceptable
 577  0
             boolean foundValue = false;
 578  0
             if (message != null)
 579  
             {
 580  0
                 for (Iterator iterator = propertyExtractors.iterator(); iterator.hasNext();)
 581  
                 {
 582  0
                     PropertyExtractor pe = (PropertyExtractor)iterator.next();
 583  0
                     value = pe.getProperty(name, message);
 584  0
                     if (value != null)
 585  
                     {
 586  0
                         if (value.equals(StringUtils.EMPTY) && pe instanceof BeanPropertyExtractor)
 587  
                         {
 588  0
                             value = null;
 589  
                         }
 590  0
                         foundValue = true;
 591  0
                         break;
 592  
                     }
 593  
                 }
 594  
             }
 595  0
             if (!foundValue)
 596  
             {
 597  0
                 value = endpoint.getProperty(name);
 598  
             }
 599  
 
 600  
             // Allow null values which may be acceptable to the user
 601  
             // Why shouldn't nulls be allowed? Otherwise every null parameter has to
 602  
             // be defined
 603  
             // if (value == null && !foundValue)
 604  
             // {
 605  
             // throw new IllegalArgumentException("Can not retrieve argument " +
 606  
             // name);
 607  
             // }
 608  0
             params[i] = value;
 609  
         }
 610  0
         return params;
 611  
     }
 612  
 }