001    package org.hackystat.sensorbase.server;
002    
003    import java.io.File;
004    import java.io.FileInputStream;
005    import java.io.IOException;
006    import java.util.Map;
007    import java.util.Properties;
008    import java.util.TreeMap;
009    
010    /**
011     * Provides access to the values stored in the sensorbase.properties file. 
012     * @author Philip Johnson
013     */
014    public class ServerProperties {
015      
016      /** The admin email key. */
017      public static final String ADMIN_EMAIL_KEY =     "sensorbase.admin.email";
018      /** The admin password. */
019      public static final String ADMIN_PASSWORD_KEY =   "sensorbase.admin.password";
020      /** The context root key. */
021      public static final String CONTEXT_ROOT_KEY =    "sensorbase.context.root";
022      /** The database directory key. */
023      public static final String DB_DIR_KEY =          "sensorbase.db.dir";
024      /** The database implementation class. */
025      public static final String DB_IMPL_KEY =          "sensorbase.db.impl";
026      /** The hostname key. */
027      public static final String HOSTNAME_KEY =        "sensorbase.hostname";
028      /** The logging level key. */
029      public static final String LOGGING_LEVEL_KEY =   "sensorbase.logging.level";
030      /** The Restlet Logging key. */
031      public static final String RESTLET_LOGGING_KEY = "sensorbase.restlet.logging";
032      /** The SMTP host key. */
033      public static final String SMTP_HOST_KEY =       "sensorbase.smtp.host";
034      /** The sensorbase port key. */
035      public static final String PORT_KEY =            "sensorbase.port";
036      /** The XML directory key. */
037      public static final String XML_DIR_KEY =         "sensorbase.xml.dir";
038      /** The test installation key. */
039      public static final String TEST_INSTALL_KEY =    "sensorbase.test.install";
040      /** The test domain key. */
041      public static final String TEST_DOMAIN_KEY =     "sensorbase.test.domain";
042      /** The sensorbase port key during testing. */
043      public static final String TEST_PORT_KEY =       "sensorbase.test.port";
044      /** The sensorbase db dir during testing. */
045      public static final String TEST_DB_DIR_KEY =       "sensorbase.test.db.dir";
046      /** The test admin email key. */
047      public static final String TEST_ADMIN_EMAIL_KEY =     "sensorbase.test.admin.email";
048      /** The test admin password. */
049      public static final String TEST_ADMIN_PASSWORD_KEY =   "sensorbase.test.admin.password";  
050      /** The test hostname. */
051      public static final String TEST_HOSTNAME_KEY =   "sensorbase.test.hostname";  
052      /** Whether to compress on startup. */
053      public static final String COMPRESS_ON_STARTUP_KEY =   "sensorbase.db.startup.compress";  
054      /** Whether to re-index on startup. */
055      public static final String REINDEX_ON_STARTUP_KEY =   "sensorbase.db.startup.reindex";  
056      /** Where we store the properties. */
057      private Properties properties; 
058      
059      private static String FALSE = "false";
060      
061      /**
062       * Creates a new ServerProperties instance. 
063       * Prints an error to the console if problems occur on loading. 
064       */
065      public ServerProperties() {
066        try {
067          initializeProperties();
068        }
069        catch (Exception e) {
070          System.out.println("Error initializing server properties.");
071        }
072      }
073      
074      /**
075       * Reads in the properties in ~/.hackystat/sensorbase.properties if this file exists,
076       * and provides default values for all properties not mentioned in this file.
077       * Will also add any pre-existing System properties that start with "sensorbase.".
078       * @throws Exception if errors occur.
079       */
080      private void initializeProperties () throws Exception {
081        String userHome = System.getProperty("user.home");
082        String userDir = System.getProperty("user.dir");
083        String hackyHome = userHome + "/.hackystat";
084        String sensorBaseHome = hackyHome + "/sensorbase"; 
085        String propFile = userHome + "/.hackystat/sensorbase/sensorbase.properties";
086        String defaultAdmin = "admin@hackystat.org";
087        this.properties = new Properties();
088        // Set defaults for 'standard' operation. These will override any previously
089        properties.setProperty(ADMIN_EMAIL_KEY, defaultAdmin);
090        properties.setProperty(ADMIN_PASSWORD_KEY, defaultAdmin);
091        properties.setProperty(CONTEXT_ROOT_KEY, "sensorbase");
092        properties.setProperty(DB_DIR_KEY, sensorBaseHome + "/db");
093        properties.setProperty(DB_IMPL_KEY, "org.hackystat.sensorbase.db.derby.DerbyImplementation");
094        properties.setProperty(HOSTNAME_KEY, "localhost");
095        properties.setProperty(LOGGING_LEVEL_KEY, "INFO");
096        properties.setProperty(RESTLET_LOGGING_KEY, FALSE);
097        properties.setProperty(SMTP_HOST_KEY, "mail.hawaii.edu");
098        properties.setProperty(PORT_KEY, "9876");
099        properties.setProperty(XML_DIR_KEY, userDir + "/xml");
100        properties.setProperty(TEST_DOMAIN_KEY, "hackystat.org");
101        properties.setProperty(TEST_INSTALL_KEY, FALSE);
102        properties.setProperty(TEST_ADMIN_EMAIL_KEY, defaultAdmin);
103        properties.setProperty(TEST_ADMIN_PASSWORD_KEY, defaultAdmin);
104        properties.setProperty(TEST_DB_DIR_KEY, sensorBaseHome + "/testdb");
105        properties.setProperty(TEST_PORT_KEY, "9976");
106        properties.setProperty(TEST_HOSTNAME_KEY, "localhost");
107        properties.setProperty(COMPRESS_ON_STARTUP_KEY, FALSE);
108        properties.setProperty(REINDEX_ON_STARTUP_KEY, FALSE);
109    
110        FileInputStream stream = null;
111        try {
112          stream = new FileInputStream(propFile);
113          properties.load(stream);
114          System.out.println("Loading SensorBase properties from: " + propFile);
115        }
116        catch (IOException e) {
117          System.out.println(propFile + " not found. Using default sensorbase properties.");
118        }
119        finally {
120          if (stream != null) {
121            stream.close();
122          }
123        }
124        addSensorBaseSystemProperties(this.properties);
125        trimProperties(properties);
126        
127        // Now add to System properties. Since the Mailer class expects to find this stuff on the 
128        // System Properties, we will add everything to it.  In general, however, clients should not
129        // use the System Properties to get at these values, since that precludes running several
130        // SensorBases in a single JVM.   And just is generally bogus. 
131        Properties systemProperties = System.getProperties();
132        systemProperties.putAll(properties);
133        System.setProperties(systemProperties);
134      }
135      
136      /**
137       * Finds any System properties whose key begins with "sensorbase.", and adds those
138       * key-value pairs to the passed Properties object. 
139       * @param properties The properties instance to be updated with the SensorBase system 
140       * properties. 
141       */
142      private void addSensorBaseSystemProperties(Properties properties) {
143        Properties systemProperties = System.getProperties();
144        for (Map.Entry<Object, Object> entry : systemProperties.entrySet()) {
145          String sysPropName = (String)entry.getKey();
146          if (sysPropName.startsWith("sensorbase.")) {
147            String sysPropValue = (String)entry.getValue();
148            properties.setProperty(sysPropName, sysPropValue);
149          }
150        }
151      }
152      
153      /**
154       * Sets the following properties to their "test" equivalents.
155       * <ul>
156       * <li> ADMIN_EMAIL_KEY
157       * <li> ADMIN_PASSWORD_KEY
158       * <li> HOSTNAME_KEY
159       * <li> DB_DIR_KEY
160       * <li> PORT_KEY
161       * <li> XML_DIR_KEY (if HACKYSTAT_SENSORBASE_HOME is in System properties).
162       * </ul>
163       * Also sets TEST_INSTALL_KEY to true.
164       */
165      public void setTestProperties() {
166        properties.setProperty(ADMIN_EMAIL_KEY, properties.getProperty(TEST_ADMIN_EMAIL_KEY));
167        properties.setProperty(ADMIN_PASSWORD_KEY, properties.getProperty(TEST_ADMIN_PASSWORD_KEY));
168        properties.setProperty(HOSTNAME_KEY, properties.getProperty(TEST_HOSTNAME_KEY));
169        properties.setProperty(DB_DIR_KEY, properties.getProperty(TEST_DB_DIR_KEY));
170        properties.setProperty(PORT_KEY, properties.getProperty(TEST_PORT_KEY));
171        properties.setProperty(TEST_INSTALL_KEY, "true");
172        // Change the XML dir location if HACKYSTAT_SENSORBASE_HOME exists. 
173        String sensorbaseHome = System.getProperty("HACKYSTAT_SENSORBASE_HOME");
174        if (sensorbaseHome != null) {
175          File file = new File(sensorbaseHome, "xml");
176          if (file.exists()) {
177            properties.setProperty(XML_DIR_KEY, file.getAbsolutePath());
178          }
179          else {
180            System.out.println("Bad HACKYSTAT_SENSORBASE_HOME: " + sensorbaseHome);
181          }
182        }
183        // Change the db implementation class if DB_IMPL_KEY is in system properties. 
184        String dbImpl = System.getProperty(DB_IMPL_KEY);
185        if (dbImpl != null) {
186          properties.setProperty(DB_IMPL_KEY, dbImpl);
187        }
188        trimProperties(properties);
189        // update the system properties object to reflect these new values. 
190        Properties systemProperties = System.getProperties();
191        systemProperties.putAll(properties);
192        System.setProperties(systemProperties);    
193      }
194    
195      /**
196       * Returns a string containing all current properties in alphabetical order.
197       * @return A string with the properties.  
198       */
199      public String echoProperties() {
200        String cr = System.getProperty("line.separator"); 
201        String eq = " = ";
202        String pad = "                ";
203        // Adding them to a treemap has the effect of alphabetizing them. 
204        TreeMap<String, String> alphaProps = new TreeMap<String, String>();
205        for (Map.Entry<Object, Object> entry : this.properties.entrySet()) {
206          String propName = (String)entry.getKey();
207          String propValue = (String)entry.getValue();
208          alphaProps.put(propName, propValue);
209        }
210        StringBuffer buff = new StringBuffer(25);
211        buff.append("SensorBase Properties:").append(cr);
212        for (String key : alphaProps.keySet()) {
213          buff.append(pad).append(key).append(eq).append(get(key)).append(cr);
214        }
215        return buff.toString();
216      }
217      
218      /**
219       * Returns the value of the Server Property specified by the key.
220       * @param key Should be one of the public static final strings in this class.
221       * @return The value of the key, or null if not found.
222       */
223      public String get(String key) {
224        return this.properties.getProperty(key);
225      }
226      
227      /**
228       * Ensures that the there is no leading or trailing whitespace in the property values.
229       * The fact that we need to do this indicates a bug in Java's Properties implementation to me. 
230       * @param properties The properties. 
231       */
232      private void trimProperties(Properties properties) {
233        // Have to do this iteration in a Java 5 compatible manner. no stringPropertyNames().
234        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
235          String propName = (String)entry.getKey();
236          properties.setProperty(propName, properties.getProperty(propName).trim());
237        }
238      }
239       
240      /**
241       * Returns the fully qualified host name, such as "http://localhost:9876/sensorbase/".
242       * @return The fully qualified host name.
243       */
244      public String getFullHost() {
245        return "http://" + get(HOSTNAME_KEY) + ":" + get(PORT_KEY) + "/" + get(CONTEXT_ROOT_KEY) + "/";
246      }
247      
248      /**
249       * True if the sensorbase.properties file indicates that the user wishes to compress the db
250       * on startup. 
251       * @return True if compress on startup.
252       */
253      public boolean compressOnStartup () {
254        return this.properties.getProperty(COMPRESS_ON_STARTUP_KEY).equalsIgnoreCase("true");
255      }
256      
257      /**
258       * True if the sensorbase.properties file indicates the user wants to reindex on startup. 
259       * @return True if reindex on startup. 
260       */
261      public boolean reindexOnStartup () {
262        return this.properties.getProperty(REINDEX_ON_STARTUP_KEY).equalsIgnoreCase("true");
263      }
264    }