001 package org.hackystat.sensorbase.db; 002 003 import java.lang.reflect.Constructor; 004 import java.util.List; 005 import java.util.Set; 006 007 import javax.xml.datatype.XMLGregorianCalendar; 008 import org.hackystat.sensorbase.resource.projects.jaxb.Project; 009 import org.hackystat.sensorbase.resource.projects.jaxb.ProjectSummary; 010 import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData; 011 import org.hackystat.sensorbase.resource.sensordatatypes.jaxb.SensorDataType; 012 import org.hackystat.sensorbase.resource.users.jaxb.User; 013 import org.hackystat.sensorbase.server.Server; 014 import static org.hackystat.sensorbase.server.ServerProperties.DB_IMPL_KEY; 015 import org.hackystat.utilities.stacktrace.StackTrace; 016 017 /** 018 * Provides an interface to storage for the resources managed by the SensorBase. 019 * Currently we have one storage mechanisms: a persistent store which is implemented by 020 * an embedded Derby database. 021 * @author Philip Johnson 022 */ 023 public class DbManager { 024 025 /** The chosen Storage system. */ 026 private DbImplementation dbImpl; 027 028 /** The SensorDataIndex open tag. */ 029 public static final String sensorDataIndexOpenTag = "<SensorDataIndex>"; 030 031 /** The SensorDataIndex close tag. */ 032 public static final String sensorDataIndexCloseTag = "</SensorDataIndex>"; 033 034 /** 035 * Creates a new DbManager which manages access to the underlying persistency layer(s). 036 * Instantiates the underlying storage system to use. 037 * @param server The Restlet server instance. 038 */ 039 public DbManager(Server server) { 040 //Defaults to: "org.hackystat.sensorbase.db.derby.DerbyImplementation" 041 String dbClassName = server.getServerProperties().get(DB_IMPL_KEY); 042 Class<?> dbClass = null; 043 //First, try to find the class specified in the sensorbase.properties file (or the default) 044 try { 045 dbClass = Class.forName(dbClassName); 046 } 047 catch (ClassNotFoundException e) { 048 String msg = "DB error instantiating " + dbClassName + ". Could not find this class."; 049 server.getLogger().warning(msg + "\n" + StackTrace.toString(e)); 050 throw new IllegalArgumentException(e); 051 } 052 // Next, try to find a constructor that accepts a Server as its parameter. 053 Class<?>[] constructorParam = {org.hackystat.sensorbase.server.Server.class}; 054 Constructor<?> dbConstructor = null; 055 try { 056 dbConstructor = dbClass.getConstructor(constructorParam); 057 } 058 catch (Exception e) { 059 String msg = "DB error instantiating " + dbClassName + ". Could not find Constructor(server)"; 060 server.getLogger().warning(msg + "\n" + StackTrace.toString(e)); 061 throw new IllegalArgumentException(e); 062 } 063 // Next, try to create an instance of DbImplementation from the Constructor. 064 Object[] serverArg = {server}; 065 try { 066 this.dbImpl = (DbImplementation) dbConstructor.newInstance(serverArg); 067 } 068 catch (Exception e) { 069 String msg = "DB error instantiating " + dbClassName + ". Could not create instance."; 070 server.getLogger().warning(msg + "\n" + StackTrace.toString(e)); 071 throw new IllegalArgumentException(e); 072 } 073 this.dbImpl.initialize(); 074 } 075 076 /** 077 * Persists a SensorData instance. If the Owner/Timestamp already exists in the table, it is 078 * overwritten. 079 * @param data The sensor data. 080 * @param xmlSensorData The sensor data resource as an XML String. 081 * @param xmlSensorDataRef The sensor data resource as an XML resource reference 082 */ 083 public void storeSensorData(SensorData data, String xmlSensorData, String xmlSensorDataRef) { 084 this.dbImpl.storeSensorData(data, xmlSensorData, xmlSensorDataRef); 085 } 086 087 /** 088 * Persists a SensorDataType instance. If the SDT name already exists in the table, it is 089 * overwritten. 090 * @param sdt The sensor data. 091 * @param xmlSensorDataType The SDT resource as an XML String. 092 * @param xmlSensorDataTypeRef The SDT as an XML resource reference 093 */ 094 public void storeSensorDataType(SensorDataType sdt, String xmlSensorDataType, 095 String xmlSensorDataTypeRef) { 096 this.dbImpl.storeSensorDataType(sdt, xmlSensorDataType, xmlSensorDataTypeRef); 097 } 098 099 /** 100 * Persists a User instance. If the User email already exists in the table, it is 101 * overwritten. 102 * @param user The user instance. 103 * @param xmlUser The User resource as an XML String. 104 * @param xmlUserRef The User as an XML resource reference 105 */ 106 public void storeUser(User user, String xmlUser, String xmlUserRef) { 107 this.dbImpl.storeUser(user, xmlUser, xmlUserRef); 108 } 109 110 /** 111 * Persists a Project instance. If the Project already exists in the db, it is 112 * overwritten. 113 * @param project The project instance. 114 * @param xmlProject The Project resource as an XML String. 115 * @param xmlProjectRef The Project as an XML resource reference 116 */ 117 public void storeProject(Project project, String xmlProject, String xmlProjectRef) { 118 this.dbImpl.storeProject(project, xmlProject, xmlProjectRef); 119 } 120 121 /** 122 * Returns the XML SensorDataIndex for all sensor data. 123 * @return An XML String providing an index of all sensor data resources. 124 */ 125 public String getSensorDataIndex() { 126 return this.dbImpl.getSensorDataIndex(); 127 } 128 129 130 /** 131 * Returns the XML SensorDataIndex for all sensor data for this user. 132 * @param user The User whose sensor data is to be returned. 133 * @return The XML String providing an index of all relevent sensor data resources. 134 */ 135 public String getSensorDataIndex(User user) { 136 return this.dbImpl.getSensorDataIndex(user); 137 } 138 139 /** 140 * Returns the XML SensorDataIndex for all sensor data for this user and sensor data type. 141 * @param user The User whose sensor data is to be returned. 142 * @param sdtName The sensor data type name. 143 * @return The XML Document instance providing an index of all relevent sensor data resources. 144 */ 145 public String getSensorDataIndex(User user, String sdtName) { 146 return this.dbImpl.getSensorDataIndex(user, sdtName); 147 } 148 149 /** 150 * Returns the XML SensorDataIndex for all sensor data matching this user, start/end time, and 151 * whose resource string matches at least one in the list of UriPatterns. 152 * @param users The list of users. 153 * @param startTime The start time. 154 * @param endTime The end time. 155 * @param uriPatterns A list of UriPatterns. 156 * @param sdt The SensorDataType of interest, or null if all data of all SDTs should be retrieved. 157 * @return The XML SensorDataIndex string corresponding to the matching sensor data. 158 */ 159 public String getSensorDataIndex(List<User> users, XMLGregorianCalendar startTime, 160 XMLGregorianCalendar endTime, List<String> uriPatterns, String sdt) { 161 return this.dbImpl.getSensorDataIndex(users, startTime, endTime, uriPatterns, sdt); 162 } 163 164 /** 165 * Returns the XML SensorDataIndex for all sensor data matching this user, start/end time, and 166 * whose resource string matches at least one in the list of UriPatterns. 167 * @param users The list of users. 168 * @param startTime The start time. 169 * @param endTime The end time. 170 * @param uriPatterns A list of UriPatterns. 171 * @param sdt The SensorDataType of interest, or null if all data of all SDTs should be retrieved. 172 * @param tool The tool of interest. 173 * @return The XML SensorDataIndex string corresponding to the matching sensor data. 174 */ 175 public String getSensorDataIndex(List<User> users, XMLGregorianCalendar startTime, 176 XMLGregorianCalendar endTime, List<String> uriPatterns, String sdt, String tool) { 177 return this.dbImpl.getSensorDataIndex(users, startTime, endTime, uriPatterns, sdt, tool); 178 } 179 180 /** 181 * Returns the XML SensorDataIndex for all sensor data matching these users, start/end time, and 182 * whose resource string matches at least one in the list of UriPatterns. 183 * Client must guarantee that startTime and endTime are within Project dates, and that 184 * startIndex and maxInstances are non-negative. 185 * @param users The users. 186 * @param startTime The start time. 187 * @param endTime The end time. 188 * @param uriPatterns A list of UriPatterns. 189 * @param startIndex The starting index. 190 * @param maxInstances The maximum number of instances to return. 191 * @return The XML SensorDataIndex string corresponding to the matching sensor data. 192 */ 193 public String getSensorDataIndex(List<User> users, XMLGregorianCalendar startTime, 194 XMLGregorianCalendar endTime, List<String> uriPatterns, int startIndex, int maxInstances) { 195 return this.dbImpl.getSensorDataIndex(users, startTime, endTime, uriPatterns, startIndex, 196 maxInstances); 197 } 198 199 /** 200 * Returns the XML SensorDataIndex for all sensor data for the given user that arrived 201 * at the server between the two timestamps. This method uses the LastMod timestamp 202 * rather than the "regular" timestamp, and is used for real-time monitoring of data 203 * arriving at the server. 204 * @param user The user whose data is being monitored. 205 * @param lastModStartTime The lastMod startTime of interest. 206 * @param lastModEndTime The lastMod endTime of interest. 207 * @return The XML SensorDataIndex for the data that arrived between the two timestamps. 208 */ 209 public String getSensorDataIndexLastMod(User user, XMLGregorianCalendar lastModStartTime, 210 XMLGregorianCalendar lastModEndTime) { 211 return this.dbImpl.getSensorDataIndexLastMod(user, lastModStartTime, lastModEndTime); 212 } 213 214 /** 215 * Returns the XML SensorDataTypeIndex for all sensor data. 216 * @return An XML String providing an index of all SDT resources. 217 */ 218 public String getSensorDataTypeIndex() { 219 return this.dbImpl.getSensorDataTypeIndex(); 220 } 221 222 /** 223 * Returns the XML UserIndex for all Users.. 224 * @return An XML String providing an index of all User resources. 225 */ 226 public String getUserIndex() { 227 return this.dbImpl.getUserIndex(); 228 } 229 230 /** 231 * Returns the XML Project Index for all Projects. 232 * @return An XML String providing an index of all Project resources. 233 */ 234 public String getProjectIndex() { 235 return this.dbImpl.getProjectIndex(); 236 } 237 238 239 /** 240 * Returns a ProjectSummary instance constructed for the given Project between the startTime 241 * and endTime. This summary provides a breakdown of the number of sensor data instances found 242 * of the given type during the given time period. 243 * @param users The list of users in this project. 244 * @param startTime The startTime 245 * @param endTime The endTime. 246 * @param uriPatterns The uriPatterns for this project. 247 * @param href The URL naming this resource. 248 * @return The ProjectSummary instance. 249 */ 250 public ProjectSummary getProjectSummary(List<User> users, XMLGregorianCalendar startTime, 251 XMLGregorianCalendar endTime, List<String> uriPatterns, String href) { 252 return this.dbImpl.getProjectSummary(users, startTime, endTime, uriPatterns, href); 253 } 254 255 /** 256 * Returns a SensorDataIndex representing the "snapshot" of sensor data in the given time 257 * interval for the given sdt and tool (if tool is not null). The "snapshot" is the set of 258 * sensor data with the most recent runtime value during the interval. 259 * @param users The list of users in this project. 260 * @param startTime The startTime 261 * @param endTime The endTime. 262 * @param uriPatterns The uriPatterns for this project. 263 * @param sdt The sensor data type of the sensor data of interest. 264 * @param tool The tool associated with this snapshot, or null if any tool will do. 265 * @return The SensorDataIndex with the latest runtime. 266 */ 267 public String getProjectSensorDataSnapshot(List<User> users, XMLGregorianCalendar startTime, 268 XMLGregorianCalendar endTime, List<String> uriPatterns, String sdt, String tool) { 269 return this.dbImpl.getProjectSensorDataSnapshot(users, startTime, endTime, uriPatterns, sdt, 270 tool); 271 } 272 273 /** 274 * Returns the SensorData instance as an XML string, or null. 275 * @param user The user. 276 * @param timestamp The timestamp associated with this sensor data. 277 * @return The SensorData instance as an XML string, or null. 278 */ 279 public String getSensorData(User user, XMLGregorianCalendar timestamp) { 280 return this.dbImpl.getSensorData(user, timestamp); 281 } 282 283 /** 284 * Returns the SensorDataType instance as an XML string, or null. 285 * @param sdtName The name of the SDT to retrieve. 286 * @return The SensorDataType instance as an XML string, or null. 287 */ 288 public String getSensorDataType(String sdtName) { 289 return this.dbImpl.getSensorDataType(sdtName); 290 } 291 292 /** 293 * Returns the User instance as an XML string, or null. 294 * @param email The email address of the User to retrieve. 295 * @return The User instance as an XML string, or null. 296 */ 297 public String getUser(String email) { 298 return this.dbImpl.getUser(email); 299 } 300 301 /** 302 * Returns the Project instance as an XML string, or null. 303 * @param user The User that owns the Project to retrieve. 304 * @param projectName The name of the Project to retrieve. 305 * @return The Project instance as an XML string, or null. 306 */ 307 public String getProject(User user, String projectName) { 308 return this.dbImpl.getProject(user, projectName); 309 } 310 311 /** 312 * Returns true if the passed [user, timestamp] has sensor data defined for it. 313 * @param user The user. 314 * @param timestamp The timestamp 315 * @return True if there is any sensor data for this [user, timestamp]. 316 */ 317 public boolean hasSensorData(User user, XMLGregorianCalendar timestamp) { 318 return this.dbImpl.hasSensorData(user, timestamp); 319 } 320 321 322 /** 323 * Ensures that sensor data with the given user and timestamp no longer exists. 324 * @param user The user. 325 * @param timestamp The timestamp associated with this sensor data. 326 */ 327 public void deleteSensorData(User user, XMLGregorianCalendar timestamp) { 328 this.dbImpl.deleteSensorData(user, timestamp); 329 } 330 331 /** 332 * Ensures that sensor data with the given user no longer exists. 333 * @param user The user. 334 */ 335 public void deleteSensorData(User user) { 336 this.dbImpl.deleteSensorData(user); 337 } 338 339 /** 340 * Ensures that the SensorDataType with the given name no longer exists. 341 * @param sdtName The SDT name. 342 */ 343 public void deleteSensorDataType(String sdtName) { 344 this.dbImpl.deleteSensorDataType(sdtName); 345 } 346 347 /** 348 * Ensures that the User with the given email address is no longer present in this db. 349 * @param email The user email. 350 */ 351 public void deleteUser(String email) { 352 this.dbImpl.deleteUser(email); 353 } 354 355 /** 356 * Ensures that the Project with the given user and name is no longer present in this db. 357 * @param user The User who owns this Project. 358 * @param projectName The name of the Project to delete. 359 */ 360 public void deleteProject(User user, String projectName) { 361 this.dbImpl.deleteProject(user, projectName); 362 } 363 364 /** 365 * Databases like Derby require an explicit compress command for releasing disk space 366 * after a large number of rows have been deleted. This operation invokes the compress 367 * command on all tables in the database. If a database implementation does not support 368 * explicit compression, then this command should do nothing but return true. 369 * @return True if the compress command succeeded or if the database does not support 370 * compression. 371 */ 372 public boolean compressTables() { 373 return this.dbImpl.compressTables(); 374 } 375 376 /** 377 * The most appropriate set of indexes for the database has been evolving over time as we 378 * develop new queries. This command sets up the appropriate set of indexes. It should be 379 * able to be called repeatedly without error. 380 * @return True if the index commands succeeded. 381 */ 382 public boolean indexTables() { 383 return this.dbImpl.indexTables(); 384 } 385 386 /** 387 * Returns the current number of rows in the specified table. 388 * @param table The table whose rows are to be counted. 389 * @return The number of rows in the table, or -1 if the table does not exist or an error occurs. 390 */ 391 public int getRowCount(String table) { 392 return this.dbImpl.getRowCount(table); 393 } 394 395 /** 396 * Returns a set containing the names of all tables in this database. Used by clients to 397 * invoke getRowCount with a legal table name. 398 * @return A set of table names. 399 */ 400 public Set<String> getTableNames() { 401 return this.dbImpl.getTableNames(); 402 } 403 404 }