View Javadoc

1   /*
2    *******************************************************************************
3    * Copyright (c) 2005 Chris Rose and AIMedia
4    * All rights reserved. ConnectionManager and the accompanying materials
5    * are made available under the terms of the Common Public License v1.0
6    * which accompanies this distribution, and is available at
7    * http://www.eclipse.org/legal/cpl-v10.html
8    * 
9    * Contributors:
10   *     Chris Rose
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         * (non-Javadoc)
57         * 
58         * @see ca.spaz.cron.database.DatabaseID#getID()
59         */
60        public String getID() {
61           return id;
62        }
63  
64        /*
65         * (non-Javadoc)
66         * 
67         * @see ca.spaz.cron.database.DatabaseID#getName()
68         */
69        public String getName() {
70           return name;
71        }
72  
73        /*
74         * (non-Javadoc)
75         * 
76         * @see java.lang.Object#toString()
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           * This is a test to determine if the connection is 'live' -- if it is
193           * not, we'll be replacing it with a new one.
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           * Acquire a connection based on values set in db.properties
210           */
211          CRONConfiguration p = getProperties();
212          /*
213           * Load the db properties. Defaults are specified for server mode
214           * operation.
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 }