View Javadoc

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