001: /**
002: * Library name : Primrose - A Java Database Connection Pool.
003: * Published by Ben Keeping, http://primrose.org.uk .
004: * Copyright (C) 2004 Ben Keeping, primrose.org.uk
005: * Email: Use "Contact Us Form" on website
006: *
007: * This library is free software; you can redistribute it and/or
008: * modify it under the terms of the GNU Lesser General Public
009: * License as published by the Free Software Foundation; either
010: * version 2.1 of the License, or (at your option) any later version.
011: *
012: * This library is distributed in the hope that it will be useful,
013: * but WITHOUT ANY WARRANTY; without even the implied warranty of
014: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015: * Lesser General Public License for more details.
016: *
017: * You should have received a copy of the GNU Lesser General Public
018: * License along with this library; if not, write to the Free Software
019: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020: */package uk.org.primrose.pool.core;
021:
022: import uk.org.primrose.DebugLogger;
023: import uk.org.primrose.GeneralException;
024: import uk.org.primrose.Util;
025: import uk.org.primrose.pool.PoolException;
026: import uk.org.primrose.ConfigEncrypter;
027: import uk.org.primrose.Constants;
028: import java.io.*;
029: import java.util.*;
030:
031: public class PoolLoader {
032:
033: // A list of the current loaded pools
034: private static List<Pool> loadedPools = new ArrayList<Pool>();
035: private static PoolLoader singleton = new PoolLoader();
036:
037: // Indicator to see if we've been loaded before
038: private static boolean alreadyStarted = false;
039:
040: // JVM shutdown hook
041: static class PoolStopper extends Thread {
042: public void run() {
043: try {
044: PoolLoader.stopAllPools();
045: } catch (Throwable t) {
046: t.printStackTrace();
047: }
048: }
049: }
050:
051: static {
052: // Add a JVM exit shutdown hook
053: if (!alreadyStarted) {
054: alreadyStarted = true;
055: Runtime.getRuntime().addShutdownHook(new PoolStopper());
056: }
057: }
058:
059: /**
060: * Load multiple pools from a primrose config file
061: */
062: public static List<String> loadPool(String configFile,
063: boolean createNewPools) throws GeneralException,
064: IOException {
065: // The properties of our pool
066: Properties p = null;
067:
068: // Read the config file, and populate the pool Properties object
069: BufferedReader br = new BufferedReader(new InputStreamReader(
070: new FileInputStream(configFile)));
071: String line;
072:
073: // A list of pools that have been loaded
074: List<String> loadedPoolNames = new ArrayList<String>();
075: String lastKey = null;
076: while ((line = br.readLine()) != null) {
077: line = line.trim();
078: // Ignore comments and whitespace
079: if (line.startsWith("#") || line.length() == 0)
080: continue;
081:
082: String[] parts = line.split("=");
083: String key = parts[0];
084: String value = "";
085: if (parts.length > 2) {
086: // handle cases where line is like "name=value&soemthing=value2" - like on driver URLs
087: // or checkSQL statements
088: for (int i = 1; i < parts.length; i++) {
089: if (i == (parts.length - 1)) {
090: value += parts[i];
091: } else {
092: value += (parts[i] + "=");
093: }
094: }
095: } else {
096: if (parts.length > 1) {
097: value = parts[1];
098: }
099: }
100:
101: if (line.endsWith("=")) {
102: value += "=";
103: }
104:
105: if (key != null)
106: key = key.trim();
107: if (value != null)
108: value = value.trim();
109:
110: // handle multi line driverURL's
111: if (!Util.isConfigParameterValid(key) && lastKey != null
112: && lastKey.equalsIgnoreCase(Constants.DRIVER_URL)) {
113: if (p != null) {
114: p.setProperty(lastKey, p.getProperty(lastKey)
115: + line);
116: }
117: continue; // continue on so we don't reset the true lastKey (ie lines of multi-line driver are more than 1 ...)
118: }
119:
120: // Got a new poolName entry - means the beginning of
121: // a new pool
122: // If we have a pool ready to load (p != null)
123: // then start it up
124: if (key.equals(Constants.POOL_NAME)) {
125: if (p != null) {
126: // Check for encrypted passwords
127: DebugLogger
128: .log("Seeing if we need to decrpyt the passwords");
129: decryptPassword(p);
130:
131: // Start the pool
132: String poolName = loadPool(p, createNewPools);
133:
134: // Add to our list the loaded pools
135: loadedPoolNames.add(poolName);
136: }
137: p = new Properties();
138: }
139:
140: if (p != null) {
141: if (key.equals("="))
142: key = "";
143: if (value.equals("="))
144: value = "";
145:
146: p.setProperty(key, value);
147: }
148: lastKey = key;
149:
150: }
151:
152: // Load the last pool in the list
153: if (p != null) {
154: // Check for encrypted passwords
155: DebugLogger
156: .log("Seeing if we need to decrpyt the passwords");
157: decryptPassword(p);
158:
159: String poolName = loadPool(p, createNewPools);
160: loadedPoolNames.add(poolName);
161: }
162:
163: br.close();
164:
165: return loadedPoolNames;
166: }
167:
168: /**
169: * If the property Constants.ENCRYPTION_KEY_FILE is present (ie passwords are encypted)
170: * then decrpyt them and reset the entry in the Properties object
171: */
172: public static void decryptPassword(Properties p)
173: throws GeneralException {
174: Object encKeyFileObj = p.get(Constants.ENCRYPTION_FILE_KEY);
175: if (encKeyFileObj != null) {
176: String encKeyFile = (String) encKeyFileObj;
177: String encPassword = (String) p.get(Constants.PASSWORD);
178: String password = null;
179: DebugLogger.log("About to decrypt password (" + encPassword
180: + ")");
181: DebugLogger.log("Using keyfile (" + encKeyFile + ")");
182: try {
183: password = ConfigEncrypter.getDecryptedString(
184: encPassword, encKeyFile);
185: DebugLogger.log("Decrypt OK : (" + password + ")");
186: } catch (Exception e) {
187: DebugLogger.log("Got error decrpyting : " + e);
188: throw new GeneralException(
189: "Error decrypting password String for pool : "
190: + p.getProperty(Constants.POOL_NAME), e
191: .getCause());
192: }
193: p.setProperty(Constants.PASSWORD, password);
194:
195: } else {
196: DebugLogger
197: .log("envKeyFileObj is null ... not decrpyting passwords");
198: }
199: }
200:
201: /**
202: * Get a list of loaded pools
203: */
204: public static List<Pool> getLoadedPools() {
205: return loadedPools;
206: }
207:
208: /**
209: * Stop all pools
210: */
211: public static void stopAllPools() throws PoolException {
212: for (Pool p : loadedPools) {
213: p.stop(true);
214: }
215: }
216:
217: /**
218: * Stop a single pool
219: */
220: public static void stopPool(String name) throws PoolException {
221: Pool p = findExistingPool(name);
222: if (p != null) {
223: p.stop(true);
224: }
225: }
226:
227: /**
228: * Load a single new Pool, or alter settings in an existing Pool using
229: * a Properties object, which should contain all
230: * the details required to configure primrose.
231: * If createNewPool is true, then a brand new pool is made. Any pool running under an exsting name is shut down.
232: * if false then an existing pool is located, and its properties updated, and then restarted
233: * without killing existing connection SQL jobs running from the pool
234: */
235: public static String loadPool(Properties config,
236: boolean createNewPool) throws GeneralException {
237: Pool pool = null;
238: String poolName = config.getProperty(Constants.POOL_NAME);
239:
240: if (poolName == null) {
241: throw new GeneralException(
242: "Cannot load pool without a poolName property");
243: }
244: pool = findExistingPool(poolName.trim());
245:
246: // If we are creating a new Pool, see if we have an existing pool under that name, and stop it.
247: // Then instantiate a new Pool object
248: // or if we are altering an existing pool, then find it, update the settings and restart it
249: if (createNewPool) {
250: if (pool != null) {
251: pool.stop(false);
252: }
253: pool = new Pool();
254: } else {
255: if (pool == null) {
256: throw new GeneralException(
257: "Cannot locate pool under name '" + poolName
258: + "'");
259: }
260: }
261:
262: // Set the pool properties
263: // Are we loading our pool using a pool config provider class
264: // or are we loading using individual settings (ie from a primrose.config text file)
265: String configClassName = (String) config
266: .get(Constants.EXTERNAL_CONFIG_PROVIDER);
267:
268: //
269: // Load pool settings using a user provided class
270: //
271: if (configClassName != null) {
272: // Instantiate their class that provides the config settings
273: Object o = null;
274: try {
275: o = Class.forName(configClassName).newInstance();
276: } catch (Exception e) {
277: throw new GeneralException(
278: "Error loading "
279: + Constants.EXTERNAL_CONFIG_PROVIDER
280: + " class", e.getCause());
281: }
282: ExternalPoolConfigProvider configProvider = (ExternalPoolConfigProvider) o;
283:
284: // set our pool name
285: pool.setPoolName(poolName);
286:
287: // For all available config items, get the values from their class
288: for (String itemName : Constants.POOL_CONFIG_ITEM_NAMES) {
289: String value = configProvider.getConfigItem(poolName,
290: itemName);
291: // Ignore nulls ... use default
292: if (value != null && !value.equals("null")
293: && value.length() > 0) {
294: String camelItemName = (itemName.charAt(0) + "")
295: .toUpperCase()
296: + itemName.substring(1, itemName.length());
297:
298: // Throws GeneralException if errors
299: Util.callClassMethod(pool.getClass(), pool, "set"
300: + camelItemName, new Object[] { value });
301: }
302: }
303: //
304: // Load in the traditional manner (config text file)
305: //
306: } else {
307: for (Enumeration e = config.propertyNames(); e
308: .hasMoreElements();) {
309: String name = ((String) e.nextElement()).trim();
310: String value = config.getProperty(name);
311:
312: // If value is null - do not attempt to set the property
313: if (value == null) {
314: continue;
315: }
316:
317: value = value.trim();
318:
319: String camelName = (name.charAt(0) + "").toUpperCase()
320: + name.substring(1, name.length());
321:
322: // Throws GeneralException if errors
323: Util.callClassMethod(pool.getClass(), pool, "set"
324: + camelName, new Object[] { value });
325:
326: }
327:
328: }
329:
330: // Start the pool
331: if (createNewPool) {
332: pool.start();
333: loadedPools.add(pool);
334: } else {
335: pool.restart(false);
336: }
337: return poolName;
338: }
339:
340: /**
341: * Get a reference to an existing pool
342: */
343: public static Pool findExistingPool(String poolName) {
344: for (Pool pool : loadedPools) {
345: if (pool.getPoolName().equals(poolName)) {
346: return pool;
347: }
348: }
349: return null;
350: }
351:
352: /**
353: * Have any pools been loaded ?
354: */
355: public static boolean havePoolsBeenLoaded() {
356: return loadedPools.size() > 0;
357: }
358:
359: public static PoolLoader getInstance() {
360: return singleton;
361: }
362:
363: }
|