KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > objectweb > cjdbc > driver > Driver


1 /**
2  * C-JDBC: Clustered JDBC.
3  * Copyright (C) 2002-2005 French National Institute For Research In Computer
4  * Science And Control (INRIA).
5  * Contact: c-jdbc@objectweb.org
6  *
7  * This library is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by the
9  * Free Software Foundation; either version 2.1 of the License, or any later
10  * version.
11  *
12  * This library is distributed in the hope that it will be useful, but WITHOUT
13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
15  * for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this library; if not, write to the Free Software Foundation,
19  * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
20  *
21  * Initial developer(s): Emmanuel Cecchet.
22  * Contributor(s): Julie Marguerite, Mathieu Peltier, Marek Prochazka, Sara
23  * Bouchenak, Jaco Swart.
24  */

25
26 package org.objectweb.cjdbc.driver;
27
28 import java.net.Socket JavaDoc;
29 import java.sql.DriverPropertyInfo JavaDoc;
30 import java.sql.SQLException JavaDoc;
31 import java.util.ArrayList JavaDoc;
32 import java.util.HashMap JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.Properties JavaDoc;
35
36 import javax.net.SocketFactory;
37
38 import org.objectweb.cjdbc.common.exceptions.AuthenticationException;
39 import org.objectweb.cjdbc.common.exceptions.NoMoreControllerException;
40 import org.objectweb.cjdbc.common.exceptions.driver.DriverSQLException;
41 import org.objectweb.cjdbc.common.net.SSLConfiguration;
42 import org.objectweb.cjdbc.common.net.SocketFactoryFactory;
43 import org.objectweb.cjdbc.common.stream.CJDBCInputStream;
44 import org.objectweb.cjdbc.common.stream.CJDBCOutputStream;
45 import org.objectweb.cjdbc.common.util.Constants;
46 import org.objectweb.cjdbc.controller.core.ControllerConstants;
47 import org.objectweb.cjdbc.driver.protocol.Commands;
48
49 /**
50  * C-JDBC Driver for client side. This driver is a generic driver that is
51  * designed to replace any specific JDBC driver that could be used by a client.
52  * The client only has to know the node where the C-JDBC controller is running
53  * and the database he wants to access (the RDBMS could be PostgreSQL, Oracle,
54  * DB2, Sybase, MySQL or whatever, we only need the name of the database and the
55  * C-JDBC controller will be responsible for finding the RDBMs hosting this
56  * database).
57  * <p>
58  * The C-JDBC driver can be loaded from the client with:
59  * <code>Class.forName("org.objectweb.cjdbc.driver.Driver");</code>
60  * <p>
61  * The URL expected for the use with C-JDBC is:
62  * <code>jdbc:cjdbc://host1:port1,host2:port2/database</code>.
63  * <p>
64  * At least one host must be specified. If several hosts are given, one is
65  * picked up randomly from the list. If the currently selected controller fails,
66  * another one is automatically picked up from the list.
67  * <p>
68  * Default port number is 25322 if omitted.
69  * <p>
70  * Those 2 examples are equivalent:
71  *
72  * <pre>
73  * DriverManager.getConnection(&quot;jdbc:cjdbc://localhost:/tpcw&quot;);
74  * DriverManager.getConnection(&quot;jdbc:cjdbc://localhost:25322/tpcw&quot;);
75  * </pre>
76  *
77  * <p>
78  * Examples using 2 controllers for fault tolerance:
79  *
80  * <pre>
81  * DriverManager
82  * .getConnection(&quot;jdbc:cjdbc://cluster1.objectweb.org:25322,cluster2.objectweb.org:25322/tpcw&quot;);
83  * DriverManager
84  * .getConnection(&quot;jdbc:cjdbc://localhost:25322,remote.objectweb.org:25322/tpcw&quot;);
85  * DriverManager
86  * .getConnection(&quot;jdbc:cjdbc://smpnode.com:25322,smpnode.com:1098/tpcw&quot;);
87  * </pre>
88  *
89  * <p>
90  * The driver accepts a number of options that starts after a ? sign and are
91  * separated by an & sign. Each option is a name=value pair. Example:
92  * jdbc:cjdbc://host/db?option1=value1;option2=value2.
93  * <p>
94  * Currently supported options are:
95  *
96  * <pre>
97  * user: user login
98  * password: user password
99  * booleanTrue: value for the 'true' value when using PreparedStatement.setBoolean method
100  * booleanFalse: value for the 'false' value when using PreparedStatement.setBoolean method
101  * escapeBackslash: set this to true to escape backslashes when performing escape processing of PreparedStatements
102  * escapeSingleQuote: set this to true to escape single quotes (') when performing escape processing of PreparedStatements
103  * escapeCharacter: use this character to prepend and append to the values when performing escape processing of PreparedStatements
104  * driverProcessed: set this to false to let queries be passed and prepared for each individual backend
105  * connectionPooling: set this to false if you do not want the driver to perform transparent connection pooling
106  * preferredController: defines the strategy to use to choose a preferred controller to connect to
107  * - jdbc:cjdbc://node1,node2,node3/myDB?preferredController=ordered
108  * Always connect to node1, and if not available then try to node2 and
109  * finally if none are available try node3.
110  * - jdbc:cjdbc://node1,node2,node3/myDB?preferredController=random
111  * Pickup a controller node randomly (default strategy)
112  * - jdbc:cjdbc://node1,node2:25343,node3/myDB?preferredController=node2:25343,node3
113  * Round-robin between node2 and node3, fallback to node1 if none of node2
114  * and node3 is available.
115  * - jdbc:cjdbc://node1,node2,node3/myDB?preferredController=roundRobin
116  * Round robin starting with first node in URL.
117  * retryIntervalInMs: once a controller has died, the driver will try to
118  * reconnect to this controller every retryIntervalInMs to see if the backend
119  * is back online. The default is 5000 (5 seconds).
120  * </pre>
121  *
122  * <p>
123  * This original code has been inspired from the PostgreSQL JDBC driver by Peter
124  * T. Mount <peter@retep.org.uk>and the MM MySQL JDBC Drivers from Mark Matthews
125  * <mmatthew@worldserver.com>.
126  *
127  * @author <a HREF="mailto:Emmanuel.Cecchet@inria.fr">Emmanuel Cecchet </a>
128  * @author <a HREF="mailto:Julie.Marguerite@inria.fr">Julie Marguerite </a>
129  * @author <a HREF="mailto:Mathieu.Peltier@inrialpes.fr">Mathieu Peltier </a>
130  * @author <a HREF="mailto:Marek.Prochazka@inrialpes.fr">Marek Prochazka </a>
131  * @author <a HREF="mailto:Nicolas.Modrzyk@inrialpes.fr">Nicolas Modrzyk </a>
132  * @author <a HREF="mailto:jaco.swart@iblocks.co.uk">Jaco Swart </a>
133  * @version 1.0
134  */

135
136 public class Driver implements java.sql.Driver JavaDoc
137 {
138   /** Driver major version. */
139   public static final int MAJOR_VERSION = Constants
140                                                                                .getMajorVersion();
141
142   /** Driver minor version. */
143   public static final int MINOR_VERSION = Constants
144                                                                                .getMinorVersion();
145   /** Get the cjdbc.ssl.enabled system property to check if SSL is enabled */
146   protected static final boolean SSL_ENABLED_PROPERTY = "true"
147                                                                                .equalsIgnoreCase(System
148                                                                                    .getProperty("cjdbc.ssl.enabled"));
149
150   /**
151    * Default interval in milliseconds before retrying to re-connect to a
152    * controller that has failed (default is 5 seconds or 5000 milliseconds)
153    */

154   public static final long DEFAULT_RETRY_INTERVAL_IN_MS = 5000;
155
156   /**
157    * List of driver properties initialized in the static class initializer
158    * <p>
159    * !!! Static intializer needs to be udpated when new properties are added !!!
160    */

161   protected static ArrayList JavaDoc driverProperties;
162
163   /** C-JDBC driver property name (if you add one, read driverProperties above). */
164   protected static final String JavaDoc HOST_PROPERTY = "HOST";
165   protected static final String JavaDoc PORT_PROPERTY = "PORT";
166   protected static final String JavaDoc DATABASE_PROPERTY = "DATABASE";
167   protected static final String JavaDoc USER_PROPERTY = "user";
168   protected static final String JavaDoc PASSWORD_PROPERTY = "password";
169   protected static final String JavaDoc BOOLEAN_TRUE_PROPERTY = "booleanTrue";
170   protected static final String JavaDoc BOOLEAN_FALSE_PROPERTY = "booleanFalse";
171   protected static final String JavaDoc ESCAPE_BACKSLASH_PROPERTY = "escapeBackslash";
172   protected static final String JavaDoc ESCAPE_SINGLE_QUOTE_PROPERTY = "escapeSingleQuote";
173   protected static final String JavaDoc ESCAPE_CHARACTER_PROPERTY = "escapeCharacter";
174   protected static final String JavaDoc DRIVER_PROCESSED_PROPERTY = "driverProcessed";
175   protected static final String JavaDoc CONNECTION_POOLING_PROPERTY = "connectionPooling";
176   protected static final String JavaDoc PREFERRED_CONTROLLER_PROPERTY = "preferredController";
177   protected static final String JavaDoc RETRY_INTERVAL_IN_MS_PROPERTY = "retryIntervalInMs";
178   protected static final String JavaDoc DEBUG_PROPERTY = "debugLevel";
179
180   /** C-JDBC driver property description. */
181   private static final String JavaDoc HOST_PROPERTY_DESCRIPTION = "Hostname of C-JDBC controller";
182   private static final String JavaDoc PORT_PROPERTY_DESCRIPTION = "Port number of C-JDBC controller";
183   private static final String JavaDoc DATABASE_PROPERTY_DESCRIPTION = "Database name";
184   private static final String JavaDoc USER_PROPERTY_DESCRIPTION = "Username to authenticate as";
185   private static final String JavaDoc PASSWORD_PROPERTY_DESCRIPTION = "Password to use for authentication";
186   private static final String JavaDoc BOOLEAN_TRUE_PROPERTY_DESCRIPTION = "Use this value for the 'true' value when using PreparedStatement.setBoolean method";
187   private static final String JavaDoc BOOLEAN_FALSE_PROPERTY_DESCRIPTION = "Use this value for the 'false' value when using PreparedStatement.setBoolean method";
188   private static final String JavaDoc ESCAPE_BACKSLASH_PROPERTY_DESCRIPTION = "Set this to true to escape backslashes when performing escape processing of PreparedStatements";
189   private static final String JavaDoc ESCAPE_SINGLE_QUOTE_PROPERTY_DESCRIPTION = "Set this to true to escape single quotes (') when performing escape processing of PreparedStatements";
190   private static final String JavaDoc ESCAPE_CHARACTER_PROPERTY_DESCRIPTION = "Use this character to prepend and append to the values when performing escape processing of PreparedStatements";
191   private static final String JavaDoc DRIVER_PROCESSED_PROPERTY_DESCRIPTION = "Set this to false to let queries be passed and prepared for each individual backend";
192   protected static final String JavaDoc CONNECTION_POOLING_PROPERTY_DESCRIPTION = "Set this to false if you do not want the driver to perform transparent connection pooling";
193   protected static final String JavaDoc PREFERRED_CONTROLLER_PROPERTY_DESCRIPTION = "Defines the strategy to use to choose a preferred controller to connect to";
194   protected static final String JavaDoc RETRY_INTERVAL_IN_MS_PROPERTY_DESCRIPTION = "Interval in milliseconds before retrying to re-connect to a controller that has failed";
195   protected static final String JavaDoc DEBUG_PROPERTY_DESCRIPTION = "Debug level that can be set to 'debug', 'info' or 'off'";
196
197   /** C-JDBC URL header. */
198   public static final String JavaDoc CJDBC_URL_HEADER = "jdbc:cjdbc://";
199
200   /** C-JDBC URL header length. */
201   public static final int CJDBC_URL_HEADER_LENGTH = CJDBC_URL_HEADER
202                                                                                .length();
203
204   /**
205    * Cache of parsed URLs used to connect to the controller. It always grows and
206    * is never purged: we don't yet handle the unlikely case of a long-lived
207    * driver using zillions of different URLs.
208    * <p>
209    * Hashmap is URL=> <code>CjdbcUrl</code>
210    */

211   private HashMap JavaDoc parsedUrlsCache = new HashMap JavaDoc();
212
213   /** List of connections that are ready to be closed. */
214   protected ArrayList JavaDoc pendingConnectionClosing = new ArrayList JavaDoc();
215   protected boolean connectionClosingThreadisAlive = false;
216
217   // The static initializer registers ourselves with the DriverManager
218
// and try to bind the C-JDBC Controller
219
static
220   {
221     // Register with the DriverManager (see JDBC API Tutorial and Reference,
222
// Second Edition p. 941)
223
try
224     {
225       java.sql.DriverManager.registerDriver(new Driver());
226     }
227     catch (SQLException JavaDoc e)
228     {
229       throw new RuntimeException JavaDoc("Unable to register C-JDBC driver");
230     }
231
232     // Build the static list of driver properties
233
driverProperties = new ArrayList JavaDoc();
234     driverProperties.add(Driver.HOST_PROPERTY);
235     driverProperties.add(Driver.PORT_PROPERTY);
236     driverProperties.add(Driver.DATABASE_PROPERTY);
237     driverProperties.add(Driver.USER_PROPERTY);
238     driverProperties.add(Driver.PASSWORD_PROPERTY);
239     driverProperties.add(Driver.BOOLEAN_TRUE_PROPERTY);
240     driverProperties.add(Driver.BOOLEAN_FALSE_PROPERTY);
241     driverProperties.add(Driver.ESCAPE_BACKSLASH_PROPERTY);
242     driverProperties.add(Driver.ESCAPE_SINGLE_QUOTE_PROPERTY);
243     driverProperties.add(Driver.ESCAPE_CHARACTER_PROPERTY);
244     driverProperties.add(Driver.DRIVER_PROCESSED_PROPERTY);
245     driverProperties.add(Driver.CONNECTION_POOLING_PROPERTY);
246     driverProperties.add(Driver.PREFERRED_CONTROLLER_PROPERTY);
247     driverProperties.add(Driver.RETRY_INTERVAL_IN_MS_PROPERTY);
248     driverProperties.add(Driver.DEBUG_PROPERTY);
249   }
250
251   /**
252    * Creates a new <code>Driver</code> and register it with
253    * <code>DriverManager</code>.
254    */

255   public Driver()
256   {
257     // Required for Class.forName().newInstance()
258
}
259
260   /**
261    * Asks the C-JDBC controller if the requested database can be accessed with
262    * the provided user name and password. If the C-JDBC controller can't access
263    * the requested database, an <code>SQLException</code> is thrown, else a
264    * "fake" <code>Connection</code> is returned to the user so that he or she
265    * can create <code>Statements</code>.
266    *
267    * @param url the URL of the C-JDBC controller to which to connect.
268    * @param properties a list of arbitrary string tag/value pairs as connection
269    * arguments (usually at least a "user" and "password").
270    * @return a <code>Connection</code> object that represents a connection to
271    * the database through the C-JDBC Controller.
272    * @exception SQLException if an error occurs.
273    */

274   public java.sql.Connection JavaDoc connect(String JavaDoc url, Properties JavaDoc properties)
275       throws SQLException JavaDoc
276   {
277     if (url == null)
278       throw new SQLException JavaDoc("Invalid null URL in connect");
279
280     /**
281      * We cannot raise a SQLException as the driver manager tries to connect to
282      * all registered drivers. So if the CJDBC_URL_HEADER is not found we should
283      * probably pass... and return null
284      */

285     if (!url.startsWith(CJDBC_URL_HEADER))
286       return null;
287
288     // In the common case, we do not synchronize
289
CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
290     if (cjdbcUrl == null) // Not in the cache
291
{
292       synchronized (this)
293       {
294         // Recheck here in case someone updated before we entered the
295
// synchronized block
296
cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
297         if (cjdbcUrl == null)
298         {
299           cjdbcUrl = new CjdbcUrl(url);
300           parsedUrlsCache.put(url, cjdbcUrl);
301         }
302       }
303     }
304
305     ControllerInfo controller = null;
306     try
307     {
308       // Choose a controller according to the policy
309
controller = cjdbcUrl.getControllerConnectPolicy().getController();
310       return connectToController(properties, cjdbcUrl, controller);
311     }
312     catch (AuthenticationException e)
313     {
314       throw new SQLException JavaDoc(e.getMessage());
315     }
316     catch (NoMoreControllerException e)
317     {
318       throw new SQLException JavaDoc("No controller is available to accept connections");
319     }
320     catch (SQLException JavaDoc e)
321     {
322       // Add controller to suspect list
323
if (controller != null)
324       {
325         cjdbcUrl.getControllerConnectPolicy().suspectControllerOfFailure(
326             controller);
327         // Retry
328
System.out.println("retrying");
329         return connect(url, properties);
330       }
331       else
332         throw e;
333     }
334     catch (RuntimeException JavaDoc e)
335     {
336       e.printStackTrace();
337       throw new SQLException JavaDoc(
338           "Unable to connect to the virtual database - Unexpected runtime error ("
339               + e + ")");
340     }
341   }
342
343   /**
344    * Connect to the given controller with the specified parameters.
345    *
346    * @param properties properties given in connect call
347    * @param cjdbcUrl C-JDBC URL object including parameters
348    * @param controller the controller to connect to
349    * @return the connection to the controller
350    * @throws AuthenticationException if the authentication has failed or the
351    * database name is wrong
352    * @throws DriverSQLException if the connection cannot be established with the
353    * controller
354    */

355   protected java.sql.Connection JavaDoc connectToController(Properties JavaDoc properties,
356       CjdbcUrl cjdbcUrl, ControllerInfo controller) throws
357       AuthenticationException, DriverSQLException
358   {
359     // Check user name
360
String JavaDoc user = null;
361     if (properties != null)
362       user = properties.getProperty(USER_PROPERTY);
363     if (user == null)
364       user = (String JavaDoc) cjdbcUrl.getParameters().get(USER_PROPERTY);
365     if (user == null || user.equals(""))
366       throw new AuthenticationException("Invalid user name in connect");
367
368     // Check the password
369
String JavaDoc password = null;
370     if (properties != null)
371       password = properties.getProperty(PASSWORD_PROPERTY);
372     if (password == null)
373       password = (String JavaDoc) cjdbcUrl.getParameters().get(PASSWORD_PROPERTY);
374     if (password == null)
375       password = "";
376
377     // Try to retrieve a reusable connection
378
if (!"false".equals(cjdbcUrl.getParameters().get(
379         CONNECTION_POOLING_PROPERTY)))
380     { // Connection pooling is activated
381
java.sql.Connection JavaDoc c = retrievePendingClosingConnection(properties,
382           cjdbcUrl.getUrl(), controller, user, password);
383       if (c != null)
384       {
385         if (cjdbcUrl.isDebugEnabled())
386           System.out.println("Reusing connection from pool");
387         return c; // Re-use this one
388
}
389     }
390
391     // Let's go for a new connection
392

393     // This is actually a connection constructor,
394
// we should try to move most of it below.
395
boolean sentVdbName = false;
396     boolean sentUserInfo = false;
397     try
398     {
399       // Connect to the controller
400
Socket JavaDoc socket = null;
401
402       // SSL enabled ?
403
if (SSL_ENABLED_PROPERTY)
404       {
405         SocketFactory sslFact = SocketFactoryFactory
406             .createFactory(SSLConfiguration.getDefaultConfig());
407         socket = sslFact.createSocket(controller.getHostname(), controller
408             .getPort());
409       }
410       else
411       {
412         // no ssl - we use ordinary socket
413
socket = new Socket JavaDoc(controller.getHostname(), controller.getPort());
414       }
415
416       // Disable Nagle algorithm else small messages are not sent
417
// (at least under Linux) even if we flush the output stream.
418
socket.setTcpNoDelay(true);
419
420       if (cjdbcUrl.isInfoEnabled())
421         System.out.println("Authenticating with controller " + controller);
422
423       CJDBCOutputStream out = new CJDBCOutputStream(socket);
424       // Send protocol version and database name
425
out.writeInt(Commands.ProtocolVersion);
426       out.writeUTF(cjdbcUrl.getDatabaseName());
427       out.flush();
428       sentVdbName = true;
429
430       // Send user information
431
out.writeUTF(user);
432       out.writeUTF(password);
433       out.flush();
434       sentUserInfo = true;
435
436       CJDBCInputStream in;
437       Connection con;
438
439       // Create input stream only here else it will block
440
in = new CJDBCInputStream(socket);
441
442       con = new Connection(this, socket, in, out, cjdbcUrl, controller, user,
443           password);
444
445       return setParametersOnConnection(properties, con);
446
447     } // try connect to the controller/connection constructor
448
catch (Exception JavaDoc re)
449     {
450       if (!sentVdbName)
451         throw new DriverSQLException("Unable to connect to controller on "
452             + controller.getHostname() + ":" + controller.getPort() + " (" + re
453             + ")");
454       else if (re instanceof AuthenticationException)
455         throw (AuthenticationException) re;
456       else if (!sentUserInfo)
457         throw new AuthenticationException(
458             "Unable to connect to the virtual database (virtual database name is probably not correct)");
459       else
460         throw new DriverSQLException(
461             "Unable to connect to the virtual database ("
462                 + re.getLocalizedMessage() + ")", re);
463     }
464   }
465
466   /**
467    * This method is used to implement the transparent connection pooling and try
468    * to retrieve a connection that was recently closed to the given controller
469    * with the provided login/password information.
470    *
471    * @param connectionInfo the connectino information
472    * @param url
473    * @return a connection that could be reuse or null if none
474    */

475   private java.sql.Connection JavaDoc retrievePendingClosingConnection(
476       Properties JavaDoc connectionInfo, String JavaDoc url, ControllerInfo controllerInfo,
477       String JavaDoc user, String JavaDoc password)
478   {
479     // Check if there is a connection that is about to be closed that could
480
// be reused. We take the bet that if a connection has been released by
481
// a client, in the general case, it will reuse the same connection.
482
// As we need to keep the work in the synchronized block as minimal as
483
// possible, we have to extract the string comparison
484
// (url,name,password,controller)from the sync block. This way, we cannot
485
// just read/compare/take the connection without synchronizing the whole
486
// thing. A solution is to systematically extract the first available
487
// connection in the sync block, and do the checkings outside the block. If
488
// we fail, we re-sync to put the connection back but in practice it is
489
// almost always a success and we don't really care to pay this extra cost
490
// once in a while.
491
try
492     {
493       Connection c;
494       synchronized (pendingConnectionClosing)
495       {
496         // Take the last one to prevent shifting all elements
497
c = (Connection) pendingConnectionClosing
498             .remove(pendingConnectionClosing.size() - 1);
499       }
500       if (url.equals(c.getUrl())
501           && controllerInfo.equals(c.getControllerInfo())
502           && user.equals(c.getUserName()) && password.equals(c.getPassword()))
503       { // Great! Take this one.
504
c.isClosed = false;
505         return setParametersOnConnection(connectionInfo, c);
506       }
507       else
508         // Put this connection back, it is not good for us
509
synchronized (pendingConnectionClosing)
510         {
511           pendingConnectionClosing.add(c);
512         }
513     }
514     catch (IndexOutOfBoundsException JavaDoc ignore)
515     {
516       // No connection available
517
}
518     return null;
519   }
520
521   /**
522    * Tests if the URL is understood by the driver. Calls the
523    * <code>parseURL()</code> method.
524    *
525    * @param url the JDBC URL.
526    * @return <code>true</code> if the URL is correct, otherwise an exception
527    * with extensive error message is thrown.
528    * @exception SQLException if the URL is incorrect an explicit error message
529    * is given.
530    */

531   public synchronized boolean acceptsURL(String JavaDoc url) throws SQLException JavaDoc
532   {
533     try
534     {
535       CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
536       if (cjdbcUrl == null) // Not in the cache
537
{
538         synchronized (this)
539         {
540           // Recheck here in case someone updated before we entered the
541
// synchronized block
542
cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
543           if (cjdbcUrl == null)
544           {
545             cjdbcUrl = new CjdbcUrl(url); // URL parsed here
546
// Update the cache anyway that can be useful later on
547
parsedUrlsCache.put(url, cjdbcUrl);
548           }
549         }
550       }
551       return true;
552     }
553     catch (SQLException JavaDoc e)
554     {
555       return false;
556     }
557   }
558
559   /**
560    * Set the different parameters on the connection. Possible values are:
561    * <code>BOOLEAN_TRUE_PROPERTY</code><br>
562    * <code>BOOLEAN_FALSE_PROPERTY</code><br>
563    * <code>ESCAPE_BACKSLASH_PROPERTY</code><br>
564    * <code>ESCAPE_SINGLE_QUOTE_PROPERTY</code><br>
565    * <code>ESCAPE_CHARACTER_PROPERTY</code><br>
566    * <code>DRIVER_PROCESSED_PROPERTY</code><br>
567    * <code>CONNECTION_POOLING_PROPERTY</code><br>
568    *
569    * @param props the properties used to connect to the controller. These
570    * properties should be collected from both the url and the
571    * <code>Properties</code> object passed in to the connect method
572    * @param connection the connection to set the parameters on. Previous
573    * parameters will be overriden
574    * @return the same connection with the parameters set
575    */

576   private java.sql.Connection JavaDoc setParametersOnConnection(Properties JavaDoc props,
577       org.objectweb.cjdbc.driver.Connection connection)
578   {
579     if ((props == null) || props.isEmpty())
580       return connection;
581
582     String JavaDoc booleanTrue = props.getProperty(BOOLEAN_TRUE_PROPERTY);
583     if (booleanTrue != null)
584       connection.setPreparedStatementBooleanTrue(booleanTrue);
585     String JavaDoc booleanFalse = props.getProperty(BOOLEAN_FALSE_PROPERTY);
586     if (booleanFalse != null)
587       connection.setPreparedStatementBooleanFalse(booleanFalse);
588     String JavaDoc escapeBaskslash = props.getProperty(ESCAPE_BACKSLASH_PROPERTY);
589     if (escapeBaskslash != null)
590       connection
591           .setEscapeBackslash(new Boolean JavaDoc(escapeBaskslash).booleanValue());
592     String JavaDoc escapeQuote = props.getProperty(ESCAPE_SINGLE_QUOTE_PROPERTY);
593     if (escapeQuote != null)
594       connection.setEscapeSingleQuote(new Boolean JavaDoc(escapeQuote).booleanValue());
595
596     String JavaDoc escapeChar = props.getProperty(ESCAPE_CHARACTER_PROPERTY);
597     if (escapeChar != null)
598       connection.setEscapeChar(escapeChar);
599
600     String JavaDoc driverProcessed = props.getProperty(DRIVER_PROCESSED_PROPERTY);
601     if (driverProcessed != null)
602       connection.setDriverProcessed(Boolean.valueOf(driverProcessed)
603           .booleanValue());
604
605     String JavaDoc connectionPooling = props.getProperty(CONNECTION_POOLING_PROPERTY);
606     if (connection != null)
607       connection.setConnectionPooling(Boolean.valueOf(connectionPooling)
608           .booleanValue());
609
610     return connection;
611   }
612
613   /**
614    * Change the database name in the provided URL.
615    *
616    * @param url URL to parse
617    * @param newDbName new database name to insert
618    * @return the updated URL
619    * @throws SQLException if an error occurs while parsing the url
620    */

621   public String JavaDoc changeDatabaseName(String JavaDoc url, String JavaDoc newDbName)
622       throws SQLException JavaDoc
623   {
624     StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
625     sb.append(CJDBC_URL_HEADER);
626     CjdbcUrl cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
627     if (cjdbcUrl == null)
628     {
629       acceptsURL(url); // parse and put in cache
630
cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
631     }
632     ControllerInfo[] controllerList = cjdbcUrl.getControllerList();
633     for (int i = 0; i < controllerList.length; i++)
634     {
635       if (i == 0)
636         sb.append(controllerList[i].toString());
637       else
638         sb.append("," + controllerList[i].toString());
639     }
640     sb.append("/" + newDbName);
641     HashMap JavaDoc params = cjdbcUrl.getParameters();
642     if (params != null)
643     {
644       Iterator JavaDoc paramsKeys = params.keySet().iterator();
645       String JavaDoc element = null;
646       while (paramsKeys.hasNext())
647       {
648         if (element == null)
649           sb.append("?");
650         else
651           sb.append("&");
652         element = (String JavaDoc) paramsKeys.next();
653         sb.append(element + "=" + params.get(paramsKeys));
654       }
655     }
656     return sb.toString();
657   }
658
659   /**
660    * This method is intended to allow a generic GUI tool to discover what
661    * properties it should prompt a human for in order to get enough information
662    * to connect to a database.
663    * <p>
664    * The only properties supported by C-JDBC are:
665    * <ul>
666    * <li>HOST_PROPERTY</li>
667    * <li>PORT_PROPERTY</li>
668    * <li>DATABASE_PROPERTY</li>
669    * <li>USER_PROPERTY</li>
670    * <li>PASSWORD_PROPERTY</li>
671    * <li>ESCAPE_BACKSLASH_PROPERTY</li>
672    * <li>ESCAPE_CHARACTER_PROPERTY</li>
673    * <li>ESCAPE_SINGLE_QUOTE</li>
674    * <li>BOOLEAN_FALSE_PROPERTY</li>
675    * <li>BOOLEAN_TRUE_PROPERTY</li>
676    * <li>DRIVER_PROCESSED_PROPERTY</li>
677    * <li>CONNECTION_POOLING_PROPERTY</li>
678    * <li>PREFERRED_CONTROLLER_PROPERTY
679    * <li>
680    * </ul>
681    *
682    * @param url the URL of the database to connect to
683    * @param info a proposed list of tag/value pairs that will be sent on connect
684    * open.
685    * @return an array of <code>DriverPropertyInfo</code> objects describing
686    * possible properties. This array may be an empty array if no
687    * properties are required (note that this override any setting that
688    * might be set in the URL).
689    * @exception SQLException if the url is not valid
690    * @see java.sql.Driver#getPropertyInfo
691    */

692   public DriverPropertyInfo JavaDoc[] getPropertyInfo(String JavaDoc url, Properties JavaDoc info)
693       throws SQLException JavaDoc
694   {
695     if (!acceptsURL(url))
696       throw new SQLException JavaDoc("Invalid url " + url);
697
698     CjdbcUrl cjdbcUrl;
699     synchronized (this)
700     {
701       cjdbcUrl = (CjdbcUrl) parsedUrlsCache.get(url);
702       if (cjdbcUrl == null)
703         throw new SQLException JavaDoc("Error while retrieving URL information");
704     }
705     HashMap JavaDoc params = cjdbcUrl.getParameters();
706
707     String JavaDoc host = info.getProperty(HOST_PROPERTY);
708     if (host == null)
709     {
710       ControllerInfo[] controllerList = cjdbcUrl.getControllerList();
711       for (int i = 0; i < controllerList.length; i++)
712       {
713         ControllerInfo controller = controllerList[i];
714         if (i == 0)
715           host = controller.toString();
716         else
717           host += "," + controller.toString();
718       }
719     }
720     DriverPropertyInfo JavaDoc hostProp = new DriverPropertyInfo JavaDoc(HOST_PROPERTY, host);
721     hostProp.required = true;
722     hostProp.description = HOST_PROPERTY_DESCRIPTION;
723
724     DriverPropertyInfo JavaDoc portProp = new DriverPropertyInfo JavaDoc(PORT_PROPERTY, info
725         .getProperty(PORT_PROPERTY, Integer
726             .toString(ControllerConstants.DEFAULT_PORT)));
727     portProp.required = false;
728     portProp.description = PORT_PROPERTY_DESCRIPTION;
729
730     String JavaDoc database = info.getProperty(DATABASE_PROPERTY);
731     if (database == null)
732       database = cjdbcUrl.getDatabaseName();
733     DriverPropertyInfo JavaDoc databaseProp = new DriverPropertyInfo JavaDoc(DATABASE_PROPERTY,
734         database);
735     databaseProp.required = true;
736     databaseProp.description = DATABASE_PROPERTY_DESCRIPTION;
737
738     String JavaDoc user = info.getProperty(USER_PROPERTY);
739     if (user == null)
740       user = (String JavaDoc) params.get(USER_PROPERTY);
741     DriverPropertyInfo JavaDoc userProp = new DriverPropertyInfo JavaDoc(USER_PROPERTY, user);
742     userProp.required = true;
743     userProp.description = USER_PROPERTY_DESCRIPTION;
744
745     String JavaDoc password = info.getProperty(PASSWORD_PROPERTY);
746     if (password == null)
747       password = (String JavaDoc) params.get(PASSWORD_PROPERTY);
748     DriverPropertyInfo JavaDoc passwordProp = new DriverPropertyInfo JavaDoc(PASSWORD_PROPERTY,
749         password);
750     passwordProp.required = true;
751     passwordProp.description = PASSWORD_PROPERTY_DESCRIPTION;
752
753     String JavaDoc escapeChar = info.getProperty(ESCAPE_CHARACTER_PROPERTY);
754     if (escapeChar == null)
755       escapeChar = (String JavaDoc) params.get(ESCAPE_CHARACTER_PROPERTY);
756     DriverPropertyInfo JavaDoc escapeCharProp = new DriverPropertyInfo JavaDoc(
757         ESCAPE_CHARACTER_PROPERTY, escapeChar);
758     escapeCharProp.required = false;
759     escapeCharProp.description = ESCAPE_CHARACTER_PROPERTY_DESCRIPTION;
760
761     String JavaDoc escapeBackslash = info.getProperty(ESCAPE_BACKSLASH_PROPERTY);
762     if (escapeBackslash == null)
763       escapeBackslash = (String JavaDoc) params.get(ESCAPE_BACKSLASH_PROPERTY);
764     DriverPropertyInfo JavaDoc escapeBackProp = new DriverPropertyInfo JavaDoc(
765         ESCAPE_BACKSLASH_PROPERTY, escapeBackslash);
766     escapeBackProp.required = false;
767     escapeBackProp.description = ESCAPE_BACKSLASH_PROPERTY_DESCRIPTION;
768
769     String JavaDoc escapeSingleQuote = info.getProperty(ESCAPE_SINGLE_QUOTE_PROPERTY);
770     if (escapeSingleQuote == null)
771       escapeSingleQuote = (String JavaDoc) params.get(ESCAPE_SINGLE_QUOTE_PROPERTY);
772     DriverPropertyInfo JavaDoc escapeSingleProp = new DriverPropertyInfo JavaDoc(
773         ESCAPE_SINGLE_QUOTE_PROPERTY, escapeSingleQuote);
774     escapeSingleProp.required = false;
775     escapeSingleProp.description = ESCAPE_SINGLE_QUOTE_PROPERTY_DESCRIPTION;
776
777     String JavaDoc booleanFalse = info.getProperty(BOOLEAN_FALSE_PROPERTY);
778     if (booleanFalse == null)
779       booleanFalse = (String JavaDoc) params.get(BOOLEAN_FALSE_PROPERTY);
780     DriverPropertyInfo JavaDoc booleanFalseProp = new DriverPropertyInfo JavaDoc(
781         BOOLEAN_FALSE_PROPERTY, booleanFalse);
782     booleanFalseProp.required = false;
783     booleanFalseProp.description = BOOLEAN_FALSE_PROPERTY_DESCRIPTION;
784
785     String JavaDoc booleanTrue = info.getProperty(BOOLEAN_TRUE_PROPERTY);
786     if (booleanTrue == null)
787       booleanTrue = (String JavaDoc) params.get(BOOLEAN_TRUE_PROPERTY);
788     DriverPropertyInfo JavaDoc booleanTrueProp = new DriverPropertyInfo JavaDoc(
789         BOOLEAN_TRUE_PROPERTY, booleanTrue);
790     booleanTrueProp.required = false;
791     booleanTrueProp.description = BOOLEAN_TRUE_PROPERTY_DESCRIPTION;
792
793     String JavaDoc driverProcessed = info.getProperty(DRIVER_PROCESSED_PROPERTY);
794     if (driverProcessed == null)
795       driverProcessed = (String JavaDoc) params.get(DRIVER_PROCESSED_PROPERTY);
796     DriverPropertyInfo JavaDoc parseQueryProp = new DriverPropertyInfo JavaDoc(
797         DRIVER_PROCESSED_PROPERTY, driverProcessed);
798     escapeSingleProp.required = false;
799     escapeSingleProp.description = DRIVER_PROCESSED_PROPERTY_DESCRIPTION;
800
801     String JavaDoc connectionPooling = info.getProperty(CONNECTION_POOLING_PROPERTY);
802     if (connectionPooling == null)
803       connectionPooling = (String JavaDoc) params.get(CONNECTION_POOLING_PROPERTY);
804     DriverPropertyInfo JavaDoc connectionPoolingProp = new DriverPropertyInfo JavaDoc(
805         CONNECTION_POOLING_PROPERTY, connectionPooling);
806     connectionPoolingProp.required = false;
807     connectionPoolingProp.description = CONNECTION_POOLING_PROPERTY_DESCRIPTION;
808
809     String JavaDoc preferredController = info
810         .getProperty(PREFERRED_CONTROLLER_PROPERTY);
811     if (preferredController == null)
812       preferredController = (String JavaDoc) params.get(PREFERRED_CONTROLLER_PROPERTY);
813     DriverPropertyInfo JavaDoc preferredControllerProp = new DriverPropertyInfo JavaDoc(
814         PREFERRED_CONTROLLER_PROPERTY, preferredController);
815     preferredControllerProp.required = false;
816     preferredControllerProp.description = PREFERRED_CONTROLLER_PROPERTY_DESCRIPTION;
817
818     return new DriverPropertyInfo JavaDoc[]{hostProp, portProp, databaseProp, userProp,
819         passwordProp, escapeCharProp, escapeBackProp, escapeSingleProp,
820         booleanFalseProp, booleanTrueProp, parseQueryProp,
821         connectionPoolingProp, preferredControllerProp};
822   }
823
824   /**
825    * Gets the river's major version number
826    *
827    * @return the driver's major version number
828    */

829   public int getMajorVersion()
830   {
831     return MAJOR_VERSION;
832   }
833
834   /**
835    * Gets the driver's minor version number
836    *
837    * @return the driver's minor version number
838    */

839   public int getMinorVersion()
840   {
841     return MINOR_VERSION;
842   }
843
844   /**
845    * Reports whether the driver is a genuine JDBC compliant driver. A driver may
846    * only report <code>true</code> here if it passes the JDBC compliance
847    * tests, otherwise it is required to return <code>false</code>. JDBC
848    * compliance requires full support for the JDBC API and full support for SQL
849    * 92 Entry Level. We cannot ensure that the underlying JDBC drivers will be
850    * JDBC compliant, so it is safer to return <code>false</code>.
851    *
852    * @return always <code>false</code>
853    */

854   public boolean jdbcCompliant()
855   {
856     return false;
857   }
858
859 }
860
Popular Tags