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