001 package org.hackystat.sensorshell; 002 003 import java.io.File; 004 import java.util.Map; 005 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData; 006 007 008 /** 009 * Provides "middleware" for accumulating and sending notification (sensor) 010 * data to Hackystat. SensorShell has two modes of interaction: command line and 011 * programmatic. 012 * <p> 013 * Command line mode is entered by invoking the main() method, and 014 * is intended to be used as a kind of subshell to which commands to add and 015 * send notification data of various types can be sent. The SensorShell can be invoked 016 * without any additional arguments as follows: 017 * 018 * <pre>java -jar sensorshell.jar</pre> 019 * 020 * Or you can invoke it with one, two, or three additional arguments: 021 * 022 * <pre>java -jar sensorshell.jar [tool] [sensorshell.properties] [command file]</pre> 023 * <p> 024 * Programmatic mode involves creating an instance of SensorShell, retrieving the 025 * appropriate command instance (Ping, Add, etc.) and invoking the appropriate method. 026 * 027 * @author Philip M. Johnson 028 */ 029 public class SensorShell implements Shell { 030 031 /** The underlying SingleSensorShell or MultiSensorShell. */ 032 private Shell shell = null; 033 034 /** 035 * Constructs a new SensorShell instance that can be provided with 036 * notification data to be sent eventually to a specific user key and host. 037 * The toolName field in the log file name is set to "interactive" if the tool 038 * is invoked interactively and "tool" if it is invoked programmatically. 039 * 040 * @param properties The sensor properties instance for this run. 041 * @param isInteractive Whether this SensorShell is being interactively invoked or not. 042 */ 043 public SensorShell(SensorShellProperties properties, boolean isInteractive) { 044 this(properties, isInteractive, (isInteractive) ? "interactive" : "tool", null); 045 } 046 047 048 /** 049 * Constructs a new SensorShell instance that can be provided with 050 * notification data to be sent eventually to a specific user key and host. 051 * 052 * @param properties The sensor properties instance for this run. 053 * @param isInteractive Whether this SensorShell is being interactively invoked or not. 054 * @param tool Indicates the invoking tool that is added to the log file name. 055 */ 056 public SensorShell(SensorShellProperties properties, boolean isInteractive, String tool) { 057 this(properties, isInteractive, tool, null); 058 } 059 060 061 /** 062 * Constructs a new SensorShell instance that can be provided with 063 * notification data to be sent eventually to a specific user key and host. 064 * 065 * @param properties The sensor properties instance for this run. 066 * @param isInteractive Whether this SensorShell is being interactively invoked or not. 067 * @param toolName The invoking tool that is added to the log file name. 068 * @param commandFile A file containing shell commands, or null if none provided. 069 */ 070 public SensorShell(SensorShellProperties properties, boolean isInteractive, String toolName, 071 File commandFile) { 072 if (properties.isMultiShellEnabled()) { 073 // Note that isInteractive, commandFile are ignored if MultiSensorShell is specified. 074 shell = new MultiSensorShell(properties, toolName); 075 } 076 else { 077 shell = new SingleSensorShell(properties, isInteractive, toolName, commandFile); 078 } 079 } 080 081 /** {@inheritDoc} */ 082 public void add(Map<String, String> keyValMap) throws SensorShellException { 083 this.shell.add(keyValMap); 084 } 085 086 /** {@inheritDoc} */ 087 public void add(SensorData sensorData) throws SensorShellException { 088 this.shell.add(sensorData); 089 } 090 091 /** {@inheritDoc} */ 092 public int send() throws SensorShellException { 093 return this.shell.send(); 094 } 095 096 /** {@inheritDoc} */ 097 public void quit() throws SensorShellException { 098 this.shell.quit(); 099 } 100 101 /** {@inheritDoc} */ 102 public boolean hasOfflineData() { 103 return this.shell.hasOfflineData(); 104 } 105 106 /** {@inheritDoc} */ 107 public boolean ping() { 108 return this.shell.ping(); 109 } 110 111 /** {@inheritDoc} */ 112 public SensorShellProperties getProperties() { 113 return this.shell.getProperties(); 114 } 115 116 /** {@inheritDoc} */ 117 public void statechange(long resourceCheckSum, Map<String, String> keyValMap) throws Exception { 118 this.shell.statechange(resourceCheckSum, keyValMap); 119 } 120 121 /** 122 * The command line shell interface for invoking a single sensor shell interactively. 123 * <ul> 124 * <li> If invoked with no arguments, then the default sensorshell.properties 125 * file is used and the toolName field in the sensor log file name is 126 * "interactive". 127 * <li> If invoked with one argument, that argument is used as the toolName 128 * value. 129 * <li> If invoked with two arguments, then 130 * the second is used as the sensorshell.properties file 131 * path. 132 * <li> If invoked with three arguments, then the 133 * the third argument specifies a file of commands (the last of which should be 'quit'). 134 * </ul> 135 * Unless three arguments are provided, 136 * the shell then provides a ">>" prompt and supports interactive entry of 137 * sensor data. The following commands are supported: 138 * <ul> 139 * <li> "help" provides a summary of the available commands. 140 * <li> "send" sends all of the accumulated data to the server. 141 * <li> "quit" sends all of the accumulated data to the server and exits. 142 * <li> "ping" checks to see if the host/user/password is valid. 143 * <li> "add" adds a single Sensor Data instance to the buffered list to send. 144 * </ul> 145 * 146 * @param args The command line parameters. See above for details. 147 * @throws SensorShellException If problems occur sending data. 148 */ 149 public static void main(String args[]) throws SensorShellException { 150 // Print help line and exit if arg is -help. 151 if ((args.length == 1) && (args[0].equalsIgnoreCase("-help"))) { 152 System.out.println("java -jar sensorshell.jar [tool] [sensorshell.properties] [commandfile]"); 153 return; 154 } 155 156 // Set Parameter 1 (toolname) to supplied or default value. 157 String toolName = (args.length > 0) ? args[0] : "interactive"; 158 159 // Set Parameter 2 (sensorshell.properties file) to supplied or default value. Exit if error. 160 SensorShellProperties sensorProps = null; 161 try { 162 sensorProps = (args.length >= 2) ? 163 new SensorShellProperties(new File(args[1])) : new SensorShellProperties(); 164 } 165 catch (SensorShellException e) { 166 System.out.println(e.getMessage()); 167 System.out.println("Exiting..."); 168 return; 169 } 170 171 // Set Parameter 3 (command file). Null if not supplied. Exit if supplied and bogus. 172 File commandFile = null; 173 if (args.length == 3) { 174 commandFile = new File(args[2]); 175 if (!(commandFile.exists() && commandFile.isFile())) { 176 System.out.println("Could not find the command file. Exiting..."); 177 return; 178 } 179 } 180 181 // Set interactive parameter. From command line, always interactive unless using command file. 182 boolean interactive = ((commandFile == null)); 183 184 // Now create the (single) shell instance, supplying it with all the appropriate arguments. 185 SingleSensorShell shell = 186 new SingleSensorShell(sensorProps, interactive, toolName, commandFile); 187 188 // Start processing commands either interactively or from the command file. 189 while (true) { 190 // Get the next command 191 shell.printPrompt(); 192 String inputString = shell.readLine(); 193 shell.processInputString(inputString); 194 if (inputString.equalsIgnoreCase("quit")) { 195 return; 196 } 197 } 198 } 199 200 } 201