View Javadoc

1   /*
2    * $Id: JdbcConnections.java,v 1.4 2006/06/15 16:50:09 clq2 Exp $
3    *
4    * (C) Copyright Astrogrid...
5    */
6   
7   package org.astrogrid.tableserver.jdbc;
8   
9   import java.lang.reflect.Constructor;
10  import java.sql.Connection;
11  import java.sql.Driver;
12  import java.sql.DriverManager;
13  import java.sql.SQLException;
14  import java.util.StringTokenizer;
15  import java.util.Vector;
16  import javax.naming.InitialContext;
17  import javax.naming.NamingException;
18  import javax.sql.DataSource;
19  import org.apache.commons.logging.Log;
20  import org.apache.commons.logging.LogFactory;
21  import org.astrogrid.cfg.ConfigFactory;
22  import org.astrogrid.dataservice.queriers.DatabaseAccessException;
23  
24  /***
25   * Manages the plugin connections to a JDBC/SQL database.  It looks up from
26   * the connection details in the configuration file
27   * <p>
28   *
29   * For the moment, makes a new connection each time -
30   * ie One jdbcConnection is used for each query because there appear to be some
31   * issues with tidying up resultsets before another
32   * query can be run, rather than just abandoning the connection...
33   *
34   * <p>
35   * Remember to close the connection when you're finished!
36   */
37  
38  public class JdbcConnections {
39     
40     /*** Required; JDBC Driver(s) - comma seperated list  */
41     public static final String JDBC_DRIVERS_KEY = "datacenter.plugin.jdbc.drivers";
42     
43     /*** configuration file key, stores a JDBC connection URL for tis database querier */
44     public static final String JDBC_URL_KEY = "datacenter.plugin.jdbc.url";
45     /*** configuration file key, stores the user id for the JDBC connection URL for this database querier */
46     public static final String JDBC_USER_KEY = "datacenter.plugin.jdbc.user";
47     /*** configuration file key, stores the password for the JDBC connection URL for this database querier */
48     public static final String JDBC_PASSWORD_KEY = "datacenter.plugin.jdbc.password";
49     /*** configuration file key, stores a set of properties for the connection */
50     //public static final String JDBC_CONNECTION_PROPERTIES_KEY = "DatabaseQuerier.ConnectionProperties";
51  
52     /*** JNDI key where datasource is expected */
53     public final static String JNDI_DATASOURCE = "java:comp/env/jdbc/dsa-datasource";
54     
55     /*** JDBC drivers started, also marker for jdbc drivers been started */
56     private static Driver[] drivers = null;
57  
58     /*** User name for this connection manager */
59     private final String userId;
60     /*** password for this connection manager */
61     private final String password;
62     
63     /*** Datasource defined in JNDI (or elsewhere!) for this connection manager **/
64     private final DataSource datasource;
65  
66     /*** If no datasource, connection url  **/
67     private final String jdbcUrl;
68  
69     private static final Log log = LogFactory.getLog(JdbcConnections.class);
70     
71     /***
72      * Construct from datasource
73      */
74     public JdbcConnections(DataSource givenSource) throws DatabaseAccessException {
75        this.datasource = givenSource;
76  
77        this.jdbcUrl = null;
78        this.userId = null;
79        this.password = null;
80        
81        log.info("JDBC Connection manager set to datasource "+datasource);
82  
83        if (drivers==null) { startDrivers(); }
84     }
85     
86     /***
87      * No datasource; will use DriverManager to make connections
88      */
89     public JdbcConnections(String databaseUrl, String givenUser, String givenPassword) throws DatabaseAccessException {
90        this.userId = givenUser;
91        this.password = givenPassword;
92        this.jdbcUrl = databaseUrl;
93        this.datasource = null;
94  
95        log.info("JDBC Connection manager set to "+jdbcUrl+" ("+userId+")");
96  
97        if (drivers==null) { startDrivers(); }
98     }
99     
100    
101    /***
102     * Creates appropriate connection manager.  Looks first for JNDI-defined datasource, then for
103     * Url/user/etc keys in configuration file
104     */
105    public synchronized static JdbcConnections makeFromConfig() throws DatabaseAccessException {
106       
107       log.debug("Creating JDBC Connection");
108       
109       //look in JNDI first
110       try {
111          Object jndiValue = new InitialContext().lookup(JNDI_DATASOURCE);
112          if (jndiValue != null) {
113             log.info("JNDI Key "+JNDI_DATASOURCE+" returns datasource "+jndiValue);
114             if (!(jndiValue instanceof DataSource)) {
115                throw new DatabaseAccessException("Key "+JNDI_DATASOURCE+" for JNDI returns "+jndiValue.getClass()+" should be a DataSource");
116             }
117             
118             return new JdbcConnections( (DataSource) jndiValue);
119          }
120       } catch (NamingException e) {
121          //not found - just log in passing
122          log.debug("No jndi datasource given (key "+JNDI_DATASOURCE+")");
123       }
124 
125       //try properties file
126       String userId = ConfigFactory.getCommonConfig().getString(JDBC_USER_KEY, null);
127       String password = ConfigFactory.getCommonConfig().getString(JDBC_PASSWORD_KEY, null);
128       String jdbcURL = ConfigFactory.getCommonConfig().getString(JDBC_URL_KEY, null);
129       if ( jdbcURL != null && jdbcURL.length() > 0)  {
130          log.info("JNDI Key "+JDBC_URL_KEY+" returns database url "+jdbcURL);
131          return new JdbcConnections(jdbcURL, userId, password);
132       }
133 
134       throw new DatabaseAccessException("No information on how to connect to JDBC - no '"+JNDI_DATASOURCE+"' defined in JNDI or '"+JDBC_URL_KEY+"' key in configuration file");
135    }
136    
137 
138    /***
139     *  Creates a connection from the datasource if given, from the
140     * DriverManager if not.
141     */
142    public Connection createConnection() throws SQLException {
143       
144       if (datasource != null) {
145          log.debug("Creating JDBC Connection from DataSource "+datasource);
146 
147          //connect (using user/password if given)
148          if (userId != null) {
149             return datasource.getConnection(userId, password);
150          } else {
151             return datasource.getConnection();
152          }
153       }
154       else
155       {
156          log.debug("Creating JDBC Connection from DriverManager, to Url "+jdbcUrl+" ("+userId+")");
157 
158          try {
159             //if a user/password are set, use that
160             if (userId != null) {
161                return DriverManager.getConnection(jdbcUrl,userId, password);
162             } else  {
163                return DriverManager.getConnection(jdbcUrl);
164             }
165          }
166          catch (SQLException se) {
167             //add moe info to the sql exception.  Especially the URL, as it is often a mistyped URL that gives 'no suitable driver'
168             SQLException newSe = new SQLException(se.getMessage()+" ["+se.getErrorCode()+"], connecting to "+jdbcUrl,
169                                                   se.getSQLState(), se.getErrorCode());
170             newSe.setStackTrace(se.getStackTrace());
171             throw newSe;
172          
173          }
174       }
175       
176    }
177 
178    
179    
180    /***
181     * Starts the jdbc driver(s) used to connect to the database; this method
182     * gets this driver class name(s) from the configuration file - each driver
183     * name separated by a comma, eg:
184     * <pre>
185     * JDBC Database Drivers = org.gjt.mm.mysql.Driver, unknown.class.fill.In
186     * </pre>
187     * On creation, the driver(s) self-
188     * register with the DriverManager, which can then be used to get connections
189     * to multiple types of databases).
190     * <p>
191     * this is a lazy initialisation - should only happen once - so it is
192     * synchronized and a flag is set.  So you can check the flag before calling;
193     * this is safe double checked locking...
194     */
195    public final static synchronized  Driver[] startDrivers() throws DatabaseAccessException {
196       if (drivers == null) {
197          //read value
198          String classList = ConfigFactory.getCommonConfig().getString(JDBC_DRIVERS_KEY, null);
199          Vector v = new Vector();
200          if (classList == null) {
201             log.warn("No SQL drivers found from key "+JDBC_DRIVERS_KEY);
202          } else {
203             //break down into lines
204             StringTokenizer tokenizer = new StringTokenizer(classList, ",");
205             while (tokenizer.hasMoreTokens()) {
206                String driver = tokenizer.nextToken().trim();
207                Driver d = startDriver(driver);
208                v.add(d);
209             }
210          }
211          drivers = (Driver[]) v.toArray(new Driver[] {});
212       }
213       return drivers;
214    }
215    
216    /***
217     * Starts a single driver, specified by the given string
218     * (eg  "org.gjt.mm.mysql.Driver")
219     * Seperate from startDrivers() above so that subclasses can use it to start
220     * particular drivers
221     */
222    public static Driver startDriver(String driverClassName) throws DatabaseAccessException {
223       try {
224          log.info("Starting JDBC driver '"+driverClassName+"'...");
225          Constructor constr= Class.forName(driverClassName).getConstructor(new Class[]{});
226          Driver driver = (java.sql.Driver) constr.newInstance(new Object[]{});
227          return driver;
228       }
229       catch (Exception e) {
230          throw new DatabaseAccessException("JDBC Driver error: " + e.toString(),e);
231       }
232    }
233 
234 }