001 package org.hackystat.sensorshell; 002 003 import java.io.File; 004 import java.io.FileInputStream; 005 import java.util.Properties; 006 import java.util.TreeMap; 007 import java.util.Map.Entry; 008 import java.util.logging.Level; 009 import java.util.logging.Logger; 010 011 import org.hackystat.sensorbase.client.SensorBaseClient; 012 import org.hackystat.utilities.logger.HackystatLogger; 013 import org.hackystat.utilities.home.HackystatUserHome; 014 015 /** 016 * Provides Hackystat sensors with access to standard Hackystat sensorshell properties. These 017 * properties are generally stored in ~/.hackystat/sensorshell/sensorshell.properties. 018 * This class manages access to those files for sensors, 019 * performs type conversions on the String-based properties when appropriate, and sets default 020 * values when the properties are missing or incorrectly specified. 021 * <p> 022 * See the public static final Strings for descriptions of the "standard" Hackystat sensorshell 023 * properties. 024 * 025 * @author Philip M. Johnson, Aaron A. Kagawa 026 */ 027 public class SensorShellProperties { 028 029 /** 030 * The property key retrieving an URL indicating the location of the SensorBase host. 031 * This a required property; if not supplied, instantiation will fail. 032 * Example: "http://dasha.ics.hawaii.edu:9876/sensorbase". 033 * No default value. 034 */ 035 public static final String SENSORSHELL_SENSORBASE_HOST_KEY = "sensorshell.sensorbase.host"; 036 037 /** 038 * The property key retrieving the user account associated with the SensorBase. 039 * This a required property; if not supplied, instantiation will fail. 040 * Example: "johnson@hawaii.edu". 041 * No default value. 042 */ 043 public static final String SENSORSHELL_SENSORBASE_USER_KEY = "sensorshell.sensorbase.user"; 044 045 /** 046 * The property key retrieving the password associated with the user. 047 * This a required property; if not supplied, instantiation will fail. 048 * Example: "xykdclwck". 049 * No default value. 050 */ 051 public static final String SENSORSHELL_SENSORBASE_PASSWORD_KEY = 052 "sensorshell.sensorbase.password"; 053 054 /** 055 * The property key retrieving the timeout value (in seconds) for SensorShell HTTP requests. 056 * Default: "10". 057 */ 058 public static final String SENSORSHELL_TIMEOUT_KEY = "sensorshell.timeout"; 059 060 /** 061 * The property key retrieving the timeout value (in seconds) for SensorShell 'PING' requests. 062 * Default: "2". 063 */ 064 public static final String SENSORSHELL_PING_TIMEOUT_KEY = "sensorshell.timeout.ping"; 065 066 /** 067 * The property key retrieving a boolean indicating whether the MultiSensorShell is enabled. 068 * If true, offline recovery and storage will be automatically disabled. 069 * Default: "false". 070 */ 071 public static final String SENSORSHELL_MULTISHELL_ENABLED_KEY = "sensorshell.multishell.enabled"; 072 073 074 /** 075 * The property key retrieving an integer indicating the number of shells to instantiate when 076 * the MultiSensorShell is enabled. 077 * Default: "10". 078 */ 079 public static final String SENSORSHELL_MULTISHELL_NUMSHELLS_KEY = 080 "sensorshell.multishell.numshells"; 081 082 /** 083 * The property key retrieving an integer indicating the number of instances to send to a single 084 * shell in the MultiSensorShell at once. We make this one less than the default multishell 085 * maxbuffer value so that the non-blocking autosend has chance to run rather than the 086 * blocking send() resulting from hitting the maxbuffer size. 087 * Default: "499". 088 */ 089 public static final String SENSORSHELL_MULTISHELL_BATCHSIZE_KEY = 090 "sensorshell.multishell.batchsize"; 091 092 /** 093 * The property key retrieving an integer indicating the maximum number of instances to buffer 094 * in each shell in the MultiSensorShell before a blocking send() is invoked. 095 * Default: "500". 096 */ 097 public static final String SENSORSHELL_MULTISHELL_MAXBUFFER_KEY = 098 "sensorshell.multishell.maxbuffer"; 099 100 /** 101 * The property key retrieving a double indicating how many minutes between autosends of 102 * sensor data for each shell in a MultiShell. If "0.0", then sensor data is not sent unless the 103 * client invokes the send() method explicitly. This value is typically around 0.05 to 0.10 104 * (i.e. 3 to 6 seconds). 105 * Default: "0.05". 106 */ 107 public static final String SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY = 108 "sensorshell.multishell.autosend.timeinterval"; 109 110 /** 111 * The property key retrieving a boolean indicating if data will be cached locally if the 112 * SensorBase cannot be contacted. 113 * Default: "true" if multi-shell is disabled. If multishell is enabled, then set to false. 114 */ 115 public static final String SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY = 116 "sensorshell.offline.cache.enabled"; 117 118 /** 119 * The property key retrieving a boolean indicating if offline data will be recovered at startup 120 * if the SensorBase can be contacted. 121 * Default: "true" if multi-shell is disabled. If multishell is enabled, then set to false. 122 */ 123 public static final String SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY = 124 "sensorshell.offline.recovery.enabled"; 125 126 /** 127 * The property key retrieving an integer indicating the number of seconds between "wakeups" of 128 * tool subprocesses that check for state changes. Typically used by editors such as Emacs or 129 * Eclipse to provide a standard measure of developer activity. 130 * Default: "30". 131 */ 132 public static final String SENSORSHELL_STATECHANGE_INTERVAL_KEY = 133 "sensorshell.statechange.interval"; 134 135 /** 136 * The property key retrieving a double indicating how many minutes between autosends of 137 * sensor data when not in MultiShell mode. If "0.0", then sensor data is not sent unless the 138 * client invokes the send() method explicitly. This value typically varies 139 * from 1.0 to 10.0 (i.e. 1 to 10 minutes). 140 * Default: "1.0". 141 */ 142 public static final String SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY = 143 "sensorshell.autosend.timeinterval"; 144 145 /** 146 * The property key retrieving an integer indicating the maximum number of sensor instances to 147 * buffer locally between autosends of sensor data. If "0", then no maximum size is defined. 148 * Default: "250". Note that this is the value used when multishell is not enabled, otherwise 149 * the value associated with SENSORSHELL_MULTISHELL_MAXBUFFER_KEY is used. 150 */ 151 public static final String SENSORSHELL_AUTOSEND_MAXBUFFER_KEY = 152 "sensorshell.autosend.maxbuffer"; 153 154 /** 155 * The property key retrieving a string indicating the logging level for the SensorShell(s). 156 * Default: "INFO". 157 */ 158 public static final String SENSORSHELL_LOGGING_LEVEL_KEY = "sensorshell.logging.level"; 159 160 /** The internal properties object. */ 161 private Properties sensorProps = new Properties(); 162 163 private File sensorShellPropertiesFile = new File(HackystatUserHome.getHome(), 164 ".hackystat/sensorshell/sensorshell.properties"); 165 166 private Logger logger = HackystatLogger.getLogger("org.hackystat.sensorshell.properties", 167 "sensorshell"); 168 169 /** The default timeout in seconds. */ 170 private int timeout = 10; 171 /** The default ping timeout in seconds. */ 172 private int pingTimeout = 2; 173 /** MultiShell processing is disabled by default. */ 174 private boolean multiShellEnabled = false; 175 /** If MultiShell processing is enabled, then the default number of shells is 10. */ 176 private int multiShellNumShells = 10; 177 /** If MultiShell processing is enabled, then the default num instances in a row is 499. */ 178 private int multiShellBatchSize = 499; 179 /** If MultiShell processing is enabled, then the default max buffer is 500. */ 180 private int multiShellMaxBuffer = 500; 181 /** If MultiShell processing is enabled, then the default autosend time interval is 0.10 . */ 182 private double multiShellAutoSendTimeInterval = 0.05; 183 /** Offline caching of data is enabled by default. */ 184 private boolean offlineCacheEnabled = true; 185 /** Recovery of offline data upon initialization is enabled by default. */ 186 private boolean offlineRecoveryEnabled = true; 187 /** The default state change interval is 30 seconds. */ 188 private int statechangeInterval = 30; 189 /** The default autosend time interval is 1.0 minutes. */ 190 private double autosendTimeInterval = 1.0; 191 /** The default maximum number of buffered instances is 250. */ 192 private int autosendMaxBuffer = 250; 193 /** Holds the required sensorbase host. */ 194 private String sensorBaseHost = null; 195 /** Holds the required user. */ 196 private String user = null; 197 /** Holds the required password. */ 198 private String password = null; 199 /** Holds the default logging level. */ 200 private Level loggingLevel = Level.INFO; 201 202 /** 203 * Initializes SensorShell properties using the default sensorshell.properties file. 204 * It could be located in user.home, or hackystat.user.home (if the user has set the 205 * latter in the System properties before invoking this constructor.) 206 * @throws SensorShellException If the SensorProperties instance cannot be 207 * instantiated due to a missing host, user, and/or password properties. 208 */ 209 public SensorShellProperties() throws SensorShellException { 210 this(new File(HackystatUserHome.getHome(), 211 ".hackystat/sensorshell/sensorshell.properties")); 212 } 213 214 /** 215 * Creates a SensorShellProperties instance using the specified properties file. 216 * All unspecified properties are set to their built-in default values. 217 * @param sensorFile The sensorshell properties file to read. 218 * @throws SensorShellException If the SensorProperties instance cannot be 219 * instantiated due to a missing host, user, and/or password properties. 220 */ 221 public SensorShellProperties(File sensorFile) throws SensorShellException { 222 this.sensorShellPropertiesFile = sensorFile; 223 setDefaultSensorShellProperties(true); 224 FileInputStream fileStream = null; 225 try { 226 fileStream = new FileInputStream(this.sensorShellPropertiesFile); 227 this.sensorProps.load(fileStream); 228 validateProperties(); 229 } 230 catch (Exception e) { 231 String errMsg = "SensorShellProperties error loading: " + sensorFile; 232 this.logger.warning(errMsg); 233 throw new SensorShellException(errMsg, e); 234 } 235 finally { 236 try { 237 if (fileStream != null) { 238 fileStream.close(); 239 } 240 } 241 catch (Exception e) { //NOPMD 242 // Don't say anything. 243 } 244 } 245 } 246 247 /** 248 * Constructs a "basic" instance with the supplied three required properties. 249 * All other properties are assigned values from sensorshell.properties, or the 250 * built-in defaults if not specified there. 251 * <p> 252 * Use SensorShellProperties.getTestInstance to create an instance for testing purposes, since 253 * it will override certain properties that may be present in the sensorshell.properties file. 254 * @param host The hackystat host. 255 * @param email The user's email. 256 * @param password The user's password. 257 * @throws SensorShellException If the SensorProperties instance cannot be 258 * instantiated due to a missing host, user, and/or password properties. 259 */ 260 public SensorShellProperties(String host, String email, String password) 261 throws SensorShellException { 262 this.setPropertiesFromFile(sensorShellPropertiesFile); 263 setDefaultSensorShellProperties(false); 264 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_HOST_KEY, host); 265 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_USER_KEY, email); 266 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_PASSWORD_KEY, password); 267 validateProperties(); 268 } 269 270 /** 271 * Constructs an instance with the supplied three required properties and any other 272 * properties provided in the properties argument. 273 * Any remaining properties are assigned values from sensorshell.properties, or the 274 * built-in defaults if not specified there. 275 * @param host The hackystat host. 276 * @param email The user's email. 277 * @param password The user's password. 278 * @param properties A properties instance with other properties. 279 * @param overrideFile If true, then the passed properties override sensorshell.properties. 280 * @throws SensorShellException If the SensorProperties instance cannot be 281 * instantiated due to a missing host, user, and/or password properties. 282 */ 283 public SensorShellProperties(String host, String email, String password, Properties properties, 284 boolean overrideFile) throws SensorShellException { 285 if (overrideFile) { 286 this.setPropertiesFromFile(sensorShellPropertiesFile); 287 this.sensorProps.putAll(properties); 288 this.setDefaultSensorShellProperties(false); 289 } 290 else { 291 this.sensorProps.putAll(properties); 292 this.setPropertiesFromFile(sensorShellPropertiesFile); 293 this.setDefaultSensorShellProperties(false); 294 } 295 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_HOST_KEY, host); 296 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_USER_KEY, email); 297 this.sensorProps.setProperty(SENSORSHELL_SENSORBASE_PASSWORD_KEY, password); 298 validateProperties(); 299 } 300 301 /** 302 * Creates and returns a new SensorShellProperties instance which is initialized to the contents 303 * of the passed SensorProperties instance, with additional new properties overriding the previous 304 * selection. 305 * @param orig The original properties. 306 * @param newProps The replacing properties. 307 * @throws SensorShellException If the SensorProperties instance cannot be instantiated due to 308 * invalid or missing properties. 309 */ 310 public SensorShellProperties(SensorShellProperties orig, Properties newProps) 311 throws SensorShellException { 312 this.sensorProps = orig.sensorProps; 313 this.sensorShellPropertiesFile = orig.sensorShellPropertiesFile; 314 this.sensorProps.putAll(newProps); 315 validateProperties(); 316 } 317 318 319 /** 320 * Constructs a "test" instance with the supplied three required properties. 321 * <p> 322 * This testing-only factory class sets the three required properties, and overrides the 323 * following properties for testing purposes: 324 * <ul> 325 * <li> Disables offline recovery and caching. 326 * <li> Disables logging. 327 * <li> Disables multishell. 328 * </ul> 329 * All remaining properties are set to their built-in default values. 330 * @param host The hackystat host. 331 * @param email The user's email. 332 * @param password The user's password. 333 * @return the new SensorShellProperties instance. 334 * @throws SensorShellException If the SensorProperties instance cannot be 335 * instantiated due to a missing host, user, and/or password properties. 336 */ 337 public static SensorShellProperties getTestInstance(String host, String email, String password) 338 throws SensorShellException { 339 String falseStr = "false"; 340 Properties props = new Properties(); 341 props.setProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY, "250"); 342 props.setProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, "1.0"); 343 props.setProperty(SENSORSHELL_LOGGING_LEVEL_KEY, "OFF"); 344 props.setProperty(SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY, "0.05"); 345 props.setProperty(SENSORSHELL_MULTISHELL_BATCHSIZE_KEY, "499"); 346 props.setProperty(SENSORSHELL_MULTISHELL_ENABLED_KEY, falseStr); 347 props.setProperty(SENSORSHELL_MULTISHELL_MAXBUFFER_KEY, "500"); 348 props.setProperty(SENSORSHELL_MULTISHELL_NUMSHELLS_KEY, "10"); 349 props.setProperty(SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY, falseStr); 350 props.setProperty(SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY, falseStr); 351 props.setProperty(SENSORSHELL_SENSORBASE_HOST_KEY, host); 352 props.setProperty(SENSORSHELL_SENSORBASE_PASSWORD_KEY, password); 353 props.setProperty(SENSORSHELL_SENSORBASE_USER_KEY, email); 354 props.setProperty(SENSORSHELL_STATECHANGE_INTERVAL_KEY, "30"); 355 props.setProperty(SENSORSHELL_TIMEOUT_KEY, "10"); 356 props.setProperty(SENSORSHELL_PING_TIMEOUT_KEY, "5"); 357 return new SensorShellProperties(props, true); 358 } 359 360 /** 361 * Creates a SensorShell properties instance, initializing it using the passed properties as 362 * well as any settings found in the sensorshell.properties file. 363 * If overrideFile is true, then the passed properties and settings will override matching 364 * properties and settings found in the sensorshell.properties file. If false, then the 365 * property settings found in the sensorshell.properties will override the passed properties. 366 * <p> 367 * This constructor enables the user to provide default settings in sensorshell.properties for 368 * things like MultiSensorShell, but enable a tool to provide command line args that could 369 * override these defaults. The tool could do this by processing its command line args, then 370 * creating a Properties instance containing the overridding values, then passing that in to 371 * this constructor. 372 * <p> 373 * If overrideFile is false, then these properties will only take effect if the user has not 374 * specified them in their sensorshell.properties file. 375 * <p> 376 * Standard properties not specified by either the sensorshell.properties file or the passed 377 * properties instance will be given default values. 378 * 379 * @param properties The properties. 380 * @param overrideFile If true, then the passed properties will override any matching 381 * sensorshell.properties properties. 382 * @throws SensorShellException if problems occur. 383 */ 384 public SensorShellProperties(Properties properties, boolean overrideFile) 385 throws SensorShellException { 386 if (overrideFile) { 387 this.setPropertiesFromFile(sensorShellPropertiesFile); 388 this.sensorProps.putAll(properties); 389 this.setDefaultSensorShellProperties(false); 390 } 391 else { 392 this.sensorProps.putAll(properties); 393 this.setPropertiesFromFile(sensorShellPropertiesFile); 394 this.setDefaultSensorShellProperties(false); 395 } 396 validateProperties(); 397 } 398 399 400 /** 401 * Sets the internal sensorshell properties instance with the contents of file. If the file 402 * cannot be found, then the sensor properties instance is unchanged. 403 * @param file The file to be processed. 404 */ 405 private void setPropertiesFromFile(File file) { 406 FileInputStream fileStream = null; 407 try { 408 fileStream = new FileInputStream(file); 409 this.sensorProps.load(fileStream); 410 } 411 catch (Exception e) { //NOPMD 412 } 413 finally { 414 try { 415 if (fileStream != null) { 416 fileStream.close(); 417 } 418 } 419 catch (Exception e) { 420 System.err.println("Error closing stream: " + e); 421 } 422 } 423 } 424 425 426 /** 427 * Ensures that the SensorProperties instance has values defined for all standard 428 * properties with default values. Can optionally override existing values for these 429 * standard properties. Does not check for or provide values for the three required properties. 430 * @param overridePreexisting If the default values should override the preexisting values. 431 */ 432 private void setDefaultSensorShellProperties (boolean overridePreexisting) { 433 setDefaultProperty(SENSORSHELL_TIMEOUT_KEY, 434 String.valueOf(timeout), overridePreexisting); 435 setDefaultProperty(SENSORSHELL_PING_TIMEOUT_KEY, 436 String.valueOf(pingTimeout), overridePreexisting); 437 setDefaultProperty(SENSORSHELL_MULTISHELL_ENABLED_KEY, 438 String.valueOf(multiShellEnabled), overridePreexisting); 439 setDefaultProperty(SENSORSHELL_MULTISHELL_NUMSHELLS_KEY, 440 String.valueOf(multiShellNumShells), overridePreexisting); 441 setDefaultProperty(SENSORSHELL_MULTISHELL_BATCHSIZE_KEY, 442 String.valueOf(multiShellBatchSize), overridePreexisting); 443 setDefaultProperty(SENSORSHELL_MULTISHELL_MAXBUFFER_KEY, 444 String.valueOf(multiShellMaxBuffer), overridePreexisting); 445 setDefaultProperty(SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY, 446 String.valueOf(multiShellAutoSendTimeInterval), overridePreexisting); 447 setDefaultProperty(SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY, 448 String.valueOf(offlineCacheEnabled), overridePreexisting); 449 setDefaultProperty(SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY, 450 String.valueOf(offlineRecoveryEnabled), overridePreexisting); 451 setDefaultProperty(SENSORSHELL_STATECHANGE_INTERVAL_KEY, 452 String.valueOf(statechangeInterval), overridePreexisting); 453 setDefaultProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, 454 String.valueOf(autosendTimeInterval), overridePreexisting); 455 setDefaultProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY, 456 String.valueOf(autosendMaxBuffer), overridePreexisting); 457 setDefaultProperty(SENSORSHELL_LOGGING_LEVEL_KEY, 458 String.valueOf(loggingLevel), overridePreexisting); 459 } 460 461 /** 462 * Checks that the standard sensor properties have valid values. 463 * <p> 464 * The approach is that the instance variables (timeout, multishellEnabled, etc.) always start off 465 * holding the default values, while the properties start off with potentially new and 466 * potentially invalid values. We attempt to set the instance variables to the new values 467 * in the properties. If this fails, we use the instance variables to reset the properties 468 * to the defaults. 469 * <p> 470 * The approach for the required variables is that if they do not have values, we throw 471 * an error. 472 * @throws SensorShellException if problems occur. 473 */ 474 private final void validateProperties() throws SensorShellException { 475 String newValue = null; 476 String origValue = null; 477 String errMsg = "SensorProperties instantiation error: "; 478 // TIMEOUT 479 try { 480 origValue = String.valueOf(this.timeout); 481 newValue = this.getProperty(SENSORSHELL_TIMEOUT_KEY); 482 this.timeout = Integer.parseInt(newValue); 483 if (this.timeout < 1) { 484 this.logger.warning(errMsg + SENSORSHELL_TIMEOUT_KEY + " " + newValue); 485 this.sensorProps.setProperty(SENSORSHELL_TIMEOUT_KEY, origValue); 486 this.timeout = Integer.parseInt(origValue); 487 } 488 } 489 catch (Exception e) { 490 this.logger.warning(errMsg + SENSORSHELL_TIMEOUT_KEY + " " + newValue); 491 this.sensorProps.setProperty(SENSORSHELL_TIMEOUT_KEY, origValue); 492 } 493 // Now set the SensorBaseClient default timeout. 494 System.setProperty(SensorBaseClient.SENSORBASECLIENT_TIMEOUT_KEY, 495 String.valueOf(this.timeout * 1000)); 496 // PING TIMEOUT 497 try { 498 origValue = String.valueOf(this.pingTimeout); 499 newValue = this.getProperty(SENSORSHELL_PING_TIMEOUT_KEY); 500 this.pingTimeout = Integer.parseInt(newValue); 501 if (this.pingTimeout < 1) { 502 this.logger.warning(errMsg + SENSORSHELL_PING_TIMEOUT_KEY + " " + newValue); 503 this.sensorProps.setProperty(SENSORSHELL_PING_TIMEOUT_KEY, origValue); 504 this.pingTimeout = Integer.parseInt(origValue); 505 } 506 } 507 catch (Exception e) { 508 this.logger.warning(errMsg + SENSORSHELL_PING_TIMEOUT_KEY + " " + newValue); 509 this.sensorProps.setProperty(SENSORSHELL_PING_TIMEOUT_KEY, origValue); 510 } 511 // MULTISHELL_ENABLED 512 try { 513 origValue = String.valueOf(multiShellEnabled); 514 newValue = this.getProperty(SENSORSHELL_MULTISHELL_ENABLED_KEY); 515 this.multiShellEnabled = Boolean.parseBoolean(newValue); 516 } 517 catch (Exception e) { 518 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_ENABLED_KEY + " " + newValue); 519 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_ENABLED_KEY, origValue); 520 } 521 // MULTISHELL_NUMSHELLS 522 try { 523 origValue = String.valueOf(multiShellNumShells); 524 newValue = this.getProperty(SENSORSHELL_MULTISHELL_NUMSHELLS_KEY); 525 this.multiShellNumShells = Integer.parseInt(newValue); 526 if (this.multiShellNumShells < 1) { 527 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_NUMSHELLS_KEY + " " + newValue); 528 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_NUMSHELLS_KEY, origValue); 529 this.multiShellNumShells = Integer.parseInt(origValue); 530 } 531 } 532 catch (Exception e) { 533 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_NUMSHELLS_KEY + " " + newValue); 534 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_NUMSHELLS_KEY, origValue); 535 } 536 // MULTISHELL_BATCHSIZE 537 try { 538 origValue = String.valueOf(multiShellBatchSize); 539 newValue = this.getProperty(SENSORSHELL_MULTISHELL_BATCHSIZE_KEY); 540 this.multiShellBatchSize = Integer.parseInt(newValue); 541 if (this.multiShellBatchSize < 1) { 542 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_BATCHSIZE_KEY + " " + newValue); 543 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_BATCHSIZE_KEY, origValue); 544 this.multiShellBatchSize = Integer.parseInt(origValue); 545 } 546 } 547 catch (Exception e) { 548 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_BATCHSIZE_KEY + " " + newValue); 549 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_BATCHSIZE_KEY, origValue); 550 } 551 // MULTISHELL_MAXBUFFER 552 try { 553 origValue = String.valueOf(multiShellMaxBuffer); 554 newValue = this.getProperty(SENSORSHELL_MULTISHELL_MAXBUFFER_KEY); 555 this.multiShellMaxBuffer = Integer.parseInt(newValue); 556 if (this.multiShellMaxBuffer < 1) { 557 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_MAXBUFFER_KEY + " " + newValue); 558 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_MAXBUFFER_KEY, origValue); 559 this.multiShellMaxBuffer = Integer.parseInt(origValue); 560 } 561 } 562 catch (Exception e) { 563 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_MAXBUFFER_KEY + " " + newValue); 564 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_MAXBUFFER_KEY, origValue); 565 } 566 // MULTISHELL_AUTOSEND_TIMEINTERVAL 567 try { 568 origValue = String.valueOf(multiShellAutoSendTimeInterval); 569 newValue = this.getProperty(SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY); 570 this.multiShellAutoSendTimeInterval = Double.parseDouble(newValue); 571 if (this.multiShellAutoSendTimeInterval < 0.0) { 572 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY + newValue); 573 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY, origValue); 574 this.multiShellAutoSendTimeInterval = Double.parseDouble(origValue); 575 } 576 } 577 catch (Exception e) { 578 this.logger.warning(errMsg + SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY + newValue); 579 this.sensorProps.setProperty(SENSORSHELL_MULTISHELL_AUTOSEND_TIMEINTERVAL_KEY, origValue); 580 } 581 // OFFLINE_CACHE_ENABLED 582 try { 583 origValue = String.valueOf(offlineCacheEnabled); 584 newValue = this.getProperty(SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY); 585 this.offlineCacheEnabled = Boolean.parseBoolean(newValue); 586 } 587 catch (Exception e) { 588 this.logger.warning(errMsg + SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY + " " + newValue); 589 this.sensorProps.setProperty(SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY, origValue); 590 } 591 if (this.multiShellEnabled && this.offlineCacheEnabled) { 592 this.logger.warning("Warning: Offline cache disabled since multishell enabled."); 593 this.offlineCacheEnabled = false; 594 } 595 596 // OFFLINE_RECOVERY_ENABLED 597 try { 598 origValue = String.valueOf(offlineRecoveryEnabled); 599 newValue = this.getProperty(SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY); 600 this.offlineRecoveryEnabled = Boolean.parseBoolean(newValue); 601 } 602 catch (Exception e) { 603 this.logger.warning(errMsg + SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY + " " + newValue); 604 this.sensorProps.setProperty(SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY, origValue); 605 } 606 if (this.multiShellEnabled && this.offlineRecoveryEnabled) { 607 this.logger.warning("Warning: Offline recovery disabled since multishell enabled."); 608 this.offlineRecoveryEnabled = false; 609 } 610 // STATECHANGE_INTERVAL 611 try { 612 origValue = String.valueOf(statechangeInterval); 613 newValue = this.getProperty(SENSORSHELL_STATECHANGE_INTERVAL_KEY); 614 this.statechangeInterval = Integer.parseInt(newValue); 615 if (this.statechangeInterval < 1) { 616 this.logger.warning(errMsg + SENSORSHELL_STATECHANGE_INTERVAL_KEY + " " + newValue); 617 this.sensorProps.setProperty(SENSORSHELL_STATECHANGE_INTERVAL_KEY, origValue); 618 this.statechangeInterval = Integer.parseInt(origValue); 619 } 620 } 621 catch (Exception e) { 622 this.logger.warning(errMsg + SENSORSHELL_STATECHANGE_INTERVAL_KEY + " " + newValue); 623 this.sensorProps.setProperty(SENSORSHELL_STATECHANGE_INTERVAL_KEY, origValue); 624 } 625 // AUTOSEND_TIMEINTERVAL (Single Shell) 626 try { 627 origValue = String.valueOf(autosendTimeInterval); 628 newValue = this.getProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY); 629 this.autosendTimeInterval = Double.parseDouble(newValue); 630 if (this.autosendTimeInterval < 0.0) { 631 this.logger.warning(errMsg + SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY + " " + newValue); 632 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, origValue); 633 this.autosendTimeInterval = Double.parseDouble(origValue); 634 } 635 } 636 catch (Exception e) { 637 this.logger.warning(errMsg + SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY + " " + newValue); 638 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, origValue); 639 } 640 // AUTOSEND_MAXBUFFER 641 try { 642 origValue = String.valueOf(autosendMaxBuffer); 643 newValue = this.getProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY); 644 this.autosendMaxBuffer = Integer.parseInt(newValue); 645 if (this.autosendMaxBuffer < 1) { 646 this.logger.warning(errMsg + SENSORSHELL_AUTOSEND_MAXBUFFER_KEY + " " + newValue); 647 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY, origValue); 648 this.autosendMaxBuffer = Integer.parseInt(origValue); 649 } 650 } 651 catch (Exception e) { 652 this.logger.warning(errMsg + SENSORSHELL_AUTOSEND_MAXBUFFER_KEY + " " + newValue); 653 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY, origValue); 654 } 655 // LOGGING_LEVEL 656 try { 657 origValue = String.valueOf(loggingLevel); 658 newValue = this.getProperty(SENSORSHELL_LOGGING_LEVEL_KEY); 659 this.loggingLevel = Level.parse(newValue); 660 } 661 catch (Exception e) { 662 this.logger.warning(errMsg + SENSORSHELL_LOGGING_LEVEL_KEY + " " + newValue); 663 this.sensorProps.setProperty(SENSORSHELL_LOGGING_LEVEL_KEY, origValue); 664 } 665 666 String errInfo = " You might wish to check the settings in " + 667 sensorShellPropertiesFile.getAbsolutePath(); 668 669 670 // SENSORBASE_HOST 671 this.sensorBaseHost = this.getProperty(SENSORSHELL_SENSORBASE_HOST_KEY); 672 if (this.sensorBaseHost == null) { 673 throw new SensorShellException("Missing sensorshell.sensorbase.host." + errInfo); 674 } 675 if (!this.sensorBaseHost.endsWith("/")) { 676 this.sensorBaseHost = this.sensorBaseHost + "/"; 677 } 678 // SENSORBASE_USER 679 this.user = this.getProperty(SENSORSHELL_SENSORBASE_USER_KEY); 680 if (this.user == null) { 681 throw new SensorShellException("Missing sensorshell.sensorbase.user." + errInfo); 682 } 683 // SENSORBASE_PASSWORD 684 this.password = this.getProperty(SENSORSHELL_SENSORBASE_PASSWORD_KEY); 685 if (this.password == null) { 686 throw new SensorShellException("Missing sensorshell.sensorbase.password." + errInfo); 687 } 688 } 689 690 /** 691 * Ensures that the specified property has a value in this sensorshell properties. 692 * If overridePreexisting is true, then property will be set to value regardless of whether 693 * it had a previously existing value. 694 * If overridePreexisting is false, then property will be set to value only if there is no 695 * currently defined property. 696 * @param property The property to set, if not yet defined. 697 * @param value The value to set the property to, if not yet defined. 698 * @param overridePreexisting True if property will be set to value even if it currently exists. 699 */ 700 private void setDefaultProperty(String property, String value, boolean overridePreexisting) { 701 if (overridePreexisting) { 702 this.sensorProps.setProperty(property, value); 703 } 704 else if (!this.sensorProps.containsKey(property)) { 705 this.sensorProps.setProperty(property, value); 706 } 707 } 708 709 /** 710 * Returns the trimmed property value associated with the property key. This is useful for 711 * clients who want to look for "non-standard" properties supplied in sensorshell.properties. 712 * For standard properties, clients should use the accessor methods, since these will perform 713 * type conversion. 714 * @param key The parameter key. 715 * @return The trimmed property value associated with this property key, or null if not found. 716 */ 717 public final String getProperty(String key) { 718 String value = this.sensorProps.getProperty(key); 719 if (value != null) { 720 value = value.trim(); 721 } 722 return value; 723 } 724 725 /** 726 * Returns the sensorbase host, such as "http://dasha.ics.hawaii.edu:9876/sensorbase". 727 * @return The sensorbase host. 728 */ 729 public String getSensorBaseHost() { 730 return this.sensorBaseHost; 731 } 732 733 734 /** 735 * Returns the password for this user, such as "xu876csld". 736 * @return The user password. 737 */ 738 public String getSensorBasePassword() { 739 return this.password; 740 } 741 742 /** 743 * Returns the account for this user, such as "johnson@hawaii.edu". 744 * @return The user account. 745 */ 746 public String getSensorBaseUser() { 747 return this.user; 748 } 749 750 /** 751 * Returns the AutoSend time interval, such as 1.0. 752 * @return The autosend interval. 753 */ 754 public double getAutoSendTimeInterval() { 755 return this.autosendTimeInterval; 756 } 757 758 /** 759 * Returns the AutoSend batch size, such as 250. 760 * @return The autosend batch size. 761 */ 762 public int getAutoSendMaxBuffer() { 763 return this.autosendMaxBuffer; 764 } 765 766 /** 767 * Returns the StateChange interval. 768 * @return The state change interval in seconds. 769 */ 770 public int getStateChangeInterval() { 771 return this.statechangeInterval; 772 } 773 774 /** 775 * Returns the current timeout setting for non-Ping requests. 776 * @return The timeout in seconds. 777 */ 778 public int getTimeout() { 779 return this.timeout; 780 } 781 782 /** 783 * Returns the current timeout setting for Ping requests. 784 * @return The ping timeout in seconds. 785 */ 786 public int getPingTimeout() { 787 return this.pingTimeout; 788 } 789 790 /** 791 * Returns the logging level specified for SensorShells. 792 * @return The logging level. 793 */ 794 public Level getLoggingLevel() { 795 return this.loggingLevel; 796 } 797 798 /** 799 * Returns true if multishell processing is enabled. 800 * @return True if multishell processing. 801 */ 802 public boolean isMultiShellEnabled () { 803 return this.multiShellEnabled; 804 } 805 806 /** 807 * Returns the number of shells to instantiate if multishell processing is enabled. 808 * @return The number of shells to instantiate. 809 */ 810 public int getMultiShellNumShells() { 811 return this.multiShellNumShells; 812 } 813 814 /** 815 * Returns the number of instances to send to one shell in a row if multishell processing. 816 * @return The number of instances to send to one shell in a row. 817 */ 818 public int getMultiShellBatchSize() { 819 return this.multiShellBatchSize; 820 } 821 822 /** 823 * Returns the maximum number of instances to buffer before a blocking send is invoked in 824 * multishell mode. 825 * @return The maximum number of instances to buffer. 826 */ 827 public int getMultiShellMaxBuffer() { 828 return this.multiShellMaxBuffer; 829 } 830 831 /** 832 * Returns the MultiShell AutoSend time interval, such as 0.10. 833 * @return The multishell autosend interval. 834 */ 835 public double getMultiShellAutoSendTimeInterval() { 836 return this.multiShellAutoSendTimeInterval; 837 } 838 839 840 /** 841 * Returns true if offline cache data saving is enabled. 842 * @return True if offline caching enabled. 843 */ 844 public boolean isOfflineCacheEnabled() { 845 return this.offlineCacheEnabled; 846 } 847 848 849 /** 850 * Returns true if offline data recovery is enabled. 851 * @return True if offline data recovery enabled. 852 */ 853 public boolean isOfflineRecoveryEnabled () { 854 return this.offlineRecoveryEnabled; 855 } 856 857 /** 858 * Returns the set of SensorProperties as a multi-line string. 859 * Does not print out the value associated with SENSORSHELL_PASSWORD_KEY. 860 * @return The Sensor property keys and values. 861 */ 862 @Override 863 public String toString() { 864 String cr = System.getProperty("line.separator"); 865 StringBuffer buff = new StringBuffer(100); 866 // It turns out to be much, much more usable to get these properties alphabetized. 867 TreeMap<String, String> map = new TreeMap<String, String>(); 868 for (Object key : this.sensorProps.keySet()) { 869 map.put(key.toString(), this.sensorProps.get(key).toString()); 870 } 871 // Now print them out in alphabetical order. 872 buff.append("SensorProperties"); 873 for (Entry<String, String> entry : map.entrySet()) { 874 buff.append(cr); 875 buff.append(" "); 876 buff.append(entry.getKey()); 877 buff.append(" : "); 878 buff.append((entry.getKey().equals(SENSORSHELL_SENSORBASE_PASSWORD_KEY)) ? 879 "<password hidden>" : entry.getValue()); 880 } 881 buff.append(cr); 882 buff.append(" sensorshell.properties file location: "); 883 buff.append(this.sensorShellPropertiesFile.getAbsolutePath()); 884 return buff.toString(); 885 } 886 887 /** 888 * Sets the autosend time interval and autosend max buffer size to the multishell versions. 889 * Invoked by the multishell just before instantiating its child SingleSensorShells so that 890 * they are set up with the appropriate multishell time interval. 891 */ 892 public void switchToMultiShellMode() { 893 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, 894 String.valueOf(this.getMultiShellAutoSendTimeInterval())); 895 this.autosendTimeInterval = this.multiShellAutoSendTimeInterval; 896 this.sensorProps.setProperty(SENSORSHELL_AUTOSEND_MAXBUFFER_KEY, 897 String.valueOf(this.getMultiShellMaxBuffer())); 898 this.autosendMaxBuffer = this.getMultiShellMaxBuffer(); 899 } 900 901 /** 902 * Returns a new SensorShellProperties instance customized for offline data recovery. 903 * This means that multishell, autosend, and offline recovery/caching are all disabled. 904 * @param properties The original properties. 905 * @return The new SensorShellProperties instance. 906 * @throws SensorShellException If something goes wrong. 907 */ 908 909 public static SensorShellProperties getOfflineMode(SensorShellProperties properties) 910 throws SensorShellException { 911 String falseStr = "false"; 912 Properties props = new Properties(); 913 props.setProperty(SENSORSHELL_MULTISHELL_ENABLED_KEY, falseStr); 914 props.setProperty(SENSORSHELL_OFFLINE_CACHE_ENABLED_KEY, falseStr); 915 props.setProperty(SENSORSHELL_OFFLINE_RECOVERY_ENABLED_KEY, falseStr); 916 props.setProperty(SENSORSHELL_AUTOSEND_TIMEINTERVAL_KEY, "0.0"); 917 return new SensorShellProperties(props, true); 918 } 919 } 920