1
2
3
4
5
6
7
8
9
10
11
12 package ca.spaz.cron.datasource.sql;
13
14 import java.sql.*;
15 import java.util.*;
16
17 import org.apache.log4j.Logger;
18
19 import ca.spaz.cron.CRONConfiguration;
20 import ca.spaz.util.ToolBox;
21
22 /***
23 * Class for JDBC connection management. Provides information about available
24 * datasources, and provides Connection objects to calling classes that are
25 * guaranteed to be working.
26 *
27 * @author Chris Rose
28 */
29 public class ConnectionManager {
30
31 /***
32 * Implementation of the DatabaseID interface for DB name and DB ID
33 *
34 * @author Chris Rose
35 */
36 private static class DatabaseIDImpl implements DatabaseID {
37
38 private String id;
39
40 private String name;
41
42 /***
43 * Create a new instance, with the supplied DB ID and name.
44 *
45 * @param dbID
46 * the ID of the database in the properties file
47 * @param dbName
48 * the human-readable name of the database.
49 */
50 public DatabaseIDImpl(String dbID, String dbName) {
51 this.id = dbID;
52 this.name = dbName;
53 }
54
55
56
57
58
59
60 public String getID() {
61 return id;
62 }
63
64
65
66
67
68
69 public String getName() {
70 return name;
71 }
72
73
74
75
76
77
78 public String toString() {
79 return name;
80 }
81
82 }
83
84 private static List dbIDList;
85
86 private static Map instanceMap;
87
88 private static final Logger logger = Logger
89 .getLogger(ConnectionManager.class);
90
91 /***
92 * Get the human-friendly name for a connection ID
93 *
94 * @param connectionID
95 * the ID to get the name for
96 * @return the name of the Connection, or an error message.
97 */
98 static String getConnectionName(String connectionID) {
99 return getProperties().getProperty("db." + connectionID + ".name",
100 "Missing DB Name");
101 }
102
103 /***
104 * Retrieve a list of available database IDs.
105 *
106 * @return a <code>List</code> containing <code>DatabaseID</code>
107 * implementations for every available user datasource.
108 */
109 public static List getDatabaseIDs() {
110 if (null == dbIDList) {
111 dbIDList = new ArrayList();
112 String[] ids = getProperties().getProperty("db.dblist", "").split(",");
113 for (int i = 0; i < ids.length; i++) {
114 dbIDList.add(new DatabaseIDImpl(ids[i], getConnectionName(ids[i])));
115 }
116 }
117 return dbIDList;
118 }
119
120 /***
121 * Get an instance of a ConnectionManager for a particular dbID.
122 *
123 * @param dbID
124 * the ID of the connection
125 * @return a <code>ConnectionManager</code> for the ID
126 */
127 public static ConnectionManager getInstance(String dbID) {
128 ConnectionManager dbInstance = (ConnectionManager) getInstanceMap().get(
129 dbID);
130 if (null == dbInstance) {
131 dbInstance = new ConnectionManager(dbID);
132 getInstanceMap().put(dbID, dbInstance);
133 }
134 return dbInstance;
135 }
136
137 private static Map getInstanceMap() {
138 if (null == instanceMap) {
139 instanceMap = new HashMap();
140 }
141 return instanceMap;
142 }
143
144 /***
145 * @return
146 */
147 private static CRONConfiguration getProperties() {
148 return CRONConfiguration.getInstance();
149 }
150
151 /***
152 * @param foodDB
153 */
154 private static void registerInstance(ConnectionManager foodDB) {
155 getInstanceMap().put(foodDB.dbID, foodDB);
156 }
157
158 private Connection dbc;
159
160 private String dbID;
161
162 private List listeners;
163
164 private ConnectionManager(String dbID) {
165 this.dbID = dbID;
166 registerInstance(this);
167 }
168
169 /***
170 * Add an observer for notification when the database connection is made. If
171 * the database is already connected, fires a notification immediately.
172 *
173 * @param observer
174 * the Observer.
175 */
176 public void addConnectionStatusListener(IConnectionStatusObserver observer) {
177 getListeners().add(observer);
178 if (getInstanceMap().containsKey(dbID)) {
179 observer.connectionMade(dbID);
180 }
181 }
182
183 /***
184 * Get the database connection. Lazy initializer.
185 *
186 * @return the connection to the food database or null if there is no
187 * connection possible.
188 */
189 public Connection getConnection() {
190 try {
191
192
193
194
195 if (dbc != null) {
196 dbc.createStatement().execute(
197 "CREATE TABLE _garbage___(id char(1))");
198 dbc.createStatement().execute("DROP TABLE _garbage___");
199 }
200 } catch (SQLException e) {
201 if (logger.isDebugEnabled()) {
202 logger.debug("getConnection() - lost working connection to DB ["
203 + dbID + "]", e);
204 }
205 dbc = null;
206 }
207 if (dbc == null) {
208
209
210
211 CRONConfiguration p = getProperties();
212
213
214
215
216 String serverMode = "";
217 serverMode = p.getProperty("db." + dbID);
218 if (null == serverMode) {
219 logger.error("Incomplete properties -- missing datasource for "
220 + dbID);
221 }
222 String classname = p.getProperty("db." + serverMode + ".driverclass");
223 String url = p.getProperty("db." + serverMode + ".url");
224 int idx = url.indexOf("${APP_HOME}");
225 if (idx >= 0) {
226 String oldURL = url;
227 url = oldURL.substring(0, idx) + ToolBox.getUserAppDirectory("cronometer").getAbsolutePath() + oldURL.substring(idx + "${APP_HOME}".length());
228 }
229 String username = p.getProperty("db." + serverMode + ".username");
230 String password = p.getProperty("db." + serverMode + ".password");
231 if (null == url || null == classname || null == username
232 || null == password) {
233 logger.error("Incomplete properties for [" + dbID + "] source "
234 + serverMode);
235 return null;
236 }
237 try {
238 if (logger.isInfoEnabled()) {
239 logger
240 .info("getConnection() - Acquiring db connection to : url = "
241 + url);
242 }
243
244 Class.forName(classname).newInstance();
245 dbc = DriverManager.getConnection(url, username, password);
246 notifyObservers();
247 } catch (Exception e) {
248 logger.error("getConnection()", e);
249 }
250 }
251 return dbc;
252 }
253
254 private List getListeners() {
255 if (null == listeners) {
256 listeners = new ArrayList();
257 }
258 return listeners;
259 }
260
261 private void notifyObservers() {
262 for (Iterator iter = getListeners().iterator(); iter.hasNext();) {
263 IConnectionStatusObserver element = (IConnectionStatusObserver) iter
264 .next();
265 element.connectionMade(dbID);
266 }
267 }
268
269 /***
270 * Remove an observer from this ConnectionManager
271 *
272 * @param observer
273 * the Observer to remove.
274 */
275 public void removeConnectionStatusListener(IConnectionStatusObserver observer) {
276 getListeners().remove(observer);
277 }
278
279 }