Commit in java/trunk/conditions on MAIN
pom.xml+20-5291 -> 292
src/main/java/org/hps/conditions/AbstractConditionsDatabaseObject.java+194added 292
                                /ConditionsDatabaseObject.java+82added 292
                                /ConditionsTableMetaData.java+24added 292
                                /ConnectionManager.java+50-4291 -> 292
src/test/java/org/hps/conditions/ConditionsDatabaseObjectTest.java+102added 292
+472-9
4 added + 2 modified, total 6 files
Checkpoint work on adding an API for updating individual conditions objects.  This functions as a basic ORM.  (Work in progress.)

java/trunk/conditions
pom.xml 291 -> 292
--- java/trunk/conditions/pom.xml	2014-03-12 01:25:44 UTC (rev 291)
+++ java/trunk/conditions/pom.xml	2014-03-12 02:04:24 UTC (rev 292)
@@ -1,11 +1,12 @@
 <?xml version="1.0"?>
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
-    
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
     <modelVersion>4.0.0</modelVersion>
     <artifactId>hps-conditions</artifactId>
     <name>conditions</name>
     <description>HPS conditions framework</description>
-    
+
     <parent>
         <groupId>org.hps</groupId>
         <artifactId>hps-parent</artifactId>
@@ -18,7 +19,21 @@
         <connection>scm:svn:svn://svn.freehep.org/hps/java/trunk/conditions/</connection>
         <developerConnection>scm:svn:svn://svn.freehep.org/hps/java/trunk/conditions/</developerConnection>
     </scm>
-    
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>org/hps/conditions/ConditionsDatabaseObjectTest.java</exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
     <dependencies>
         <dependency>
             <groupId>org.lcsim</groupId>
@@ -40,5 +55,5 @@
             <version>5.1.26</version>
         </dependency>
     </dependencies>
-    
+
 </project>

java/trunk/conditions/src/main/java/org/hps/conditions
AbstractConditionsDatabaseObject.java added at 292
--- java/trunk/conditions/src/main/java/org/hps/conditions/AbstractConditionsDatabaseObject.java	                        (rev 0)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/AbstractConditionsDatabaseObject.java	2014-03-12 02:04:24 UTC (rev 292)
@@ -0,0 +1,194 @@
+package org.hps.conditions;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+/**
+ * The abstract implementation of {@link ConditionsDatabaseObject}.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public abstract class AbstractConditionsDatabaseObject implements ConditionsDatabaseObject {
+
+    ConnectionManager _connectionManager = null;
+    ConditionsTableMetaData _tableMetaData = null;
+    int _rowId = -1;
+    int _setId = -1;
+    boolean _isDirty = false;
+    boolean _isReadOnly = false;
+    FieldValueMap _fieldValues = null;
+    
+    /**
+     * Map of field names to their values.
+     */
+    public static final class FieldValueMap extends LinkedHashMap<String, Object> {
+    }      
+    
+    /**
+     * Constructor for a new object which cannot initially be read only as it must be inserted.
+     * @param tableMetaData
+     * @param fieldValues
+     * @param setId
+     * @param isReadOnly
+     */
+    public AbstractConditionsDatabaseObject(
+            ConnectionManager connectionManager,
+            ConditionsTableMetaData tableMetaData,  
+            int setId,
+            FieldValueMap fieldValues) {
+        
+        if (connectionManager == null) {
+            throw new IllegalArgumentException("The connectionManager is null.");
+        }
+        if (tableMetaData == null) {
+            throw new IllegalArgumentException("The tableMetaData is null");
+        }
+        if (setId <= 0) {
+            throw new IllegalArgumentException("The set ID value is invalid: " + setId);
+        }        
+        _connectionManager = connectionManager;
+        _tableMetaData = tableMetaData;
+        _setId = setId;
+        _rowId = -1;
+        if (fieldValues != null) {
+            _fieldValues = fieldValues;
+        } else {
+            _fieldValues = new FieldValueMap();
+        }
+    }
+    
+    /**
+     * Constructor for loading data from an existing object with a row ID.
+     * @param tableMetaData
+     * @param rowId
+     * @param isReadOnly
+     */
+    public AbstractConditionsDatabaseObject(
+            ConnectionManager connectionManager,
+            ConditionsTableMetaData tableMetaData,
+            int rowId,
+            boolean isReadOnly) {
+        if (connectionManager == null) {
+            throw new IllegalArgumentException("The connectionManager cannot be null!");
+        }
+        if (tableMetaData == null) {
+            throw new IllegalArgumentException("The tableMetaData cannot be null");
+        }
+        if (rowId <= 0) {
+            throw new IllegalArgumentException("Invalid row ID: " + rowId);
+        }
+        _connectionManager = connectionManager;
+        _tableMetaData = tableMetaData;
+        _rowId = rowId;
+        _isReadOnly = isReadOnly;
+        _fieldValues = new FieldValueMap();
+    }
+    
+    public ConditionsTableMetaData getTableMetaData() {
+        return _tableMetaData;
+    }
+    
+    public int getRowId() {
+        return _rowId;
+    }
+
+    public int getSetId() {
+        return _setId;
+    }
+    
+    public boolean isReadOnly() {
+        return _isReadOnly;
+    }
+
+    public boolean isNew() {
+        return _rowId == -1;
+    }
+
+    public boolean isDirty() {
+        return _isDirty;
+    }
+    
+    public void delete() throws ConditionsDatabaseObjectException {
+        if (isReadOnly()) {
+            throw new ConditionsDatabaseObjectException("This object cannot be deleted in read only mode.");
+        }
+        String query = "DELETE FROM " + _tableMetaData.getTableName() + " WHERE id = " + _rowId;
+        _connectionManager.update(query);
+        _rowId = -1;
+    }
+    
+    public void insert() throws ConditionsDatabaseObjectException, SQLException {
+        if (!isNew()) {
+            throw new ConditionsDatabaseObjectException("Record already exists in database.");
+        }
+        if (isReadOnly()) {
+            throw new ConditionsDatabaseObjectException("Cannot insert in read only mode.");
+        }
+        if (_fieldValues.size() == 0) {
+            throw new ConditionsDatabaseObjectException("There are no field values to insert.");
+        }        
+        StringBuffer buff = new StringBuffer();
+        buff.append("INSERT INTO " + _tableMetaData.getTableName() + "( set_id");
+        for (Entry<String, Object> entry : _fieldValues.entrySet()) {
+            buff.append(", " + entry.getKey());
+        }
+        buff.append(" ) VALUES ( ");
+        buff.append(_setId);
+        for (Entry<String, Object> entry : _fieldValues.entrySet()) {
+            buff.append(", " + entry.getValue());
+        } 
+        buff.append(") ");       
+        int key = _connectionManager.update(buff.toString());        
+        _rowId = key;        
+    }
+
+    public void select() throws ConditionsDatabaseObjectException, SQLException {
+        if (isNew()) {
+            throw new ConditionsDatabaseObjectException("Record has not been inserted into database yet.");
+        }
+        StringBuffer buff = new StringBuffer();
+        buff.append("SELECT ");
+        for (String fieldName : _tableMetaData.getFieldNames()) {
+            buff.append(fieldName + ", ");
+        }
+        buff.delete(buff.length()-2, buff.length()-1);
+        buff.append(" FROM " + _tableMetaData.getTableName());        
+        buff.append(" WHERE id = " + _rowId);
+        ResultSet resultSet = _connectionManager.query(buff.toString());        
+        ResultSetMetaData metadata = resultSet.getMetaData();
+        int ncolumns = metadata.getColumnCount();
+        if (resultSet.next()) {        
+            for (int i=1; i<=ncolumns; i++) {
+                _fieldValues.put(metadata.getColumnName(i), resultSet.getObject(i));
+            }
+        }        
+    }
+        
+    public void update() throws ConditionsDatabaseObjectException {
+        if (isReadOnly()) {
+            throw new ConditionsDatabaseObjectException("Cannot update in read only mode.");
+        }
+        if (isNew()) {
+            throw new ConditionsDatabaseObjectException("Cannot update a new record.");
+        }
+        if (_fieldValues.size() == 0) {
+            throw new ConditionsDatabaseObjectException("No field values to update.");
+        }
+        StringBuffer buff = new StringBuffer();
+        buff.append("UPDATE " + _tableMetaData.getTableName() + " SET ");
+        for (Entry entry : _fieldValues.entrySet()) {
+            buff.append(entry.getKey() + " = '" + entry.getValue() + "', ");
+        }
+        buff.delete(buff.length()-2, buff.length()-1);
+        buff.append(" WHERE id = " + _rowId); 
+        _connectionManager.update(buff.toString());
+        _isDirty = false;
+    }
+    
+    public void setFieldValue(String key, Object value) {
+        _fieldValues.put(key, value);
+        _isDirty = true;
+    }        
+}

java/trunk/conditions/src/main/java/org/hps/conditions
ConditionsDatabaseObject.java added at 292
--- java/trunk/conditions/src/main/java/org/hps/conditions/ConditionsDatabaseObject.java	                        (rev 0)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ConditionsDatabaseObject.java	2014-03-12 02:04:24 UTC (rev 292)
@@ -0,0 +1,82 @@
+package org.hps.conditions;
+
+import java.sql.SQLException;
+
+/**
+ * This is an interface for accessing conditions database information by row.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public interface ConditionsDatabaseObject {
+    
+    /**
+     * Get the database table meta data associated to this object.
+     * @return The database table meta data associated to this object.
+     */
+    ConditionsTableMetaData getTableMetaData();
+    
+    /**
+     * Get the row ID of this object.
+     * @return The database row ID.
+     */
+    int getRowId();
+    
+    /**
+     * Get the set ID of this object identifying its collection. 
+     * @return The collection ID.
+     */
+    int getSetId();
+    
+    /**
+     * Update this row in the database using a SQL UPDATE statement.    
+     */
+    void update() throws ConditionsDatabaseObjectException;
+    
+    /**
+     * Delete this object's row in the database using a SQL DELETE statement.
+     */
+    void delete() throws ConditionsDatabaseObjectException;
+    
+    /**
+     * Insert this object into the database using a SQL INSERT statement.     
+     */
+    void insert() throws ConditionsDatabaseObjectException, SQLException;
+    
+    /**
+     * Select data into this object from the database using a SQL SELECT statement.     
+     */
+    void select() throws ConditionsDatabaseObjectException, SQLException;
+    
+    /**
+     * Return true if this object is read-only.
+     * @return True if object is read-only.
+     */
+    boolean isReadOnly();
+    
+    /**
+     * Return true if this object is new and hasn't been inserted into the database yet.
+     * @return True if object is new.
+     */
+    boolean isNew();
+    
+    /**
+     * Return true if this object's data has been modified without a database update.
+     * @return True if object is dirty.
+     */
+    boolean isDirty();
+        
+    /**
+     * Generic set method.  This will set the object to the 'dirty' state.
+     * @param fieldName The name of the field.
+     * @param fieldValue The field value.
+     */
+    void setFieldValue(String field, Object value);
+    
+    /**
+     * Exception type throw by methods in this interface.
+     */
+    public static final class ConditionsDatabaseObjectException extends Exception {
+        public ConditionsDatabaseObjectException(String message) {
+            super(message);
+        }
+    }    
+}

java/trunk/conditions/src/main/java/org/hps/conditions
ConditionsTableMetaData.java added at 292
--- java/trunk/conditions/src/main/java/org/hps/conditions/ConditionsTableMetaData.java	                        (rev 0)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ConditionsTableMetaData.java	2014-03-12 02:04:24 UTC (rev 292)
@@ -0,0 +1,24 @@
+package org.hps.conditions;
+
+import java.util.Set;
+
+public class ConditionsTableMetaData {
+    
+    String _tableName;
+    Set<String> _fieldNames = null;
+    
+    ConditionsTableMetaData(String tableName, Set<String> fieldNames) {
+        _tableName = tableName;
+        _fieldNames = fieldNames;
+    }
+    
+    Set<String> getFieldNames() {
+        return _fieldNames;
+    }
+    
+    String getTableName() {
+        return _tableName;
+    }    
+    
+    // TODO: Add method for getting next set ID.
+}

java/trunk/conditions/src/main/java/org/hps/conditions
ConnectionManager.java 291 -> 292
--- java/trunk/conditions/src/main/java/org/hps/conditions/ConnectionManager.java	2014-03-12 01:25:44 UTC (rev 291)
+++ java/trunk/conditions/src/main/java/org/hps/conditions/ConnectionManager.java	2014-03-12 02:04:24 UTC (rev 292)
@@ -23,12 +23,11 @@
     private Connection connection = null;
 
     /**
-     * Class constructor which is private so singleton must be used.
+     * Class constructor.  Override at your own risk!
      */
-    private ConnectionManager() {
-        setupFromProperties();
+    protected ConnectionManager() {
     }
-
+    
     /**
      * Get the singleton instance of this class.
      * @return The instance of this class.
@@ -63,6 +62,7 @@
     Connection createConnection() {
         Connection newConnection = connectionParameters.createConnection();
         try {
+            System.out.println("USE " + connectionParameters.getDatabase());
             newConnection.createStatement().execute("USE " + connectionParameters.getDatabase());
         } catch (SQLException e) {
             throw new RuntimeException("Failed to connect to database.", e);
@@ -113,6 +113,9 @@
      * @return The ResultSet from the query or null.
      */
     public ResultSet query(String query) {
+        
+        System.out.println(query);
+        
         if (connection == null)
             connection = createConnection();
         ResultSet result = null;
@@ -125,6 +128,32 @@
         return result;
     }
     
+    /**
+     * Perform a query with an update SQL command like INSERT, DELETE or UPDATE.
+     * @return query The SQL query string.
+     * @return The number of rows affected.
+     */
+    public int update(String query) {
+        
+        System.out.println(query);
+        
+        if (connection == null)
+            connection = createConnection();
+        int key = -1;
+        try {
+            // NOTE: Assumes only one row is updated!
+            Statement statement = connection.createStatement();
+            statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS); 
+            ResultSet resultSet = statement.getGeneratedKeys();
+            if (resultSet.next()) {
+                key = resultSet.getInt(1);
+            }
+        } catch (SQLException x) {
+            throw new RuntimeException("Error in query: " + query, x);
+        }
+        return key;
+    }
+        
     public void disconnect() {
         cleanup(connection);
     }
@@ -132,6 +161,7 @@
     /**
      * Setup the object from a properties file.
      */
+    /*
     private void setupFromProperties() {
         Object obj = System.getProperties().get("hps.conditions.db.configuration");
         if (obj != null) {
@@ -147,4 +177,20 @@
             connectionParameters = ConnectionParameters.fromProperties(p);
         }
     }
+    */
+    public void setupFromProperties(File propertiesFile) {
+        //Object obj = System.getProperties().get("hps.conditions.db.configuration");
+        //if (obj != null) {
+            //String config = obj.toString();
+        Properties p = new Properties();
+        try {
+            p.load(new FileInputStream(propertiesFile));
+        } catch (FileNotFoundException e) {
+            throw new RuntimeException(e);
+        } catch (IOException e) {
+            throw new RuntimeException(e);
+        }
+        connectionParameters = ConnectionParameters.fromProperties(p);
+        //}
+    }
 }

java/trunk/conditions/src/test/java/org/hps/conditions
ConditionsDatabaseObjectTest.java added at 292
--- java/trunk/conditions/src/test/java/org/hps/conditions/ConditionsDatabaseObjectTest.java	                        (rev 0)
+++ java/trunk/conditions/src/test/java/org/hps/conditions/ConditionsDatabaseObjectTest.java	2014-03-12 02:04:24 UTC (rev 292)
@@ -0,0 +1,102 @@
+package org.hps.conditions;
+
+import java.io.File;
+import java.sql.ResultSet;
+import java.util.HashSet;
+import java.util.Set;
+
+import junit.framework.TestCase;
+
+import org.hps.conditions.AbstractConditionsDatabaseObject.FieldValueMap;
+import org.hps.conditions.ConditionsDatabaseObject.ConditionsDatabaseObjectException;
+
+/**
+ * Test the basic functionality of a {@link ConditionsDatabaseObject} on a dummy database.
+ * @author Jeremy McCormick <[log in to unmask]>
+ */
+public class ConditionsDatabaseObjectTest extends TestCase {
+    
+    String dummyTableName = "dummy_table";
+    String dummyFieldName = "dummy_field1";
+    float firstValue = 1.234f;
+    float secondValue = 5.678f;
+    
+    public void testDummy() {
+    
+        // Connect to local test database.
+        ConnectionManager connectionManager = new ConnectionManager();
+        //ConnectionManager connectionManager = new DummyConnectionManager();
+        connectionManager.setupFromProperties(new File("dummy_db.properties"));
+        
+        // Setup table meta data information.
+        Set<String> dummyFieldNames = new HashSet<String>();
+        dummyFieldNames.add(dummyFieldName);               
+        ConditionsTableMetaData tableMetaData = new ConditionsTableMetaData(dummyTableName, dummyFieldNames);
+        
+        // Create a dummy data object with a single field value.
+        FieldValueMap fieldValues = new FieldValueMap();
+        fieldValues.put(dummyFieldName, firstValue);
+        ConditionsDatabaseObject dummyObject = new DummyConditionsObject(connectionManager, tableMetaData, 1, fieldValues);        
+         
+        try {
+            // Insert the object into the database.
+            dummyObject.insert();
+            int key = dummyObject.getRowId();
+
+            // Set a new field value and push update to the database.
+            dummyObject.setFieldValue(dummyFieldName, secondValue);
+            dummyObject.update();
+            
+            // Load an object in read only mode.
+            DummyConditionsObject readOnlyObject = new DummyConditionsObject(connectionManager, tableMetaData, key);            
+            readOnlyObject.select();
+            try {
+                readOnlyObject.delete();
+                throw new RuntimeException("Should not get here.");
+            } catch (ConditionsDatabaseObjectException x) {
+                System.out.println("Caught error: " + x.getMessage());
+            }
+
+            // Delete the object from the database.
+            dummyObject.delete();
+                                    
+        } catch (Exception x) {
+            throw new RuntimeException(x);
+        }        
+    }
+    
+    public static class DummyConditionsObject extends AbstractConditionsDatabaseObject {
+        
+        // Create a new object.
+        DummyConditionsObject(ConnectionManager connectionManager,
+                ConditionsTableMetaData tableMetaData,
+                int setId,
+                FieldValueMap fieldValues) {       
+            super(connectionManager, tableMetaData, setId, fieldValues);
+        }
+        
+        // Load an existing object in read only mode.
+        DummyConditionsObject(
+                ConnectionManager connectionManager,
+                ConditionsTableMetaData tableMetaData,
+                int rowId) {
+            super(connectionManager, tableMetaData, rowId, true);
+        }
+        
+    }
+    
+    public class DummyConnectionManager extends ConnectionManager {
+        
+        public ResultSet query(String query) {
+            System.out.println("Dummy query method ...");
+            System.out.println(query);            
+            return null;
+        }
+        
+        public int update(String query) {
+            System.out.println("Dummy update method ...");
+            System.out.println(query);            
+            return 1;
+        }
+    }
+}
SVNspam 0.1