001    package org.hackystat.dailyprojectdata.server;
002    
003    import java.io.FileInputStream;
004    import java.io.IOException;
005    import java.util.Map;
006    import java.util.Properties;
007    import java.util.TreeMap;
008    
009    /**
010     * Provides access to the values stored in the dailyprojectdata.properties file. 
011     * @author Philip Johnson
012     */
013    public class ServerProperties {
014      
015      /** The sensorbase fully qualified host name, such as http://localhost:9876/sensorbase. */
016      public static final String SENSORBASE_FULLHOST_KEY =  "dailyprojectdata.sensorbase.host";
017      /** The dailyprojectdata hostname key. */
018      public static final String HOSTNAME_KEY =        "dailyprojectdata.hostname";
019      /** The dailyprojectdata context root. */
020      public static final String CONTEXT_ROOT_KEY =     "dailyprojectdata.context.root";
021      /** The logging level key. */
022      public static final String LOGGING_LEVEL_KEY =   "dailyprojectdata.logging.level";
023      /** The dailyprojectdata port key. */
024      public static final String PORT_KEY =            "dailyprojectdata.port";
025      /** The XML directory key. */
026      public static final String XML_DIR_KEY =         "dailyprojectdata.xml.dir";
027      /** The Restlet Logging key. */
028      public static final String RESTLET_LOGGING_KEY = "dailyprojectdata.restlet.logging";
029      /** The dpd port key during testing. */
030      public static final String TEST_PORT_KEY =       "dailyprojectdata.test.port";
031      /** The test installation key. */
032      public static final String TEST_INSTALL_KEY =    "dailyprojectdata.test.install";
033      /** The test installation key. */
034      public static final String TEST_HOSTNAME_KEY =    "dailyprojectdata.test.hostname";
035      /** The test installation key. */
036      public static final String TEST_SENSORBASE_FULLHOST_KEY = "dailyprojectdata.test.sensorbase.host";
037      /** Indicates whether SensorBaseClient caching is enabled. */
038      public static final String CACHE_ENABLED = "dailyprojectdata.cache.enabled";
039      /** The maxLife in days for each instance in each SensorBaseClient cache. */
040      public static final String CACHE_MAX_LIFE = "dailyprojectdata.cache.max.life";
041      /** The total capacity of each SensorBaseClient cache. */
042      public static final String CACHE_CAPACITY = "dailyprojectdata.cache.capacity";
043      /** Whether or not the front side cache is enabled. */
044      public static final String FRONTSIDECACHE_ENABLED = "dailyprojectdata.cache.frontside.enabled";
045      
046      private String falseString = "false"; // for PMD.
047      
048      
049    
050      /** Where we store the properties. */
051      private Properties properties; 
052     
053      /**
054       * Creates a new ServerProperties instance. 
055       * Prints an error to the console if problems occur on loading. 
056       */
057      public ServerProperties() {
058        try {
059          initializeProperties();
060        }
061        catch (Exception e) {
062          System.out.println("Error initializing server properties: " + e.getMessage());
063        }
064      }
065      
066      /**
067       * Reads in the properties in ~/.hackystat/dailyprojectdata/dailyprojectdata.properties 
068       * if this file exists, and provides default values for all properties.
069       * @throws Exception if errors occur.
070       */
071      private void initializeProperties () throws Exception {
072        String userHome = System.getProperty("user.home");
073        String userDir = System.getProperty("user.dir");
074        String propFile = userHome + "/.hackystat/dailyprojectdata/dailyprojectdata.properties";
075        this.properties = new Properties();
076        // Set defaults
077        properties.setProperty(SENSORBASE_FULLHOST_KEY, "http://localhost:9876/sensorbase/");
078        properties.setProperty(HOSTNAME_KEY, "localhost");
079        properties.setProperty(PORT_KEY, "9877");
080        properties.setProperty(CONTEXT_ROOT_KEY, "dailyprojectdata");
081        properties.setProperty(LOGGING_LEVEL_KEY, "INFO");
082        properties.setProperty(RESTLET_LOGGING_KEY, falseString);
083        properties.setProperty(XML_DIR_KEY, userDir + "/xml");
084        properties.setProperty(TEST_PORT_KEY, "9977");
085        properties.setProperty(TEST_HOSTNAME_KEY, "localhost");
086        properties.setProperty(TEST_SENSORBASE_FULLHOST_KEY, "http://localhost:9976/sensorbase");
087        properties.setProperty(TEST_INSTALL_KEY, falseString);
088        properties.setProperty(CACHE_ENABLED, "true");
089        properties.setProperty(FRONTSIDECACHE_ENABLED, "true");
090        properties.setProperty(CACHE_MAX_LIFE, "365");
091        properties.setProperty(CACHE_CAPACITY, "500000");
092        FileInputStream stream = null;
093        try {
094          stream = new FileInputStream(propFile);
095          System.out.println("Loading DailyProjectData properties from: " + propFile);
096          properties.load(stream);
097        }
098        catch (IOException e) {
099          System.out.println(propFile + " not found. Using default dailyprojectdata properties.");
100        }
101        finally {
102          if (stream != null) {
103            stream.close();
104          }
105        }
106        
107        trimProperties(properties);
108        // make sure that SENSORBASE_HOST always has a final slash.
109        String sensorBaseHost = (String) properties.get(SENSORBASE_FULLHOST_KEY);
110        if (!sensorBaseHost.endsWith("/")) {
111          properties.put(SENSORBASE_FULLHOST_KEY, sensorBaseHost + "/");
112        }
113      }
114      
115      /**
116       * Sets the following properties' values to their "test" equivalent.
117       * <ul>
118       * <li> HOSTNAME_KEY
119       * <li> PORT_KEY
120       * <li> SENSORBASE_FULLHOST_KEY
121       * </ul>
122       * Also sets TEST_INSTALL_KEY's value to "true".
123       */
124      public void setTestProperties() {
125        properties.setProperty(HOSTNAME_KEY, properties.getProperty(TEST_HOSTNAME_KEY));
126        properties.setProperty(PORT_KEY, properties.getProperty(TEST_PORT_KEY));
127        properties.setProperty(SENSORBASE_FULLHOST_KEY, 
128        properties.getProperty(TEST_SENSORBASE_FULLHOST_KEY));
129        properties.setProperty(TEST_INSTALL_KEY, "true");
130        properties.setProperty(CACHE_ENABLED, falseString);
131        properties.setProperty(FRONTSIDECACHE_ENABLED, falseString);
132        trimProperties(properties);
133      }
134      
135      /**
136       * Returns the value of the Server Property specified by the key.
137       * @param key Should be one of the public static final strings in this class.
138       * @return The value of the key, or null if not found.
139       */
140      public String get(String key) {
141        return this.properties.getProperty(key);
142      }
143      
144      /**
145       * Ensures that the there is no leading or trailing whitespace in the property values.
146       * The fact that we need to do this indicates a bug in Java's Properties implementation to me. 
147       * @param properties The properties. 
148       */
149      private void trimProperties(Properties properties) {
150        // Have to do this iteration in a Java 5 compatible manner. no stringPropertyNames().
151        for (Map.Entry<Object, Object> entry : properties.entrySet()) {
152          String propName = (String)entry.getKey();
153          properties.setProperty(propName, properties.getProperty(propName).trim());
154        }
155      }
156      
157      /**
158       * Returns the fully qualified host name, such as "http://localhost:9877/dailyprojectdata/".
159       * @return The fully qualified host name.
160       */
161      public String getFullHost() {
162        return "http://" + get(HOSTNAME_KEY) + ":" + get(PORT_KEY) + "/" + get(CONTEXT_ROOT_KEY) + "/";
163      }
164      
165      /**
166       * Returns true if caching is enabled in this service. 
167       * @return True if caching enabled.
168       */
169      public boolean isCacheEnabled() {
170        return "True".equalsIgnoreCase(this.properties.getProperty(CACHE_ENABLED));
171      }
172      
173      /**
174       * Returns true if front side caching is enabled in this service. 
175       * @return True if caching enabled.
176       */
177      public boolean isFrontSideCacheEnabled() {
178        return "True".equalsIgnoreCase(this.properties.getProperty(FRONTSIDECACHE_ENABLED));
179      }
180      
181      /**
182       * Returns the caching max life as a double.
183       * If the property has an illegal value, then return the default. 
184       * @return The max life of each instance in the cache.
185       */
186      public double getCacheMaxLife() {
187        String maxLifeString = this.properties.getProperty(CACHE_MAX_LIFE);
188        double maxLife = 0;
189        try {
190          maxLife = Double.valueOf(maxLifeString);
191        }
192        catch (Exception e) {
193          System.out.println("Illegal cache max life: " + maxLifeString + ". Using default.");
194          maxLife = 365D;
195        }
196        return maxLife;
197      }
198      
199      /**
200       * Returns the in-memory capacity for each cache.
201       * If the property has an illegal value, then return the default. 
202       * @return The in-memory capacity.
203       */
204      public long getCacheCapacity() {
205        String capacityString = this.properties.getProperty(CACHE_CAPACITY);
206        long capacity = 0;
207        try {
208          capacity = Long.valueOf(capacityString);
209        }
210        catch (Exception e) {
211          System.out.println("Illegal cache capacity: " + capacityString + ". Using default.");
212          capacity = 500000L;
213        }
214        return capacity;
215      }
216      
217      /**
218       * Returns a string containing all current properties in alphabetical order.
219       * @return A string with the properties.  
220       */
221      public String echoProperties() {
222        String cr = System.getProperty("line.separator"); 
223        String eq = " = ";
224        String pad = "                ";
225        // Adding them to a treemap has the effect of alphabetizing them. 
226        TreeMap<String, String> alphaProps = new TreeMap<String, String>();
227        for (Map.Entry<Object, Object> entry : this.properties.entrySet()) {
228          String propName = (String)entry.getKey();
229          String propValue = (String)entry.getValue();
230          alphaProps.put(propName, propValue);
231        }
232        StringBuffer buff = new StringBuffer(30);
233        buff.append("DailyProjectData Properties:").append(cr);
234        for (String key : alphaProps.keySet()) {
235          buff.append(pad).append(key).append(eq).append(get(key)).append(cr);
236        }
237        return buff.toString();
238      }
239    
240    }