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