Author: [log in to unmask] Date: Wed Apr 22 16:39:56 2015 New Revision: 2791 Log: Add DatabaseObject interface and cleanup basic operations for objects and collections. Added: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java Removed: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/BaseConditionsObjectCollectionTest.java java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/BaseConditionsObjectTest.java java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/api/DummyConditionsObjectConverterTest.java Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/AbstractConditionsObjectConverter.java Wed Apr 22 16:39:56 2015 @@ -124,7 +124,7 @@ // Select the objects into the collection by the collection ID. try { collection.select(conditionsRecord.getCollectionId()); - } catch (ConditionsObjectException | SQLException e) { + } catch (DatabaseObjectException | SQLException e) { throw new RuntimeException("Error creating conditions collection from table " + name + " with collection ID " + conditionsRecord.getCollectionId(), e); } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObject.java Wed Apr 22 16:39:56 2015 @@ -29,7 +29,7 @@ static final int UNSET_COLLECTION_ID = -1; - static final int UNSET_ID = -1; + static final int UNSET_ROW_ID = -1; protected static String defaultToString(final BaseConditionsObject object) { final StringBuffer sb = new StringBuffer(); @@ -55,7 +55,7 @@ /** * The row ID of the object in its table. This will be -1 for new objects that are not in the database. */ - private int id = UNSET_ID; + private int id = UNSET_ROW_ID; /** * Flag to indicate object is locally changed and database update has not been executed. @@ -114,36 +114,32 @@ * @throw SQLException if there is an SQL error when executing the SELECT operation */ public BaseConditionsObject(final Connection connection, final TableMetaData tableMetaData, final int rowId) - throws ConditionsObjectException, SQLException { + throws DatabaseObjectException, SQLException { this.connection = connection; this.tableMetaData = tableMetaData; this.fieldValues = new FieldValuesMap(tableMetaData); - final boolean selected = select(rowId); - if (!selected) { - throw new ConditionsObjectException("Failed to select data into object using row ID " + rowId); - } - } - - @Override - public final boolean delete() throws ConditionsObjectException, SQLException { + select(rowId); + // if (!selected) { + // throw new ConditionsObjectException("Failed to select data into object using row ID " + rowId); + // } + } + + @Override + public final void delete() throws DatabaseObjectException, SQLException { if (isNew()) { - throw new ConditionsObjectException("Object is not in database and so cannot be deleted."); + throw new DatabaseObjectException("Object is not in database and so cannot be deleted.", this); } final String sql = "DELETE FROM " + this.tableMetaData.getTableName() + " WHERE id=" + this.getRowId(); - // if (this.verbose) { - // System.out.println(sql); - // } Statement statement = null; - int rowsUpdated; try { statement = this.connection.createStatement(); - rowsUpdated = statement.executeUpdate(sql); + statement.executeUpdate(sql); + this.setRowId(UNSET_ROW_ID); } finally { if (statement != null) { statement.close(); } } - return rowsUpdated != 0; } @Override @@ -177,9 +173,12 @@ } @Override - public final boolean insert() throws ConditionsObjectException, SQLException { - if (!isNew()) { - throw new ConditionsObjectException("Cannot insert an existing record."); + public final void insert() throws DatabaseObjectException, SQLException { + if (!this.isNew()) { + throw new DatabaseObjectException("Cannot insert existing record with row ID: " + this.getRowId(), this); + } + if (!this.hasValidCollection()) { + throw new DatabaseObjectException("Cannot insert object without a valid collection ID.", this); } final StringBuffer sb = new StringBuffer(); sb.append("INSERT INTO " + this.tableMetaData.getTableName() + " ("); @@ -198,15 +197,11 @@ sb.setLength(sb.length() - 2); sb.append(")"); final String sql = sb.toString(); - // if (this.verbose) { - // System.out.println(sql); - // } Statement statement = null; ResultSet resultSet = null; - int rowsUpdated = 0; try { statement = this.connection.createStatement(); - rowsUpdated = statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); + statement.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS); resultSet = statement.getGeneratedKeys(); while (resultSet.next()) { final int key = resultSet.getInt(1); @@ -221,7 +216,7 @@ statement.close(); } } - return rowsUpdated != 0; + this.isDirty = false; } @Override @@ -231,11 +226,11 @@ @Override public final boolean isNew() { - return getRowId() == UNSET_ID; - } - - @Override - public final boolean select(final int id) throws ConditionsObjectException, SQLException { + return getRowId() == UNSET_ROW_ID; + } + + @Override + public final boolean select(final int id) throws DatabaseObjectException, SQLException { this.id = id; if (id < 1) { throw new IllegalArgumentException("bad row ID value: " + id); @@ -249,20 +244,18 @@ sb.append(" FROM " + this.tableMetaData.getTableName()); sb.append(" WHERE id = " + this.getRowId()); final String sql = sb.toString(); - // if (this.verbose) { - // System.out.println(sql); - // } Statement statement = null; ResultSet resultSet = null; boolean selected = false; try { statement = this.connection.createStatement(); resultSet = statement.executeQuery(sql); - while (resultSet.next()) { + selected = resultSet.next(); + if (selected) { for (int columnIndex = 1; columnIndex <= this.tableMetaData.getFieldNames().length; columnIndex++) { - this.setFieldValue(this.tableMetaData.getFieldNames()[columnIndex - 1], resultSet.getObject(columnIndex)); + this.setFieldValue(this.tableMetaData.getFieldNames()[columnIndex - 1], + resultSet.getObject(columnIndex)); } - selected = true; } } finally { if (resultSet != null) { @@ -297,7 +290,7 @@ } @Override - public void setId(final int id) { + public void setRowId(final int id) { this.id = id; } @@ -318,10 +311,11 @@ } @Override - public final boolean update() throws ConditionsObjectException, SQLException { + public final boolean update() throws DatabaseObjectException, SQLException { + int rowsUpdated = 0; if (isDirty()) { if (isNew()) { - throw new ConditionsObjectException("Cannot update a new object."); + throw new DatabaseObjectException("Cannot update a new object.", this); } final StringBuffer sb = new StringBuffer(); sb.append("UPDATE " + this.tableMetaData.getTableName() + " SET "); @@ -338,30 +332,29 @@ sb.setLength(sb.length() - 2); sb.append(" WHERE id=" + this.getRowId()); final String sql = sb.toString(); - // if (this.verbose) { - // System.out.println(sql); - // } Statement statement = null; - int rowsUpdated = 0; try { statement = this.connection.createStatement(); rowsUpdated = statement.executeUpdate(sql); - if (rowsUpdated > 0) { - this.isDirty = false; - } } finally { if (statement != null) { statement.close(); } } - return rowsUpdated != 0; - } else { - return false; - } + } + if (rowsUpdated > 0) { + this.isDirty = false; + } + return rowsUpdated != 0; } @Override public <T> T getFieldValue(final String name) { return (T) this.fieldValues.getValue(name); } + + @Override + public boolean hasValidCollection() { + return getCollectionId() != UNSET_COLLECTION_ID; + } } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/BaseConditionsObjectCollection.java Wed Apr 22 16:39:56 2015 @@ -23,12 +23,13 @@ private Connection connection; private final Set<ObjectType> objects = new LinkedHashSet<ObjectType>(); private TableMetaData tableMetaData; + private boolean isDirty; protected BaseConditionsObjectCollection() { } public BaseConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData, - final int collectionId) throws SQLException, ConditionsObjectException { + final int collectionId) throws SQLException, DatabaseObjectException { this.connection = connection; this.tableMetaData = tableMetaData; this.collectionId = collectionId; @@ -39,15 +40,16 @@ @Override public boolean add(final ObjectType object) { - // System.out.println("adding object " + object + " to collection " + getCollectionId()); + if (object == null) { + throw new IllegalArgumentException("The object argument is null."); + } if (getCollectionId() != BaseConditionsObject.UNSET_COLLECTION_ID) { - if (object.getCollectionId() != BaseConditionsObject.UNSET_ID) { + if (object.getCollectionId() != BaseConditionsObject.UNSET_ROW_ID) { if (object.getCollectionId() != this.collectionId) { throw new IllegalArgumentException("Cannot add object with different collection ID: " + object.getCollectionId()); } } else { - // System.out.println("object.setCollectionId - " + this.collectionId); try { object.setCollectionId(this.collectionId); } catch (final ConditionsObjectException e) { @@ -55,7 +57,12 @@ } } } - return this.objects.add(object); + final boolean added = this.objects.add(object); + if (!added) { + throw new RuntimeException("Failed to add object."); + } + this.isDirty = true; + return added; } /** @@ -63,14 +70,13 @@ * @throws SQLException */ @Override - public void deleteAll() throws ConditionsObjectException, SQLException { + public final void delete() throws DatabaseObjectException, SQLException { Statement statement = null; try { final String sql = "DELETE FROM `" + this.tableMetaData.getTableName() + "` WHERE collection_id = '" + getCollectionId() + "'"; statement = this.connection.createStatement(); statement.executeUpdate(sql); - // System.out.println("result from delete: " + result); this.connection.commit(); } catch (final SQLException e) { e.printStackTrace(); @@ -82,7 +88,7 @@ } @Override - public ObjectType get(final int index) { + public final ObjectType get(final int index) { if (index + 1 > this.size() || index < 0) { throw new IndexOutOfBoundsException("index out of bounds: " + index); } @@ -97,12 +103,12 @@ } @Override - public int getCollectionId() { + public final int getCollectionId() { return this.collectionId; } @Override - public TableMetaData getTableMetaData() { + public final TableMetaData getTableMetaData() { return this.tableMetaData; } @@ -112,12 +118,22 @@ * @throws SQLException */ @Override - public void insertAll(final int collectionId) throws ConditionsObjectException, SQLException { - if (this.collectionId != -1) { - throw new RuntimeException("collection already exists"); - } - this.collectionId = collectionId; - + public final void insert() throws DatabaseObjectException, SQLException { + + // Turn off auto-commit to perform a transaction. + this.connection.setAutoCommit(false); + + if (this.collectionId == BaseConditionsObject.UNSET_COLLECTION_ID) { + // Automatically get the next global collection ID from the conditions database. + this.collectionId = this.getNextCollectionId(); + } else { + // If the collection already exists in the database with this ID then it cannot be inserted. + if (checkExists()) { + throw new DatabaseObjectException("The collection " + this.collectionId + + " cannot be inserted because it already exists in the " + this.tableMetaData.getTableName() + + " table.", this); + } + } PreparedStatement insertObjects = null; final StringBuffer sb = new StringBuffer(); sb.append("INSERT INTO " + this.getTableMetaData().getTableName() + " ("); @@ -132,25 +148,27 @@ sb.setLength(sb.length() - 2); sb.append(")"); final String updateStatement = sb.toString(); - // System.out.println(updateStatement); - try { - this.connection.setAutoCommit(false); + try { insertObjects = this.connection.prepareStatement(updateStatement, Statement.RETURN_GENERATED_KEYS); for (final ObjectType object : this) { - object.setCollectionId(this.collectionId); + try { + object.setCollectionId(this.collectionId); + } catch (final ConditionsObjectException e) { + throw new DatabaseObjectException("Error setting collection ID on object.", object); + } for (int fieldIndex = 0; fieldIndex < this.getTableMetaData().getFieldNames().length; fieldIndex++) { final String fieldName = this.getTableMetaData().getFieldNames()[fieldIndex]; - final Object value = object.getFieldValue(getTableMetaData().getFieldType(fieldName), fieldName); - System.out.println(fieldName + "=" + value); insertObjects.setObject(fieldIndex + 1, object.getFieldValue(getTableMetaData().getFieldType(fieldName), fieldName)); } insertObjects.executeUpdate(); - this.connection.commit(); final ResultSet resultSet = insertObjects.getGeneratedKeys(); resultSet.next(); - object.setId(resultSet.getInt(1)); - // System.out.println("set id to " + resultSet.getInt(1) + " from generated keys"); + object.setRowId(resultSet.getInt(1)); + + // This will commit the insert statements for the collections info table and the records. + this.connection.commit(); + resultSet.close(); } } catch (final SQLException e1) { @@ -159,7 +177,6 @@ try { System.err.println("Transaction is being rolled back ..."); this.connection.rollback(); - System.err.println("Done rolling back transaction!"); } catch (final SQLException e2) { e2.printStackTrace(); } @@ -168,24 +185,53 @@ if (insertObjects != null) { insertObjects.close(); } - } - } - - @Override - public boolean remove(final int index) { - final ObjectType object = get(index); - if (object != null) { - return this.objects.remove(object); - } else { - return false; - } - } - - @Override - public void select(final int collectionId) throws SQLException, ConditionsObjectException { - // System.out.println("BaseConditionsObjectCollection.select - " + collectionId); + this.connection.setAutoCommit(true); + } + } + + /** + * Add a row for a new collection in the <i>collections</i> table and return the new collection ID assigned to it. + * + * @param tableName the name of the table + * @param comment an optional comment about this new collection + * @return the collection's ID + * @throws SQLException + */ + private synchronized int getNextCollectionId() throws SQLException, DatabaseObjectException { + final String log = "BaseConditionsObject generated new collection ID"; + final String description = "inserted " + this.size() + " records into " + this.tableMetaData.getTableName(); + PreparedStatement statement = null; + ResultSet resultSet = null; + int collectionId = -1; + try { + statement = this.connection.prepareStatement( + "INSERT INTO collections (table_name, log, description, created) VALUES (?, ?, ?, NOW())", + Statement.RETURN_GENERATED_KEYS); + statement.setString(1, this.tableMetaData.getTableName()); + statement.setString(2, log); + statement.setString(3, description); + statement.execute(); + resultSet = statement.getGeneratedKeys(); + if (!resultSet.next()) { + throw new DatabaseObjectException("Failed to create new collection record.", this); + } + collectionId = resultSet.getInt(1); + } finally { + if (resultSet != null) { + resultSet.close(); + } + if (statement != null) { + statement.close(); + } + } + return collectionId; + } + + @Override + public final boolean select(final int collectionId) throws SQLException, DatabaseObjectException { this.collectionId = collectionId; Statement statement = null; + boolean selected = false; try { statement = this.connection.createStatement(); final StringBuffer sb = new StringBuffer(); @@ -196,7 +242,6 @@ sb.setLength(sb.length() - 2); sb.append(" FROM " + this.tableMetaData.getTableName() + " WHERE collection_id=" + collectionId); final String sql = sb.toString(); - // System.out.println(sql); final ResultSet resultSet = statement.executeQuery(sql); while (resultSet.next()) { try { @@ -204,12 +249,13 @@ newObject.setConnection(this.connection); newObject.setTableMetaData(this.tableMetaData); final int id = resultSet.getInt(1); - newObject.setId(id); + newObject.setRowId(id); for (int fieldIndex = 0; fieldIndex < this.tableMetaData.getFieldNames().length; fieldIndex++) { final String fieldName = this.tableMetaData.getFieldNames()[fieldIndex]; newObject.setFieldValue(fieldName, resultSet.getObject(fieldIndex + 2)); } add(newObject); + selected = true; } catch (InstantiationException | IllegalAccessException e) { throw new RuntimeException(e); } @@ -219,17 +265,38 @@ statement.close(); } } - } - - @Override - public void setCollectionId(final int collectionId) { + return selected; + } + + @Override + public final void setCollectionId(final int collectionId) { this.collectionId = collectionId; + try { + // Set collection ID on all objects. + setConditionsObjectCollectionIds(); + } catch (final ConditionsObjectException e) { + throw new RuntimeException("Error setting collection ID on object.", e); + } + } + + private final void setConditionsObjectCollectionIds() throws ConditionsObjectException { + for (final ConditionsObject object : this) { + object.setCollectionId(this.collectionId); + } + } + + private final void setConditionsObjectConnections() { + for (final ConditionsObject object : this) { + object.setConnection(this.connection); + } } @Override public void setConnection(final Connection connection) { this.connection = connection; - // TODO: This should set the connection on all objects also. + + // Set connection on all objects. + setConditionsObjectConnections(); } @Override @@ -243,13 +310,17 @@ } @Override - // FIXME: Should execute prepared statement in transaction. - public void updateAll() throws ConditionsObjectException, SQLException { + // FIXME: This method should execute a prepared statement in a transaction. + public boolean update() throws DatabaseObjectException, SQLException { + boolean updated = false; for (final ObjectType object : this.objects) { if (object.isDirty()) { - object.update(); - } - } + if (object.update() && updated == false) { + updated = true; + } + } + } + return updated; } /** @@ -297,4 +368,41 @@ public Iterator<ObjectType> iterator() { return this.objects.iterator(); } + + @Override + public boolean isDirty() { + return this.isDirty; + } + + @Override + public boolean isNew() { + if (this.collectionId == BaseConditionsObject.UNSET_COLLECTION_ID) { + return true; + } + try { + return checkExists(); + } catch (final SQLException e) { + throw new RuntimeException(e); + } + } + + private boolean checkExists() throws SQLException { + Statement statement = null; + ResultSet resultSet = null; + boolean exists = false; + try { + statement = this.connection.createStatement(); + resultSet = statement.executeQuery("SELECT id FROM " + this.tableMetaData.getTableName() + + " where collection_id=" + this.collectionId); + exists = resultSet.next(); + } finally { + if (statement != null) { + statement.close(); + } + if (resultSet != null) { + resultSet.close(); + } + } + return exists; + } } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObject.java Wed Apr 22 16:39:56 2015 @@ -1,21 +1,13 @@ package org.hps.conditions.api; -import java.sql.Connection; -import java.sql.SQLException; - -public interface ConditionsObject { - - /** - * @return - * @throws ConditionsObjectException - * @throws SQLException - */ - boolean delete() throws ConditionsObjectException, SQLException; +public interface ConditionsObject extends DatabaseObject { /** * @return */ Integer getCollectionId(); + + boolean hasValidCollection(); FieldValues getFieldValues(); @@ -25,13 +17,6 @@ int getRowId(); /** - * @return - */ - TableMetaData getTableMetaData(); - - /** - * q - * * @param type * @param name * @return @@ -41,50 +26,19 @@ <T> T getFieldValue(final String name); /** - * @return - * @throws ConditionsObjectException - * @throws SQLException - */ - boolean insert() throws ConditionsObjectException, SQLException; - - /** - * @return - */ - boolean isDirty(); - - /** - * @return - */ - boolean isNew(); - - boolean select(final int rowId) throws ConditionsObjectException, SQLException; - - /** * @param collectionId * @throws ConditionsObjectException */ void setCollectionId(int collectionId) throws ConditionsObjectException; - /** - * @param connection - */ - void setConnection(Connection connection); - void setFieldValues(FieldValues fieldValues); - void setId(int id); - - void setTableMetaData(TableMetaData tableMetaData); + // FIXME: Maybe this should not be in the interface. + void setRowId(int id); /** * @param name * @param value */ void setFieldValue(String name, Object value); - - /** - * @return - * @throws ConditionsObjectException - */ - boolean update() throws ConditionsObjectException, SQLException; } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectCollection.java Wed Apr 22 16:39:56 2015 @@ -1,36 +1,20 @@ package org.hps.conditions.api; -import java.sql.Connection; -import java.sql.SQLException; import java.util.Comparator; -public interface ConditionsObjectCollection<ObjectType extends ConditionsObject> extends Iterable<ObjectType> { +public interface ConditionsObjectCollection<ObjectType extends ConditionsObject> extends Iterable<ObjectType>, + DatabaseObject { boolean add(final ObjectType object); - - void deleteAll() throws ConditionsObjectException, SQLException; ObjectType get(final int index); int getCollectionId(); - TableMetaData getTableMetaData(); - - void insertAll(final int collectionId) throws ConditionsObjectException, SQLException; - - boolean remove(final int index); - - void select(final int collectionId) throws ConditionsObjectException, SQLException; - - void setCollectionId(int collectionId); - - void setTableMetaData(TableMetaData tableMetaData); - - void setConnection(Connection connection); + // FIXME: Perhaps should not be part of interface. + void setCollectionId(int id); int size(); - - void updateAll() throws ConditionsObjectException, SQLException; void sort(final Comparator<ObjectType> comparator); Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/ConditionsObjectUtilities.java Wed Apr 22 16:39:56 2015 @@ -70,18 +70,16 @@ public static Set<String> getFieldNames(final Class<? extends ConditionsObject> type) { final Set<String> fieldNames = new HashSet<String>(); for (final Method method : type.getMethods()) { - System.out.println(method.getName()); if (!method.getReturnType().equals(Void.TYPE)) { for (final Annotation annotation : method.getAnnotations()) { if (annotation.annotationType().equals(Field.class)) { if (!Modifier.isPublic(method.getModifiers())) { throw new RuntimeException("The method " + type.getName() + "." + method.getName() - + " has a Field annotation but is not public."); + + " has a Field annotation, but it is not public."); } final Field field = (Field) annotation; for (final String fieldName : field.names()) { if (fieldName != null && !"".equals(fieldName)) { - System.out.println(" " + fieldName); fieldNames.add(fieldName); } } Added: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java (added) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObject.java Wed Apr 22 16:39:56 2015 @@ -0,0 +1,62 @@ +package org.hps.conditions.api; + +import java.sql.Connection; +import java.sql.SQLException; + +public interface DatabaseObject { + + /** + * Get the {@link TableMetaData} for this object. + * + * @return the {@link TableMetaData} for this object. + */ + TableMetaData getTableMetaData(); + + void setTableMetaData(TableMetaData tableMetaData); + + /** + * Insert the data of this object into the database. + * <p> + * This could be a single object or a collection of objects depending on the implementation. + * + * @throws ConditionsObjectException + * @throws SQLException + */ + public void insert() throws DatabaseObjectException, SQLException; + + /** + * Set the database <code>Connection</code> for the object. + * + * @param connection the database <code>Connection</code> for the object + */ + void setConnection(Connection connection); + + /** + * Return <code>true</code> if there are local data modifications that have not been persisted to the database. + * + * @return <code>true</code> if there un-persisted local data modifications + */ + boolean isDirty(); + + /** + * Return <code>true</code> if the record + * + * @return + */ + boolean isNew(); + + /** + * @return <code>true</code> if an update occurred + * @throws ConditionsObjectException + */ + boolean update() throws DatabaseObjectException, SQLException; + + /** + * @return + * @throws ConditionsObjectException + * @throws SQLException + */ + void delete() throws DatabaseObjectException, SQLException; + + boolean select(final int id) throws DatabaseObjectException, SQLException; +} Added: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java (added) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/api/DatabaseObjectException.java Wed Apr 22 16:39:56 2015 @@ -0,0 +1,16 @@ +package org.hps.conditions.api; + +public final class DatabaseObjectException extends Exception { + + private final DatabaseObject object; + + public DatabaseObjectException(final String message, final DatabaseObject object) { + super(message); + this.object = object; + } + + public DatabaseObject getDatabaseObject() { + return this.object; + } + +} Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/AddCommand.java Wed Apr 22 16:39:56 2015 @@ -8,8 +8,8 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.hps.conditions.api.ConditionsObjectException; import org.hps.conditions.api.ConditionsRecord; +import org.hps.conditions.api.DatabaseObjectException; import org.hps.conditions.api.FieldValuesMap; import org.hps.conditions.database.DatabaseConditionsManager; import org.lcsim.util.log.LogUtil; @@ -161,13 +161,9 @@ if (!DatabaseConditionsManager.getInstance().isConnected()) { createdConnection = manager.openConnection(); } - try { - conditionsRecord.insert(); - } catch (final SQLException e) { - throw new RuntimeException("Error inserting new conditions record.", e); - } + conditionsRecord.insert(); manager.closeConnection(createdConnection); - } catch (final ConditionsObjectException e) { + } catch (final SQLException | DatabaseObjectException e) { LOGGER.log(Level.SEVERE, "Error adding conditions record", e); throw new RuntimeException("An error occurred while adding a conditions record.", e); } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/cli/TagCommand.java Wed Apr 22 16:39:56 2015 @@ -10,9 +10,9 @@ import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import org.apache.commons.cli.Options; -import org.hps.conditions.api.ConditionsObjectException; import org.hps.conditions.api.ConditionsRecord; import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection; +import org.hps.conditions.api.DatabaseObjectException; import org.hps.conditions.api.TableMetaData; import org.hps.conditions.database.DatabaseConditionsManager; import org.lcsim.conditions.ConditionsManager.ConditionsNotFoundException; @@ -23,7 +23,7 @@ * * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> */ -// TODO: Add multiple records strategy (last updated, last created, etc.). +// TODO: Add command switch to specify multiple records strategy (last updated, last created, latest run end, etc.). public class TagCommand extends AbstractCommand { /** @@ -158,8 +158,8 @@ // Create the tag in the database if user verified or force option was present. if (makeTag) { try { - tagRecords.insertAll(-1); // FIXME: I guess insertAll should be overridden for ConditionsRecord. - } catch (ConditionsObjectException | SQLException e) { + tagRecords.insert(); + } catch (DatabaseObjectException | SQLException e) { throw new RuntimeException(e); } } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsRecordConverter.java Wed Apr 22 16:39:56 2015 @@ -6,9 +6,9 @@ import org.hps.conditions.api.AbstractConditionsObjectConverter; import org.hps.conditions.api.BaseConditionsObjectCollection; import org.hps.conditions.api.ConditionsObject; -import org.hps.conditions.api.ConditionsObjectException; import org.hps.conditions.api.ConditionsRecord; import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection; +import org.hps.conditions.api.DatabaseObjectException; import org.hps.conditions.api.TableMetaData; import org.lcsim.conditions.ConditionsManager; @@ -68,9 +68,7 @@ // resultSet, tableMetaData); collection.add(conditionsRecord); } - } catch (final SQLException x) { - throw new RuntimeException("Database error", x); - } catch (final ConditionsObjectException e) { + } catch (final DatabaseObjectException | SQLException e) { throw new RuntimeException("Error creating new conditions record.", e); } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/database/ConditionsSeriesConverter.java Wed Apr 22 16:39:56 2015 @@ -4,10 +4,10 @@ import org.hps.conditions.api.ConditionsObject; import org.hps.conditions.api.ConditionsObjectCollection; -import org.hps.conditions.api.ConditionsObjectException; import org.hps.conditions.api.ConditionsRecord; import org.hps.conditions.api.ConditionsRecord.ConditionsRecordCollection; import org.hps.conditions.api.ConditionsSeries; +import org.hps.conditions.api.DatabaseObjectException; import org.hps.conditions.api.TableMetaData; /** @@ -93,7 +93,7 @@ collection.setTableMetaData(tableMetaData); collection.setConnection(conditionsManager.getConnection()); collection.select(conditionsRecord.getCollectionId()); - } catch (final ConditionsObjectException | SQLException e) { + } catch (final DatabaseObjectException | SQLException e) { throw new RuntimeException(e); } series.add((ConditionsObjectCollection<ObjectType>) collection); Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/dummy/DummyConditionsObject.java Wed Apr 22 16:39:56 2015 @@ -5,7 +5,7 @@ import org.hps.conditions.api.BaseConditionsObject; import org.hps.conditions.api.BaseConditionsObjectCollection; -import org.hps.conditions.api.ConditionsObjectException; +import org.hps.conditions.api.DatabaseObjectException; import org.hps.conditions.api.TableMetaData; import org.hps.conditions.database.Field; import org.hps.conditions.database.Table; @@ -17,11 +17,12 @@ public final class DummyConditionsObject extends BaseConditionsObject { public static class DummyConditionsObjectCollection extends BaseConditionsObjectCollection<DummyConditionsObject> { + public DummyConditionsObjectCollection() { } - DummyConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData) - throws SQLException, ConditionsObjectException { + public DummyConditionsObjectCollection(final Connection connection, final TableMetaData tableMetaData) + throws SQLException, DatabaseObjectException { super(connection, tableMetaData, -1); } } Modified: java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java (original) +++ java/branches/conditions-HPSJAVA-488/src/main/java/org/hps/conditions/svt/SvtConditionsLoader.java Wed Apr 22 16:39:56 2015 @@ -128,11 +128,11 @@ // Set the collection ID. final int collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID( SvtConditionsLoader.CALIBRATIONS_TABLE_NAME); - // calibrations.setCollectionId(collectionID); + calibrations.setCollectionId(collectionID); logger.info("Using collection ID " + collectionID); // Load the calibrations - calibrations.insertAll(collectionID); + calibrations.insert(); logger.info("A total of " + calibrations.size() + " SvtCalibrations were loaded successfully into the database."); @@ -167,11 +167,11 @@ // Set the collection ID int collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID( SvtConditionsLoader.DAQ_MAP_TABLE_NAME); - // daqMapping.setCollectionId(collectionID); + daqMapping.setCollectionId(collectionID); logger.info("Using collection ID " + collectionID); // Load the DAQ map - daqMapping.insertAll(collectionID); + daqMapping.insert(); logger.info("DAQ map has been loaded successfully"); logger.fine(daqMapping.toString()); @@ -193,10 +193,10 @@ // Set the collection ID collectionID = DatabaseConditionsManager.getInstance().getNextCollectionID( SvtConditionsLoader.SVT_CHANNELS_TABLE_NAME); - // svtChannels.setCollectionId(collectionID); + svtChannels.setCollectionId(collectionID); logger.info("Using collection ID " + collectionID); - svtChannels.insertAll(collectionID); + svtChannels.insert(); logger.info("A total of " + svtChannels.size() + " SvtChannels were successfully loaded into the database."); Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java (added) +++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectCollectionTest.java Wed Apr 22 16:39:56 2015 @@ -0,0 +1,66 @@ +package org.hps.conditions.dummy; + +import java.sql.Connection; + +import junit.framework.TestCase; + +import org.hps.conditions.api.TableMetaData; +import org.hps.conditions.api.TableRegistry; +import org.hps.conditions.database.DatabaseConditionsManager; +import org.hps.conditions.dummy.DummyConditionsObject.DummyConditionsObjectCollection; + +public class DummyConditionsObjectCollectionTest extends TestCase { + + public void testBaseConditionsObjectCollection() throws Exception { + + // Configure the conditions system. + final DatabaseConditionsManager manager = DatabaseConditionsManager.getInstance(); + manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop"); + manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml"); + final Connection connection = manager.getConnection(); + + // Setup basic table meta data. + final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy"); + + // Create a new collection. + final DummyConditionsObjectCollection collection = new DummyConditionsObjectCollection(connection, + tableMetaData); + + // Add object to collection. + final DummyConditionsObject object1 = new DummyConditionsObject(connection, tableMetaData); + object1.setFieldValue("dummy", 1.0); + collection.add(object1); + + // Add object to collection. + final DummyConditionsObject object2 = new DummyConditionsObject(connection, tableMetaData); + object2.setFieldValue("dummy", 2.0); + collection.add(object2); + + // Insert all objects into the database. + collection.insert(); + + System.out.println(collection.size() + " objects inserted into " + collection.getCollectionId()); + + assertTrue("Collection isNew returned wrong value.", !collection.isNew()); + + // Create another collection. + final DummyConditionsObjectCollection anotherCollection = new DummyConditionsObjectCollection(connection, + tableMetaData); + + // Select the previously created objects into this collection by using the collection_id value. + anotherCollection.select(collection.getCollectionId()); + System.out.println("selected " + anotherCollection.size() + " objects into collection"); + + // Change the objects locally. + anotherCollection.get(0).setFieldValue("dummy", 3.0); + anotherCollection.get(1).setFieldValue("dummy", 4.0); + + // Update all objects. + System.out.println("updating objects from collection " + collection.getCollectionId()); + anotherCollection.update(); + + // Delete all objects. + System.out.println("deleting objects from collection " + collection.getCollectionId()); + collection.delete(); + } +} Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java (added) +++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectConverterTest.java Wed Apr 22 16:39:56 2015 @@ -0,0 +1,48 @@ +package org.hps.conditions.dummy; + +import junit.framework.TestCase; + +import org.hps.conditions.api.TableMetaData; +import org.hps.conditions.api.TableRegistry; +import org.hps.conditions.database.DatabaseConditionsManager; +import org.hps.conditions.dummy.DummyConditionsObject.DummyConditionsObjectCollection; + +/** + * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> + */ +public class DummyConditionsObjectConverterTest extends TestCase { + + public void testConditionsObjectConverter() throws Exception { + + final DatabaseConditionsManager manager = DatabaseConditionsManager.getInstance(); + manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop"); + manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml"); + manager.registerConditionsConverter(new DummyConditionsObjectConverter()); + manager.setDetector("HPS-dummy-detector", 1); + manager.openConnection(); + + final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy"); + + final DummyConditionsObjectCollection newCollection = new DummyConditionsObjectCollection(); + newCollection.setTableMetaData(tableMetaData); + newCollection.setConnection(manager.getConnection()); + + final DummyConditionsObject object = new DummyConditionsObject(manager.getConnection(), tableMetaData); + object.setFieldValue("dummy", 1.2345); + newCollection.setCollectionId(1001); + newCollection.add(object); + + try { + newCollection.insert(); + + final DummyConditionsObjectCollection readCollection = manager.getCachedConditions( + DummyConditionsObjectCollection.class, "dummy").getCachedData(); + + System.out.println("got dummy collection " + readCollection.getCollectionId() + " with " + + readCollection.size() + " objects"); + } finally { + System.out.println("deleting collection " + newCollection.getCollectionId()); + newCollection.delete(); + } + } +} Added: java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java ============================================================================= --- java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java (added) +++ java/branches/conditions-HPSJAVA-488/src/test/java/org/hps/conditions/dummy/DummyConditionsObjectTest.java Wed Apr 22 16:39:56 2015 @@ -0,0 +1,148 @@ +package org.hps.conditions.dummy; + +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; + +import junit.framework.TestCase; + +import org.hps.conditions.api.DatabaseObjectException; +import org.hps.conditions.api.TableMetaData; +import org.hps.conditions.api.TableRegistry; +import org.hps.conditions.database.DatabaseConditionsManager; + +/** + * Perform create, select, update, and delete operations on the <code>BaseConditionsObject</code> class via a dummy + * conditions class implementation with a single double value. + * + * @author <a href="mailto:[log in to unmask]">Jeremy McCormick</a> + */ +public final class DummyConditionsObjectTest extends TestCase { + + private static DatabaseConditionsManager manager; + + @Override + public void setUp() { + // Configure the conditions system. This uses my local development database that is not globally accessible. + // --JM + manager = DatabaseConditionsManager.getInstance(); + manager.setConnectionResource("/org/hps/conditions/config/jeremym_dev_connection.prop"); + manager.setXmlConfig("/org/hps/conditions/config/conditions_database_no_svt.xml"); + } + + /** + * Dummy value. + */ + private static final double DUMMY_VALUE1 = 1.0; + + /** + * Another dummy value. + */ + private static final double DUMMY_VALUE2 = 2.0; + + /** + * Perform the test. + * + * @throws Exception if errors occurred when performing the test + */ + public void testBaseConditionsObject() throws Exception { + + // Open the database connection. + final Connection connection = manager.getConnection(); + + // Get table meta data. + final TableMetaData tableMetaData = TableRegistry.getTableRegistry().findByTableName("dummy"); + + assertNotNull("No meta data found for dummy table.", tableMetaData); + + // Insert a new object. + final DummyConditionsObject newObject = new DummyConditionsObject(connection, tableMetaData); + + assertEquals("The isNew method returned the wrong value.", true, newObject.isNew()); + + // The insert operation should fail with an error. + try { + newObject.insert(); + throw new RuntimeException("The insert operation on an invalid object should have failed."); + } catch (final DatabaseObjectException e) { + System.err.println(e.getMessage()); + } + + // The delete operation should fail with an error. + try { + newObject.delete(); + throw new RuntimeException("The delete operation on an invalid object should have failed."); + } catch (final DatabaseObjectException e) { + System.err.println(e.getMessage()); + } + + // The update operation should fail with an error. + final boolean updated = newObject.update(); + assertTrue("The update operation on a new object should have failed.", !updated); + + newObject.setFieldValue("collection_id", 42); /* Use an arbitrary collection ID value. */ + newObject.setFieldValue("dummy", DUMMY_VALUE1); + newObject.insert(); + System.out.println("Inserted object with id " + newObject.getRowId() + " into " + + newObject.getTableMetaData().getTableName() + " table."); + assertEquals("The isNew method returned the wrong value.", false, newObject.isNew()); + assertEquals("The isDirty method returned the wrong value.", false, newObject.isDirty()); + assertEquals("Object does not have a valid collection after insert.", true, newObject.hasValidCollection()); + + // Select into another object by ID. + final DummyConditionsObject anotherObject = new DummyConditionsObject(connection, tableMetaData); + final int rowId = newObject.getRowId(); + anotherObject.select(rowId); + System.out.println("Selected row " + rowId + " into another object."); + + // Check that the selection into another object worked. + assertEquals("Selected object has wrong row id.", newObject.getRowId(), anotherObject.getRowId()); + assertTrue("Select object does not have valid collection.", anotherObject.hasValidCollection()); + assertEquals("Selected object has wrong collection id.", newObject.getCollectionId(), + anotherObject.getCollectionId()); + assertEquals("Selected object has wrong value.", newObject.getDummy(), anotherObject.getDummy()); + + // Update the same object. + newObject.setFieldValue("dummy", DUMMY_VALUE2); + System.out.println("Set dummy to " + DUMMY_VALUE2 + " in existing object."); + assertEquals("The value is wrong before update.", DUMMY_VALUE2, newObject.getDummy()); + assertEquals("The isDirty method returned the wrong value.", true, newObject.isDirty()); + assertEquals("The updated method returned the wrong value.", true, newObject.update()); + assertEquals("The value is wrong after update.", DUMMY_VALUE2, newObject.getDummy()); + assertEquals("The isDirty method returned the wrong value.", false, newObject.isDirty()); + + // Update which should be ignored on non-dirty record. + assertEquals("The update method should have returned false.", false, newObject.update()); + + // Select again into another object. + anotherObject.select(newObject.getRowId()); + + // Select into another object using the row ID. + assertEquals("Select object has wrong value after update.", newObject.getDummy(), DUMMY_VALUE2); + + // Delete the object. + newObject.delete(); + + System.out.println("Deleted object."); + + assertEquals("The isNew method returned the wrong value after delete.", true, newObject.isNew()); + + final boolean selected = anotherObject.select(rowId); + assertEquals("The select operation returned the wrong value after delete.", false, selected); + } + + /** + * Cleanup the table used for the test. + */ + @Override + public void tearDown() { + Statement statement = null; + try { + statement = manager.getConnection().createStatement(); + statement.executeUpdate("DELETE from dummy"); + } catch (final SQLException e) { + e.printStackTrace(); + } + manager.closeConnection(); + } +}