001: /**
002: * Copyright (C) 2001 Yasna.com. All rights reserved.
003: *
004: * ===================================================================
005: * The Apache Software License, Version 1.1
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution,
020: * if any, must include the following acknowledgment:
021: * "This product includes software developed by
022: * Yasna.com (http://www.yasna.com)."
023: * Alternately, this acknowledgment may appear in the software itself,
024: * if and wherever such third-party acknowledgments normally appear.
025: *
026: * 4. The names "Yazd" and "Yasna.com" must not be used to
027: * endorse or promote products derived from this software without
028: * prior written permission. For written permission, please
029: * contact yazd@yasna.com.
030: *
031: * 5. Products derived from this software may not be called "Yazd",
032: * nor may "Yazd" appear in their name, without prior written
033: * permission of Yasna.com.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL YASNA.COM OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of Yasna.com. For more information
051: * on Yasna.com, please see <http://www.yasna.com>.
052: */package com.Yasna.forum;
053:
054: import com.Yasna.forum.database.DbConnectionManager;
055:
056: import javax.naming.InitialContext;
057: import java.util.*;
058: import java.io.*;
059: import java.sql.ResultSet;
060: import java.sql.Connection;
061: import java.sql.PreparedStatement;
062: import java.sql.SQLException;
063:
064: /**
065: * Manages properties for the entire Yazd system. Properties are merely
066: * pieces of information that need to be saved in between server restarts. The
067: * class also reports the version of Yazd.
068: * <p>
069: * At the moment, properties are stored in a Java Properties file. In a version
070: * of Yazd coming soon, the properties file format will move to XML. XML
071: * properties will allow hierarchical property structures which may mean the
072: * API of this class will have to change.
073: * <p>
074: * Yazd properties are only meant to be set and retrevied by core Yazd classes.
075: * Therefore, skin writers should probably ignore this class.
076: * <p>
077: * This class is implemented as a singleton since many classloaders seem to
078: * take issue with doing classpath resource loading from a static context.
079: */
080: public class PropertyManager {
081:
082: /**
083: * The Major version number of Yazd. i.e. 1.x.x
084: */
085: private static final int MAJOR_VERSION = 3;
086:
087: /**
088: * The Minor version number of Yazd. i.e. x.1.x.
089: */
090: private static final int MINOR_VERSION = 0;
091:
092: /**
093: * The revision version number of Yazd. i.e. x.x.1.
094: */
095: private static final int REVISION_VERSION = 1;
096:
097: private static PropertyManager manager = null;
098: private static Object managerLock = new Object();
099: private static String propsName = "/yazd.properties";
100: private static boolean loadFromDb = true;
101: private static final String LOAD_PROPERTIES = "select name,propvalue from yazdProps";
102: private static final String DELETE_PROPERTIES = "delete from yazdProps";
103: private static final String INSERT_PROPERTIES = "insert into yazdProps(name,propvalue) values (?,?)";
104:
105: /**
106: * Returns a Yazd property.
107: *
108: * @param name the name of the property to return.
109: * @return the property value specified by name.
110: */
111: public static String getProperty(String name) {
112: if (manager == null) {
113: synchronized (managerLock) {
114: if (manager == null) {
115: manager = new PropertyManager(propsName);
116: }
117: }
118: }
119: return manager.getProp(name);
120: }
121:
122: public static void setPath(String path) {
123: if (manager == null) {
124: synchronized (managerLock) {
125: if (manager == null) {
126: manager = new PropertyManager(propsName);
127: }
128: }
129: }
130: manager.setProp("path", path);
131: }
132:
133: /**
134: * Sets a Yazd property. If the property doesn't already exists, a new
135: * one will be created.
136: *
137: * @param name the name of the property being set.
138: * @param value the value of the property being set.
139: */
140: public static void setProperty(String name, String value) {
141: if (manager == null) {
142: synchronized (managerLock) {
143: if (manager == null) {
144: manager = new PropertyManager(propsName);
145: }
146: }
147: }
148: manager.setProp(name, value);
149: }
150:
151: /**
152: * Deletes a Yazd property. If the property doesn't exist, the method
153: * does nothing.
154: *
155: * @param name the name of the property to delete.
156: */
157: public static void deleteProperty(String name) {
158: if (manager == null) {
159: synchronized (managerLock) {
160: if (manager == null) {
161: manager = new PropertyManager(propsName);
162: }
163: }
164: }
165: manager.deleteProp(name);
166: }
167:
168: /**
169: * Returns the names of the Yazd properties.
170: *
171: * @return an Enumeration of the Yazd property names.
172: */
173: public static Enumeration propertyNames() {
174: if (manager == null) {
175: synchronized (managerLock) {
176: if (manager == null) {
177: manager = new PropertyManager(propsName);
178: }
179: }
180: }
181: return manager.propNames();
182: }
183:
184: /**
185: * Returns true if the properties are readable. This method is mainly
186: * valuable at setup time to ensure that the properties file is setup
187: * correctly.
188: */
189: public static boolean propertyFileIsReadable() {
190: if (manager == null) {
191: synchronized (managerLock) {
192: if (manager == null) {
193: manager = new PropertyManager(propsName);
194: }
195: }
196: }
197: return manager.propFileIsReadable();
198: }
199:
200: /**
201: * Returns true if the properties are writable. This method is mainly
202: * valuable at setup time to ensure that the properties file is setup
203: * correctly.
204: */
205: public static boolean propertyFileIsWritable() {
206: if (manager == null) {
207: synchronized (managerLock) {
208: if (manager == null) {
209: manager = new PropertyManager(propsName);
210: }
211: }
212: }
213: return manager.propFileIsWritable();
214: }
215:
216: /**
217: * Returns true if the yazd.properties file exists where the path property
218: * purports that it does.
219: */
220: public static boolean propertyFileExists() {
221: if (manager == null) {
222: synchronized (managerLock) {
223: if (manager == null) {
224: manager = new PropertyManager(propsName);
225: }
226: }
227: }
228: return manager.propFileExists();
229: }
230:
231: /**
232: * Returns the version number of Yazd as a String. i.e. -- major.minor.revision
233: */
234: public static String getYazdVersion() {
235: return MAJOR_VERSION + "." + MINOR_VERSION + "."
236: + REVISION_VERSION;
237: }
238:
239: /**
240: * Returns the major version number of Yazd. i.e. -- 1.x.x
241: */
242: public static int getYazdVersionMajor() {
243: return MAJOR_VERSION;
244: }
245:
246: /**
247: * Returns the minor version number of Yazd. i.e. -- x.1.x
248: */
249: public static int getYazdVersionMinor() {
250: return MINOR_VERSION;
251: }
252:
253: /**
254: * Returns the revision version number of Yazd. i.e. -- x.x.1
255: */
256: public static int getYazdVersionRevision() {
257: return REVISION_VERSION;
258: }
259:
260: private Properties properties = null;
261: private Object propertiesLock = new Object();
262: private String resourceURI;
263:
264: /**
265: * Creates a new PropertyManager. Singleton access only.
266: */
267: private PropertyManager(String resourceURI) {
268: this .resourceURI = resourceURI;
269: }
270:
271: /**
272: * Gets a Yazd property. Yazd properties are stored in yazd.properties.
273: * The properties file should be accesible from the classpath. Additionally,
274: * it should have a path field that gives the full path to where the
275: * file is located. Getting properties is a fast operation.
276: *
277: * @param name the name of the property to get.
278: * @return the property specified by name.
279: */
280: protected String getProp(String name) {
281: //If properties aren't loaded yet. We also need to make this thread
282: //safe, so synchronize...
283: if (properties == null) {
284: synchronized (propertiesLock) {
285: //Need an additional check
286: if (properties == null) {
287: loadProps();
288: }
289: }
290: }
291: String property = properties.getProperty(name);
292: if (property == null) {
293: return null;
294: } else {
295: return property.trim();
296: }
297: }
298:
299: /**
300: * Sets a Yazd property. Because the properties must be saved to disk
301: * every time a property is set, property setting is relatively slow.
302: */
303: protected void setProp(String name, String value) {
304: //Only one thread should be writing to the file system at once.
305: synchronized (propertiesLock) {
306: //Create the properties object if necessary.
307: if (properties == null) {
308: loadProps();
309: }
310: properties.setProperty(name, value);
311: saveProps();
312: }
313: }
314:
315: protected void deleteProp(String name) {
316: //Only one thread should be writing to the file system at once.
317: synchronized (propertiesLock) {
318: //Create the properties object if necessary.
319: if (properties == null) {
320: loadProps();
321: }
322: properties.remove(name);
323: saveProps();
324: }
325: }
326:
327: protected Enumeration propNames() {
328: //If properties aren't loaded yet. We also need to make this thread
329: //safe, so synchronize...
330: if (properties == null) {
331: synchronized (propertiesLock) {
332: //Need an additional check
333: if (properties == null) {
334: loadProps();
335: }
336: }
337: }
338: return properties.propertyNames();
339: }
340:
341: /**
342: * Loads Yazd properties from the disk.
343: */
344: private void loadProps() {
345: switch (getLoadSource()) {
346: case 2:
347: System.err.println("Loading Properties from database");
348: loadPropsFromDb();
349: break;
350: default:
351: System.err
352: .println("Loading Properties from yolanda.properties");
353: loadPropsFromFile();
354: }
355:
356: }
357:
358: private void loadPropsFromFile() {
359: properties = new Properties();
360: InputStream in = null;
361: try {
362: in = getClass().getResourceAsStream(resourceURI);
363: properties.load(in);
364: } catch (Exception e) {
365: System.err
366: .println("Error reading Yolanda properties in PropertyManager.loadProps() "
367: + e);
368: e.printStackTrace();
369: } finally {
370: try {
371: in.close();
372: } catch (Exception e) {
373: }
374: }
375: }
376:
377: private void loadPropsFromDb() {
378: properties = new Properties();
379: Connection con = null;
380: PreparedStatement pstmt = null;
381: try {
382: con = DbConnectionManager.getConnection();
383: pstmt = con.prepareStatement(LOAD_PROPERTIES);
384: ResultSet rs = pstmt.executeQuery();
385: while (rs.next()) {
386: String name = rs.getString("name");
387: String value = rs.getString("propvalue");
388: properties.put(name, value);
389: }
390: } catch (Exception e) {
391: loadFromDb = false;
392: System.err
393: .println("Error in Property Manager:loadProperties():"
394: + e.getMessage());
395: } finally {
396: try {
397: pstmt.close();
398: } catch (Exception e) {
399: e.printStackTrace();
400: }
401: try {
402: con.close();
403: } catch (Exception e) {
404: e.printStackTrace();
405: }
406: }
407: }
408:
409: /**
410: * Saves Yazd properties to disk.
411: */
412: private void saveProps() {
413: //Now, save the properties to disk. In order for this to work, the user
414: //needs to have set the path field in the properties file. Trim
415: //the String to make sure there are no extra spaces.
416: if (propFileIsWritable()) {
417: String path = properties.getProperty("path").trim();
418: OutputStream out = null;
419: try {
420: out = new FileOutputStream(path);
421: properties.store(out, "yazd.properties -- "
422: + (new java.util.Date()));
423: } catch (Exception ioe) {
424: System.err
425: .println("There was an error writing yazd.properties to "
426: + path
427: + ". "
428: + "Ensure that the path exists and that the Yazd process has permission "
429: + "to write to it -- " + ioe);
430: ioe.printStackTrace();
431: } finally {
432: try {
433: out.close();
434: } catch (Exception e) {
435: }
436: }
437: }
438: // If everything is fine and working, we will store a copy of the properties in the database.
439: // This will be useful if the user wishes to change from using the file properties to a database properties setting.
440: if (properties.getProperty("setup") != null
441: && properties.getProperty("setup").equals("true")) {
442: Connection con = null;
443: PreparedStatement pstmt = null;
444: try {
445: con = DbConnectionManager.getConnection();
446: //Delete all old values.
447: pstmt = con.prepareStatement(DELETE_PROPERTIES);
448: pstmt.execute();
449: pstmt.close();
450: //Now insert new values.
451: pstmt = con.prepareStatement(INSERT_PROPERTIES);
452: Enumeration enume = properties.keys();
453: while (enume.hasMoreElements()) {
454: String name = (String) enume.nextElement();
455: String value = (String) properties.get(name);
456: pstmt.setString(1, name);
457: pstmt.setString(2, value);
458: pstmt.executeUpdate();
459: }
460: } catch (SQLException sqle) {
461: System.err.println(sqle);
462: } finally {
463: try {
464: pstmt.close();
465: } catch (Exception e) {
466: e.printStackTrace();
467: }
468: try {
469: con.close();
470: } catch (Exception e) {
471: e.printStackTrace();
472: }
473: }
474:
475: }
476: }
477:
478: /**
479: * this method checks to see if the application is already setup or not.
480: * If the application is setup, then you can load the properties from database.
481: * Otherwise it has to be determined how the application is being setup. Are we using
482: * the default connection pooler or a different db connection pooler.
483: * @return value of the source
484: */
485:
486: private int getLoadSource() {
487: int loadsource = 1; //assume that application is not setup.
488: // check to see if we are loading from the database using a context provider
489: try {
490: InitialContext ctxt = new InitialContext();
491: ctxt.lookup(DbConnectionManager.CONTEXT_JDBC_NAME);
492: loadsource = 2;
493: } catch (Exception e) {
494:
495: }
496: return loadsource;
497:
498: }
499:
500: /**
501: * Returns true if the properties are readable. This method is mainly
502: * valuable at setup time to ensure that the properties file is setup
503: * correctly.
504: */
505: public boolean propFileIsReadable() {
506: try {
507: InputStream in = getClass()
508: .getResourceAsStream(resourceURI);
509: return true;
510: } catch (Exception e) {
511: return false;
512: }
513: }
514:
515: /**
516: * Returns true if the yazd.properties file exists where the path property
517: * purports that it does.
518: */
519: public boolean propFileExists() {
520: String path = getProp("path");
521: if (path == null) {
522: return false;
523: }
524: File file = new File(path);
525: if (file.isFile()) {
526: return true;
527: } else {
528: return false;
529: }
530: }
531:
532: /**
533: * Returns true if the properties are writable. This method is mainly
534: * valuable at setup time to ensure that the properties file is setup
535: * correctly.
536: */
537: public boolean propFileIsWritable() {
538: String path = getProp("path");
539: File file = new File(path);
540: if (file.isFile()) {
541: //See if we can write to the file
542: if (file.canWrite()) {
543: return true;
544: } else {
545: return false;
546: }
547: } else {
548: return false;
549: }
550: }
551: }
|