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    }