1
2
3
4
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
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
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
124 log.debug("No jndi datasource given (key "+JNDI_DATASOURCE+")");
125 }
126
127
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
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
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
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
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 }