001 package org.hackystat.sensorbase.server; 002 003 import java.util.Map; 004 import java.util.Set; 005 006 import org.hackystat.sensorbase.db.DbManager; 007 import org.hackystat.sensorbase.mailer.Mailer; 008 import org.hackystat.sensorbase.resource.db.CompressResource; 009 import org.hackystat.sensorbase.resource.db.IndexResource; 010 import org.hackystat.sensorbase.resource.db.RowCountResource; 011 import org.hackystat.sensorbase.resource.ping.PingResource; 012 import org.hackystat.sensorbase.resource.projects.ProjectManager; 013 import org.hackystat.sensorbase.resource.projects.ProjectsResource; 014 import org.hackystat.sensorbase.resource.projects.UserProjectInvitationResource; 015 import org.hackystat.sensorbase.resource.projects.UserProjectRenameResource; 016 import org.hackystat.sensorbase.resource.projects.UserProjectResource; 017 import org.hackystat.sensorbase.resource.projects.UserProjectSensorDataResource; 018 import org.hackystat.sensorbase.resource.projects.UserProjectSnapshotResource; 019 import org.hackystat.sensorbase.resource.projects.UserProjectSummaryResource; 020 import org.hackystat.sensorbase.resource.projects.UserProjectsResource; 021 import org.hackystat.sensorbase.resource.registration.HomePageResource; 022 import org.hackystat.sensorbase.resource.registration.RegistrationResource; 023 import org.hackystat.sensorbase.resource.sensordata.SensorDataManager; 024 import org.hackystat.sensorbase.resource.sensordata.SensorDataResource; 025 import org.hackystat.sensorbase.resource.sensordata.UserSensorDataResource; 026 import org.hackystat.sensorbase.resource.sensordatatypes.SdtManager; 027 import org.hackystat.sensorbase.resource.sensordatatypes.SensorDataTypeResource; 028 import org.hackystat.sensorbase.resource.sensordatatypes.SensorDataTypesResource; 029 import org.hackystat.sensorbase.resource.users.UserManager; 030 import org.hackystat.sensorbase.resource.users.UserResource; 031 import org.hackystat.sensorbase.resource.users.UsersResource; 032 import org.hackystat.utilities.logger.HackystatLogger; 033 import org.hackystat.utilities.logger.RestletLoggerUtil; 034 import org.restlet.Application; 035 import org.restlet.Component; 036 import org.restlet.Guard; 037 import org.restlet.Restlet; 038 import org.restlet.Router; 039 import org.restlet.data.Protocol; 040 041 import static org.hackystat.sensorbase.server.ServerProperties.HOSTNAME_KEY; 042 import static org.hackystat.sensorbase.server.ServerProperties.PORT_KEY; 043 import static org.hackystat.sensorbase.server.ServerProperties.CONTEXT_ROOT_KEY; 044 import static org.hackystat.sensorbase.server.ServerProperties.LOGGING_LEVEL_KEY; 045 046 import java.util.logging.Logger; 047 048 049 /** 050 * Sets up the HTTP Server process and dispatching to the associated resources. 051 * @author Philip Johnson 052 */ 053 public class Server extends Application { 054 055 /** Holds the Restlet Component associated with this Server. */ 056 private Component component; 057 058 /** Holds the host name associated with this Server. */ 059 private String hostName; 060 061 /** Holds the HackystatLogger for the sensorbase. */ 062 private Logger logger; 063 064 /** Holds the ServerProperties instance associated with this sensorbase. */ 065 private ServerProperties serverProperties; 066 067 /** 068 * Creates a new instance of a SensorBase HTTP server, listening on the supplied port. 069 * SensorBase properties are initialized from the User's sensorbase.properties file. 070 * @return The Server instance created. 071 * @throws Exception If problems occur starting up this server. 072 */ 073 public static Server newInstance() throws Exception { 074 return newInstance(new ServerProperties()); 075 } 076 077 /** 078 * Creates a new instance of a SensorBase HTTP server suitable for unit testing. 079 * SensorBase properties are initialized from the User's sensorbase.properties file, 080 * then set to their "testing" versions. 081 * @return The Server instance created. 082 * @throws Exception If problems occur starting up this server. 083 */ 084 public static Server newTestInstance() throws Exception { 085 ServerProperties properties = new ServerProperties(); 086 properties.setTestProperties(); 087 return newInstance(properties); 088 } 089 090 /** 091 * Creates a new instance of a SensorBase HTTP server, listening on the supplied port. 092 * @param serverProperties The ServerProperties used to initialize this server. 093 * @return The Server instance created. 094 * @throws Exception If problems occur starting up this server. 095 */ 096 public static Server newInstance(ServerProperties serverProperties) throws Exception { 097 Server server = new Server(); 098 server.logger = HackystatLogger.getLogger("org.hackystat.sensorbase", "sensorbase"); 099 server.serverProperties = serverProperties; 100 server.hostName = "http://" + 101 server.serverProperties.get(HOSTNAME_KEY) + 102 ":" + 103 server.serverProperties.get(PORT_KEY) + 104 "/" + 105 server.serverProperties.get(CONTEXT_ROOT_KEY) + 106 "/"; 107 int port = Integer.valueOf(server.serverProperties.get(PORT_KEY)); 108 server.component = new Component(); 109 server.component.getServers().add(Protocol.HTTP, port); 110 server.component.getDefaultHost() 111 .attach("/" + server.serverProperties.get(CONTEXT_ROOT_KEY), server); 112 113 // Set up logging. 114 RestletLoggerUtil.disableLogging(); 115 HackystatLogger.setLoggingLevel(server.logger, server.serverProperties.get(LOGGING_LEVEL_KEY)); 116 server.logger.warning("Starting sensorbase."); 117 server.logger.warning("Host: " + server.hostName); 118 server.logger.info(server.serverProperties.echoProperties()); 119 120 try { 121 Mailer.getInstance(); 122 } 123 catch (Throwable e) { 124 String msg = "ERROR: JavaMail not installed correctly! Mail services will fail!"; 125 server.logger.warning(msg); 126 } 127 128 // Now create all of the Resource Managers and store them in the Context. 129 // Ordering constraints: 130 // - DbManager must precede all resource managers so it can initialize the tables 131 // before the resource managers add data to them. 132 // - UserManager must be initialized before ProjectManager, since ProjectManager needs 133 // to know about the Users. 134 Map<String, Object> attributes = 135 server.getContext().getAttributes(); 136 DbManager dbManager = new DbManager(server); // we need this later in this method. 137 attributes.put("DbManager", dbManager); 138 attributes.put("SdtManager", new SdtManager(server)); 139 attributes.put("UserManager", new UserManager(server)); 140 attributes.put("ProjectManager", new ProjectManager(server)); 141 attributes.put("SensorDataManager", new SensorDataManager(server)); 142 attributes.put("SensorBaseServer", server); 143 attributes.put("ServerProperties", server.serverProperties); 144 145 // Now let's open for business. 146 server.logger.info("Maximum Java heap size (MB): " + 147 (Runtime.getRuntime().maxMemory() / 1000000.0)); 148 server.logger.info("Table counts: " + getTableCounts(dbManager)); 149 server.component.start(); 150 server.logger.warning("SensorBase (Version " + getVersion() + ") now running."); 151 return server; 152 } 153 154 /** 155 * Returns a string with the counts of rows in the various tables. 156 * @param dbManager The dbManager. 157 * @return A string with info on row counts. 158 */ 159 private static String getTableCounts (DbManager dbManager) { 160 Set<String> tableNames = dbManager.getTableNames(); 161 StringBuffer buff = new StringBuffer(); 162 for (String tableName : tableNames) { 163 int tableRows = dbManager.getRowCount(tableName); 164 buff.append(tableName).append(':').append(tableRows).append(' '); 165 } 166 return buff.toString(); 167 } 168 169 170 /** 171 * Starts up the SensorBase web service using the properties specified in sensor.properties. 172 * Control-c to exit. 173 * @param args Ignored. 174 * @throws Exception if problems occur. 175 */ 176 public static void main(final String[] args) throws Exception { 177 Server.newInstance(); 178 } 179 180 /** 181 * Dispatch to the Projects, SensorData, SensorDataTypes, or Users Resource depending on the URL. 182 * We will authenticate all requests except for registration (users?email={email}). 183 * @return The router Restlet. 184 */ 185 @Override 186 public Restlet createRoot() { 187 // First, create a Router that will have a Guard placed in front of it so that this Router's 188 // requests will require authentication. 189 Router authRouter = new Router(getContext()); 190 191 // SENSORDATATYPES 192 authRouter.attach("/sensordatatypes", 193 SensorDataTypesResource.class); 194 authRouter.attach("/sensordatatypes/{sensordatatypename}", 195 SensorDataTypeResource.class); 196 197 // USERS 198 authRouter.attach("/users", 199 UsersResource.class); 200 authRouter.attach("/users/{user}", 201 UserResource.class); 202 203 // SENSORDATA 204 authRouter.attach("/sensordata", 205 SensorDataResource.class); 206 authRouter.attach("/sensordata/{user}", 207 UserSensorDataResource.class); 208 authRouter.attach("/sensordata/{user}?sdt={sensordatatype}", 209 UserSensorDataResource.class); 210 authRouter.attach( 211 "/sensordata/{user}?lastModStartTime={lastModStartTime}&lastModEndTime={lastModEndTime}", 212 UserSensorDataResource.class); 213 authRouter.attach("/sensordata/{user}/{timestamp}", 214 UserSensorDataResource.class); 215 216 // PROJECTS 217 authRouter.attach("/projects", 218 ProjectsResource.class); 219 authRouter.attach("/projects/{user}", 220 UserProjectsResource.class); 221 String projectUri = "/projects/{user}/{projectname}"; 222 authRouter.attach(projectUri, 223 UserProjectResource.class); 224 225 // PROJECTS SNAPSHOT 226 authRouter.attach(projectUri + "/snapshot" + 227 "?startTime={startTime}&endTime={endTime}&sdt={sdt}&tool={tool}", 228 UserProjectSnapshotResource.class); 229 authRouter.attach(projectUri + "/snapshot" + 230 "?startTime={startTime}&endTime={endTime}&sdt={sdt}", 231 UserProjectSnapshotResource.class); 232 233 // PROJECTS SUMMARY 234 authRouter.attach(projectUri + "/summary" + 235 "?startTime={startTime}&endTime={endTime}", 236 UserProjectSummaryResource.class); 237 authRouter.attach(projectUri + "/summary" + 238 "?startTime={startTime}&numDays={numDays}", 239 UserProjectSummaryResource.class); 240 241 // PROJECTS SENSORDATA 242 String projectSensorDataUri = projectUri + "/sensordata"; 243 authRouter.attach(projectSensorDataUri, 244 UserProjectSensorDataResource.class); 245 authRouter.attach(projectSensorDataUri + 246 "?startTime={startTime}&endTime={endTime}&startIndex={startIndex}&maxInstances={maxInstances}", 247 UserProjectSensorDataResource.class); 248 authRouter.attach(projectSensorDataUri + 249 "?startTime={startTime}&endTime={endTime}", 250 UserProjectSensorDataResource.class); 251 authRouter.attach(projectSensorDataUri + 252 "?sdt={sdt}&startTime={startTime}&endTime={endTime}&tool={tool}", 253 UserProjectSensorDataResource.class); 254 authRouter.attach(projectSensorDataUri + 255 "?sdt={sdt}&startTime={startTime}&endTime={endTime}", 256 UserProjectSensorDataResource.class); 257 258 // PROJECTS INVITATION 259 authRouter.attach(projectUri + "/invitation/{rsvp}", 260 UserProjectInvitationResource.class); 261 262 // PROJECTS RENAME 263 authRouter.attach(projectUri + "/rename/{newprojectname}", 264 UserProjectRenameResource.class); 265 266 // DB Commands 267 authRouter.attach("/db/table/compress", CompressResource.class); 268 authRouter.attach("/db/table/index", IndexResource.class); 269 authRouter.attach("/db/table/{table}/rowcount", RowCountResource.class); 270 271 // Here's the Guard that we will place in front of authRouter. 272 authRouter.attach("", HomePageResource.class); 273 Guard guard = new Authenticator(getContext()); 274 guard.setNext(authRouter); 275 276 // Now create our "top-level" router which will allow the registration URI to proceed without 277 // authentication, but all other URI patterns will go to the guarded Router. 278 Router router = new Router(getContext()); 279 router.attach("/register", RegistrationResource.class); 280 router.attach("/ping", PingResource.class); 281 router.attach("/ping?user={user}&password={password}", PingResource.class); 282 router.attachDefault(guard); 283 284 return router; 285 } 286 287 288 /** 289 * Returns the version associated with this Package, if available from the jar file manifest. 290 * If not being run from a jar file, then returns "Development". 291 * @return The version. 292 */ 293 public static String getVersion() { 294 String version = 295 Package.getPackage("org.hackystat.sensorbase.server").getImplementationVersion(); 296 return (version == null) ? "Development" : version; 297 } 298 299 /** 300 * Returns the host name associated with this server. 301 * Example: "http://localhost:9876/sensorbase/" 302 * @return The host name. 303 */ 304 public String getHostName() { 305 return this.hostName; 306 } 307 308 /** 309 * Returns the ServerProperties instance associated with this server. 310 * @return The server properties. 311 */ 312 public ServerProperties getServerProperties() { 313 return this.serverProperties; 314 } 315 316 /** 317 * Returns the logger for the SensorBase. 318 * @return The logger. 319 */ 320 @Override 321 public Logger getLogger() { 322 return this.logger; 323 } 324 } 325