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