Author: [log in to unmask] Date: Wed Apr 15 12:16:21 2015 New Revision: 2708 Log: Fix inserting ConditionsRecord collections using this method. Modified: java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java Modified: java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java ============================================================================= --- java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java (original) +++ java/trunk/conditions/src/main/java/org/hps/conditions/database/DatabaseConditionsManager.java Wed Apr 15 12:16:21 2015 @@ -60,145 +60,40 @@ public final class DatabaseConditionsManager extends ConditionsManagerImplementation { /** + * Name of system property that can be used to specify custom database connection parameters. + */ + private static final String CONNECTION_PROPERTY = "org.hps.conditions.connection.file"; + + /** + * The default XML config. + */ + private static final String DEFAULT_CONFIG = "/org/hps/conditions/config/conditions_database_prod.xml"; + + /** + * The connection properties resource for connecting to the default JLAB database. + */ + private static final String DEFAULT_CONNECTION_PROPERTIES_RESOURCE = "/org/hps/conditions/config/jlab_connection.prop"; + + /** + * The Eng Run XML config. + */ + private static final String ENGRUN_CONFIG = "/org/hps/conditions/config/conditions_database_engrun.xml"; + + /** * Initialize the logger. */ private static Logger logger = LogUtil.create(DatabaseConditionsManager.class.getName(), new DefaultLogFormatter(), Level.FINE); /** - * Create the global registry of conditions object converters. - */ - private ConverterRegistry converters = ConverterRegistry.create(); - - /** - * Create the global registry of table meta data. - */ - private TableRegistry tableRegistry = TableRegistry.create(); - - /** - * Name of system property that can be used to specify custom database connection parameters. - */ - private static final String CONNECTION_PROPERTY = "org.hps.conditions.connection.file"; - - /** - * The connection properties file, if one is being used from the command line. - */ - private File connectionPropertiesFile; - - /** - * The current connection parameters. - */ - private ConnectionParameters connectionParameters; - - /** - * The current database connection. - */ - private Connection connection; - - /** - * True if manager is connected to the database. - */ - private boolean isConnected = false; - - /** - * Flag used to print connection parameters one time. - */ - private boolean loggedConnectionParameters = false; - - /** - * The default XML config. - */ - private static final String DEFAULT_CONFIG = "/org/hps/conditions/config/conditions_database_prod.xml"; - - /** * The Test Run XML config. */ private static final String TEST_RUN_CONFIG = "/org/hps/conditions/config/conditions_database_testrun_2012.xml"; /** - * The Eng Run XML config. - */ - private static final String ENGRUN_CONFIG = "/org/hps/conditions/config/conditions_database_engrun.xml"; - - /** - * The connection properties resource for connecting to the default JLAB database. - */ - private static final String DEFAULT_CONNECTION_PROPERTIES_RESOURCE = "/org/hps/conditions/config/jlab_connection.prop"; - - /** * The max value for a run to be considered Test Run. */ private static final int TEST_RUN_MAX_RUN = 1365; - - /** - * The default ECAL detector name in the detector geometry. - */ - private String ecalName = "Ecal"; - - /** - * The default SVT name in the detector geometry. - */ - private String svtName = "Tracker"; - - /** - * The converter for creating the combined SVT conditions object. - */ - private ConditionsConverter svtConverter; - - /** - * The converter for creating the combined ECAL conditions object. - */ - private ConditionsConverter ecalConverter; - - /** - * The helper for setting up the SVT detector with its conditions information. - */ - private SvtDetectorSetup svtSetup = new SvtDetectorSetup(svtName); - - /** - * The currently active conditions tag. - */ - private String tag = null; - - /** - * True if the manager has been initialized, e.g. the {@link #setDetector(String, int)} method was called. - */ - private boolean isInitialized = false; - - /** - * True if the conditions system has been frozen and will ignore updates after it is initialized. - */ - private boolean isFrozen = false; - - /** - * True if the conditions manager was configured from an XML configuration resource or file. - */ - private boolean isConfigured = false; - - /** - * True to setup the SVT detector model with conditions. - */ - private boolean setupSvtDetector = true; - - /** - * True to freeze the system after initialization. - */ - private boolean freezeAfterInitialize = false; - - /** - * True to close the connection after initialization. - */ - private boolean closeConnectionAfterInitialize = true; - - /** - * True to cache all known conditions sets (from keys) during initialization. - */ - private boolean cacheAllConditions = false; - - /** - * True if current run number is from Test Run. - */ - private boolean isTestRun = false; static { // Set default login timeout of 5 seconds. @@ -206,21 +101,6 @@ } /** - * Class constructor. Calling this will automatically register this manager as the global default. - */ - private DatabaseConditionsManager() { - registerConditionsConverter(new DetectorConditionsConverter()); - setupConnectionFromSystemProperty(); - ConditionsManager.setDefaultConditionsManager(this); - setRun(-1); - for (AbstractConditionsObjectConverter converter : converters.values()) { - // logger.fine("registering converter for " + converter.getType()); - registerConditionsConverter(converter); - } - addConditionsListener(svtSetup); - } - - /** * Get the static instance of this class. * * @return the static instance of the manager @@ -230,8 +110,7 @@ logger.finest("getting conditions manager instance"); // Is there no manager installed yet? - if (!ConditionsManager.isSetup() || - !(ConditionsManager.defaultInstance() instanceof DatabaseConditionsManager)) { + if (!ConditionsManager.isSetup() || !(ConditionsManager.defaultInstance() instanceof DatabaseConditionsManager)) { logger.finest("creating new DatabaseConditionsManager"); // Create a new instance if necessary, which will install it globally as the default. new DatabaseConditionsManager(); @@ -251,6 +130,25 @@ } /** + * Get the Logger for this class, which can be used by related sub-classes if they do not have their own logger. + * + * @return the Logger for this class + */ + public static Logger getLogger() { + return logger; + } + + /** + * Utility method to determine if a run number is from the 2012 Test Run. + * + * @param runNumber the run number + * @return <code>true</code> if run number is from the Test Run + */ + public static boolean isTestRun(final int runNumber) { + return runNumber > 0 && runNumber <= TEST_RUN_MAX_RUN; + } + + /** * Reset the global static instance of the conditions manager to a new object. */ public static synchronized void resetInstance() { @@ -259,50 +157,138 @@ } /** - * Set the log level. - * - * @param level the new log level - */ - public void setLogLevel(final Level level) { - logger.config("setting log level to " + level); - logger.setLevel(level); - logger.getHandlers()[0].setLevel(level); - svtSetup.setLogLevel(level); - } - - /** - * Open the database connection. - * - * @return <code>true</code> if a connection was opened; <code>false</code> if using an existing connection. - */ - public synchronized boolean openConnection() { - boolean openedConnection = false; - if (!isConnected) { - // Do the connection parameters need to be figured out automatically? - if (connectionParameters == null) { - // Setup the default read-only connection, which will choose a SLAC or JLab database. - connectionParameters = ConnectionParameters.fromResource(DEFAULT_CONNECTION_PROPERTIES_RESOURCE); - } - - if (!loggedConnectionParameters) { - // Print out detailed info to the log on first connection within the job. - logger.info("opening connection ... " + '\n' + "connection: " - + connectionParameters.getConnectionString() + '\n' + "host: " - + connectionParameters.getHostname() + '\n' + "port: " + connectionParameters.getPort() + '\n' - + "user: " + connectionParameters.getUser() + '\n' + "database: " - + connectionParameters.getDatabase()); - loggedConnectionParameters = true; - } - - // Create the connection using the parameters. - connection = connectionParameters.createConnection(); - isConnected = true; - openedConnection = true; - } - logger.info("connection opened successfully"); - - // Flag to indicate whether an existing connection was used or not. - return openedConnection; + * True to cache all known conditions sets (from keys) during initialization. + */ + private boolean cacheAllConditions = false; + + /** + * True to close the connection after initialization. + */ + private boolean closeConnectionAfterInitialize = true; + + /** + * The current database connection. + */ + private Connection connection; + + /** + * The current connection parameters. + */ + private ConnectionParameters connectionParameters; + + /** + * The connection properties file, if one is being used from the command line. + */ + private File connectionPropertiesFile; + + /** + * Create the global registry of conditions object converters. + */ + private final ConverterRegistry converters = ConverterRegistry.create(); + + /** + * The converter for creating the combined ECAL conditions object. + */ + private ConditionsConverter ecalConverter; + + /** + * The default ECAL detector name in the detector geometry. + */ + private String ecalName = "Ecal"; + + /** + * True to freeze the system after initialization. + */ + private boolean freezeAfterInitialize = false; + + /** + * True if the conditions manager was configured from an XML configuration resource or file. + */ + private boolean isConfigured = false; + + /** + * True if manager is connected to the database. + */ + private boolean isConnected = false; + + /** + * True if the conditions system has been frozen and will ignore updates after it is initialized. + */ + private boolean isFrozen = false; + + /** + * True if the manager has been initialized, e.g. the {@link #setDetector(String, int)} method was called. + */ + private boolean isInitialized = false; + + /** + * True if current run number is from Test Run. + */ + private boolean isTestRun = false; + + /** + * Flag used to print connection parameters one time. + */ + private boolean loggedConnectionParameters = false; + + /** + * True to setup the SVT detector model with conditions. + */ + private boolean setupSvtDetector = true; + + /** + * The converter for creating the combined SVT conditions object. + */ + private ConditionsConverter svtConverter; + + /** + * The default SVT name in the detector geometry. + */ + private String svtName = "Tracker"; + + /** + * The helper for setting up the SVT detector with its conditions information. + */ + private final SvtDetectorSetup svtSetup = new SvtDetectorSetup(this.svtName); + + /** + * Create the global registry of table meta data. + */ + private final TableRegistry tableRegistry = TableRegistry.create(); + + /** + * The currently active conditions tag. + */ + private String tag = null; + + /** + * Class constructor. Calling this will automatically register this manager as the global default. + */ + private DatabaseConditionsManager() { + registerConditionsConverter(new DetectorConditionsConverter()); + setupConnectionFromSystemProperty(); + ConditionsManager.setDefaultConditionsManager(this); + setRun(-1); + for (final AbstractConditionsObjectConverter converter : this.converters.values()) { + // logger.fine("registering converter for " + converter.getType()); + registerConditionsConverter(converter); + } + addConditionsListener(this.svtSetup); + } + + /** + * Cache conditions sets for all known tables. + */ + private void cacheConditionsSets() { + for (final TableMetaData meta : this.tableRegistry.values()) { + try { + logger.fine("caching conditions " + meta.getKey() + " with type " + + meta.getCollectionClass().getCanonicalName()); + getCachedConditions(meta.getCollectionClass(), meta.getKey()); + } catch (final Exception e) { + logger.warning("could not cache conditions " + meta.getKey()); + } + } } /** @@ -310,23 +296,23 @@ */ public synchronized void closeConnection() { logger.fine("closing connection"); - if (connection != null) { + if (this.connection != null) { try { - if (!connection.isClosed()) { - connection.close(); + if (!this.connection.isClosed()) { + this.connection.close(); } - } catch (SQLException e) { + } catch (final SQLException e) { throw new RuntimeException(e); } } - connection = null; - isConnected = false; + this.connection = null; + this.isConnected = false; logger.info("connection closed"); } /** * Close the database connection but only if there was a connection opened based on the flag. Otherwise, it should - * be left open. Used in conjunction with return value of {@link #openConnection()}. + * be left open. Used in conjunction with return value of {@link #openConnection()}. * * @param connectionOpened <code>true</code> to close the connection; <code>false</code> to leave it open */ @@ -334,170 +320,6 @@ if (connectionOpened) { closeConnection(); } - } - - /** - * Get a conditions series with one or more collections. - * - * @param collectionType the type of the collection - * @param tableName the name of the data table - * @param <ObjectType> the type of the conditions object - * @param <CollectionType> the type of the conditions collection - * @return the conditions series - */ - @SuppressWarnings("unchecked") - public - <ObjectType extends ConditionsObject, CollectionType extends ConditionsObjectCollection<ObjectType>> - ConditionsSeries<ObjectType, CollectionType> getConditionsSeries( - final Class<CollectionType> collectionType, final String tableName) { - - final TableMetaData metaData = tableRegistry.get(tableName); - if (metaData == null) { - throw new IllegalArgumentException("No table metadata found for type " + collectionType.getName()); - } - if (!metaData.getCollectionClass().equals(collectionType)) { - throw new IllegalArgumentException("The type " + collectionType.getName() + " does not match the class " - + metaData.getCollectionClass().getName() + " from the meta data"); - } - final Class<? extends ConditionsObject> objectType = metaData.getObjectClass(); - final ConditionsSeriesConverter<ObjectType, CollectionType> converter = - new ConditionsSeriesConverter(objectType, collectionType); - return converter.createSeries(tableName); - } - - /** - * This method handles changes to the detector name and run number. It is called every time an LCSim event is - * created, and so it has internal logic to figure out if the conditions system actually needs to be updated. - */ - @Override - public synchronized void setDetector(final String detectorName, final int runNumber) - throws ConditionsNotFoundException { - - logger.finest("setDetector " + detectorName + " with run number " + runNumber); - - if (detectorName == null) { - throw new IllegalArgumentException("The detectorName argument is null."); - } - - if (!isInitialized || !detectorName.equals(getDetector()) || runNumber != getRun()) { - if (!isFrozen) { - logger.info("new detector " + detectorName + " and run #" + runNumber); - initialize(detectorName, runNumber); - } else { - logger.finest("Conditions changed but will be ignored because manager is frozen."); - } - } - } - - /** - * Utility method to determine if a run number is from the 2012 Test Run. - * - * @param runNumber the run number - * @return <code>true</code> if run number is from the Test Run - */ - public static boolean isTestRun(final int runNumber) { - return runNumber > 0 && runNumber <= TEST_RUN_MAX_RUN; - } - - /** - * Return <code>true</code> if Test Run configuration is active - * - * @return <code>true</code> if Test Run configuration is active - */ - public boolean isTestRun() { - return isTestRun; - } - - /** - * Get the current LCSim compact <code>Detector</code> object with the geometry and detector model. - * - * @return the detector object - */ - public Detector getDetectorObject() { - return getCachedConditions(Detector.class, "compact.xml").getCachedData(); - } - - /** - * Configure some properties of this object from an XML file - * - * @param file the XML file - */ - public void setXmlConfig(final File file) { - logger.config("setting XML config from file " + file.getPath()); - if (!file.exists()) { - throw new IllegalArgumentException("The config file does not exist: " + file.getPath()); - } - try { - configure(new FileInputStream(file)); - } catch (FileNotFoundException e) { - throw new RuntimeException(e); - } - } - - /** - * Configure this object from an embedded XML resource. - * - * @param resource the embedded XML resource - */ - public void setXmlConfig(final String resource) { - logger.config("setting XML config from resource " + resource); - final InputStream is = getClass().getResourceAsStream(resource); - configure(is); - } - - /** - * Set the path to a properties file containing connection settings. - * - * @param file the properties file - */ - public void setConnectionProperties(final File file) { - logger.config("setting connection properties file " + file.getPath()); - if (!file.exists()) { - throw new IllegalArgumentException("The connection properties file does not exist: " - + connectionPropertiesFile.getPath()); - } - connectionParameters = ConnectionParameters.fromProperties(file); - } - - /** - * Set the connection parameters of the conditions database. - * - * @param connectionParameters the connection parameters - */ - public void setConnectionParameters(final ConnectionParameters connectionParameters) { - this.connectionParameters = connectionParameters; - } - - /** - * Set the connection parameters from an embedded resource location. - * - * @param resource the classpath resource location - */ - public void setConnectionResource(final String resource) { - logger.config("setting connection resource " + resource); - connectionParameters = ConnectionParameters.fromResource(resource); - } - - /** - * Get the next collection ID for a database conditions table. - * - * @param tableName the name of the table - * @return the next collection ID - */ - public synchronized int getNextCollectionID(final String tableName) { - final boolean openedConnection = openConnection(); - final ResultSet resultSet = selectQuery("SELECT MAX(collection_id)+1 FROM " + tableName); - int collectionId = 1; - try { - resultSet.next(); - collectionId = resultSet.getInt(1); - } catch (SQLException e) { - e.printStackTrace(); - logger.warning(e.getMessage()); - } - logger.fine("new collection ID " + collectionId + " created for table " + tableName); - closeConnection(openedConnection); - return collectionId; } /** @@ -512,64 +334,42 @@ final ResultSet resultSet = selectQuery(sql); try { resultSet.last(); - } catch (SQLException e) { + } catch (final SQLException e) { e.printStackTrace(); } int rowCount = 0; try { rowCount = resultSet.getRow(); - } catch (SQLException e) { + } catch (final SQLException e) { e.printStackTrace(); } return rowCount != 0; } /** - * This method can be used to perform a database SELECT query. - * - * @param query the SQL query string - * @return the <code>ResultSet</code> from the query - * @throws RuntimeException if there is a query error - */ - ResultSet selectQuery(final String query) { - logger.fine("executing SQL select query ..." + '\n' + query); - ResultSet result = null; - Statement statement = null; - try { - statement = connection.createStatement(); - result = statement.executeQuery(query); - } catch (SQLException x) { - throw new RuntimeException("Error in query: " + query, x); - } - return result; - } - - /** - * Perform a SQL query with an update command like INSERT, DELETE or UPDATE. - * - * @param query the SQL query string - * @return the keys of the rows affected - */ - public List<Integer> updateQuery(final String query) { - final boolean openedConnection = openConnection(); - logger.fine("executing SQL update query ..." + '\n' + query); - final List<Integer> keys = new ArrayList<Integer>(); - Statement statement = null; - ResultSet resultSet = null; - try { - statement = connection.createStatement(); - statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS); - resultSet = statement.getGeneratedKeys(); - while (resultSet.next()) { - final int key = resultSet.getInt(1); - keys.add(key); - } - } catch (SQLException x) { - throw new RuntimeException("Error in SQL query: " + query, x); - } - DatabaseUtilities.cleanup(resultSet); - closeConnection(openedConnection); - return keys; + * Configure this class from an <code>InputStream</code> which should point to an XML document. + * + * @param in the InputStream which should be in XML format + */ + private void configure(final InputStream in) { + if (!this.isConfigured) { + final SAXBuilder builder = new SAXBuilder(); + Document config = null; + try { + config = builder.build(in); + } catch (JDOMException | IOException e) { + throw new RuntimeException(e); + } + loadConfiguration(config); + try { + in.close(); + } catch (final IOException e) { + logger.warning(e.getMessage()); + } + this.isConfigured = true; + } else { + logger.warning("System is already configured, so call to configure is ignored!"); + } } /** @@ -584,7 +384,7 @@ "conditions").getCachedData(); logger.fine("searching for conditions with name " + name + " in " + runConditionsRecords.size() + " records"); final ConditionsRecordCollection foundConditionsRecords = new ConditionsRecordCollection(); - for (ConditionsRecord record : runConditionsRecords) { + for (final ConditionsRecord record : runConditionsRecords) { if (record.getName().equals(name)) { if (matchesTag(record)) { foundConditionsRecords.add(record); @@ -595,18 +395,41 @@ } } } - logger.fine("found " + foundConditionsRecords.size() + " conditions records matching tag " + tag); + logger.fine("found " + foundConditionsRecords.size() + " conditions records matching tag " + this.tag); return foundConditionsRecords; } /** - * True if there is a conditions record with the given name. - * - * @param name the conditions record name (usually will match to table name) - * @return <code>true</code> if a conditions record exists with the given name - */ - public boolean hasConditionsRecord(final String name) { - return !findConditionsRecords(name).isEmpty(); + * Find table information from the collection type. + * + * @param type the collection type + * @return the table information or <code>null</code> if does not exist + */ + public List<TableMetaData> findTableMetaData(final Class<?> type) { + return this.tableRegistry.findByCollectionType(type); + } + + /** + * Find table information from the name. + * + * @param name the name of the table + * @return the table information or <code>null</code> if does not exist + */ + public TableMetaData findTableMetaData(final String name) { + return this.tableRegistry.findByTableName(name); + } + + /** + * This method can be called to "freeze" the conditions system so that any subsequent updates to run number or + * detector name will be ignored. + */ + public synchronized void freeze() { + if (getDetector() != null && getRun() != -1) { + this.isFrozen = true; + logger.config("conditions system is frozen"); + } else { + logger.warning("conditions system cannot be frozen because it is not initialized yet"); + } } /** @@ -618,14 +441,13 @@ public ConditionsRecordCollection getConditionsRecords() { logger.finer("getting conditions records ..."); final ConditionsRecordCollection conditionsRecords = new ConditionsRecordCollection(); - for (TableMetaData tableMetaData : tableRegistry.values()) { + for (final TableMetaData tableMetaData : this.tableRegistry.values()) { try { - final ConditionsRecordCollection foundConditionsRecords = - findConditionsRecords(tableMetaData.getKey()); + final ConditionsRecordCollection foundConditionsRecords = findConditionsRecords(tableMetaData.getKey()); logger.finer("found " + foundConditionsRecords.size() + " collections with name " + tableMetaData.getKey()); conditionsRecords.addAll(foundConditionsRecords); - } catch (Exception e) { + } catch (final Exception e) { e.printStackTrace(); logger.warning(e.getMessage()); } @@ -636,6 +458,42 @@ } /** + * Get a conditions series with one or more collections. + * + * @param collectionType the type of the collection + * @param tableName the name of the data table + * @param <ObjectType> the type of the conditions object + * @param <CollectionType> the type of the conditions collection + * @return the conditions series + */ + @SuppressWarnings("unchecked") + public <ObjectType extends ConditionsObject, CollectionType extends ConditionsObjectCollection<ObjectType>> ConditionsSeries<ObjectType, CollectionType> getConditionsSeries( + final Class<CollectionType> collectionType, final String tableName) { + + final TableMetaData metaData = this.tableRegistry.get(tableName); + if (metaData == null) { + throw new IllegalArgumentException("No table metadata found for type " + collectionType.getName()); + } + if (!metaData.getCollectionClass().equals(collectionType)) { + throw new IllegalArgumentException("The type " + collectionType.getName() + " does not match the class " + + metaData.getCollectionClass().getName() + " from the meta data"); + } + final Class<? extends ConditionsObject> objectType = metaData.getObjectClass(); + final ConditionsSeriesConverter<ObjectType, CollectionType> converter = new ConditionsSeriesConverter( + objectType, collectionType); + return converter.createSeries(tableName); + } + + /** + * Get the current LCSim compact <code>Detector</code> object with the geometry and detector model. + * + * @return the detector object + */ + public Detector getDetectorObject() { + return getCachedConditions(Detector.class, "compact.xml").getCachedData(); + } + + /** * Get the combined ECAL conditions for this run. * * @return the combined ECAL conditions @@ -645,197 +503,52 @@ } /** + * Get the name of the ECAL in the detector geometry. + * + * @return the name of the ECAL + */ + public String getEcalName() { + return this.ecalName; + } + + /** + * Get the subdetector object of the ECAL. + * + * @return the ECAL subdetector + */ + public Subdetector getEcalSubdetector() { + return this.getDetectorObject().getSubdetector(this.ecalName); + } + + /** + * Get the next collection ID for a database conditions table. + * + * @param tableName the name of the table + * @return the next collection ID + */ + public synchronized int getNextCollectionID(final String tableName) { + final boolean openedConnection = openConnection(); + final ResultSet resultSet = selectQuery("SELECT MAX(collection_id)+1 FROM " + tableName); + int collectionId = 1; + try { + resultSet.next(); + collectionId = resultSet.getInt(1); + } catch (final SQLException e) { + e.printStackTrace(); + logger.warning(e.getMessage()); + } + logger.fine("new collection ID " + collectionId + " created for table " + tableName); + closeConnection(openedConnection); + return collectionId; + } + + /** * Get the combined SVT conditions for this run. * * @return the combined SVT conditions */ public SvtConditions getSvtConditions() { return this.getCachedConditions(SvtConditions.class, "svt_conditions").getCachedData(); - } - - /** - * This method can be called to "freeze" the conditions system so that any subsequent updates to run number or - * detector name will be ignored. - */ - public synchronized void freeze() { - if (getDetector() != null && getRun() != -1) { - isFrozen = true; - logger.config("conditions system is frozen"); - } else { - logger.warning("conditions system cannot be frozen because it is not initialized yet"); - } - } - - /** - * Un-freeze the conditions system so that updates will be received again. - */ - public synchronized void unfreeze() { - isFrozen = false; - logger.info("conditions system unfrozen"); - } - - /** - * True if conditions system is frozen - * - * @return <code>true</code> if conditions system is currently frozen - */ - public boolean isFrozen() { - return isFrozen; - } - - /** - * Set a tag used to filter the accessible conditions records - * - * @param tag the tag value used to filter returned conditions records - */ - public void setTag(final String tag) { - this.tag = tag; - logger.info("using conditions tag: " + tag); - } - - /** - * Insert a collection of ConditionsObjects into the database. - * - * @param collection the collection to insert - * @param <ObjectType> the type of the conditions object - * @throws SQLException if there is a database or SQL error - * @throws ConditionsObjectException if there is a problem inserting the object - */ - public <ObjectType extends ConditionsObject> void insertCollection( - final ConditionsObjectCollection<ObjectType> collection) - throws SQLException, ConditionsObjectException { - - if (collection == null) { - throw new IllegalArgumentException("The collection is null."); - } - if (collection.size() == 0) { - throw new IllegalArgumentException("The collection is empty."); - } - - final TableMetaData tableMetaData = collection.getTableMetaData(); - if (tableMetaData == null) { - final List<TableMetaData> metaDataList = tableRegistry.findByCollectionType(collection.getClass()); - if (metaDataList == null) { - // This is a fatal error because no meta data is available for the type. - throw new ConditionsObjectException("Failed to find meta data for type: " + collection.getClass()); - } - } - if (collection.getCollectionId() == -1) { - try { - collection.setCollectionId(getNextCollectionID(tableMetaData.getTableName())); - } catch (ConditionsObjectException e) { - throw new RuntimeException(e); - } - } - // FIXME: If collection ID is already set this should be an error! - - logger.info("inserting collection with ID " + collection.getCollectionId() + " and key " - + tableMetaData.getKey() + " into table " + tableMetaData.getTableName()); - - final boolean openedConnection = openConnection(); - - PreparedStatement preparedStatement = null; - - try { - connection.setAutoCommit(false); - logger.fine("starting insert transaction"); - final String sql = QueryBuilder.buildPreparedInsert( - tableMetaData.getTableName(), collection.iterator().next()); - preparedStatement = connection.prepareStatement(sql); - logger.fine("using prepared statement: " + sql); - final int collectionId = collection.getCollectionId(); - for (ConditionsObject object : collection) { - preparedStatement.setObject(1, collectionId); - int parameterIndex = 2; - for (Entry<String, Object> entry : object.getFieldValues().entrySet()) { - preparedStatement.setObject(parameterIndex, entry.getValue()); - ++parameterIndex; - } - preparedStatement.executeUpdate(); - } - connection.commit(); - logger.fine("committed transaction"); - } catch (Exception e) { - e.printStackTrace(); - logger.warning(e.getMessage()); - logger.warning("rolling back transaction"); - connection.rollback(); - logger.warning("transaction was rolled back"); - } finally { - connection.setAutoCommit(true); - } - - try { - preparedStatement.close(); - } catch (Exception e) { - } - - closeConnection(openedConnection); - } - - /** - * Check if connected to the database. - * - * @return <code>true</code> if connected - */ - public boolean isConnected() { - return isConnected; - } - - /** - * Get the Logger for this class, which can be used by related sub-classes if they do not have their own logger. - * - * @return the Logger for this class - */ - public static Logger getLogger() { - return logger; - } - - /** - * Find table information from the name. - * - * @param name the name of the table - * @return the table information or <code>null</code> if does not exist - */ - public TableMetaData findTableMetaData(final String name) { - return tableRegistry.findByTableName(name); - } - - /** - * Find table information from the collection type. - * - * @param type the collection type - * @return the table information or <code>null</code> if does not exist - */ - public List<TableMetaData> findTableMetaData(final Class<?> type) { - return tableRegistry.findByCollectionType(type); - } - - /** - * Get the name of the ECAL in the detector geometry. - * - * @return the name of the ECAL - */ - public String getEcalName() { - return ecalName; - } - - /** - * Get the subdetector object of the ECAL. - * - * @return the ECAL subdetector - */ - public Subdetector getEcalSubdetector() { - return this.getDetectorObject().getSubdetector(ecalName); - } - - /** - * True if conditions manager is properly initialized. - * - * @return <code>true</code> if the manager is initialized - */ - public boolean isInitialized() { - return isInitialized; } /** @@ -852,17 +565,17 @@ while (rs.next()) { tags.add(rs.getString(1)); } - } catch (SQLException e) { + } catch (final SQLException e) { throw new RuntimeException(e); } try { rs.close(); - } catch (SQLException e) { + } catch (final SQLException e) { logger.log(Level.WARNING, "error closing ResultSet", e); } final StringBuffer sb = new StringBuffer(); sb.append("found unique conditions tags: "); - for (String tag : tags) { + for (final String tag : tags) { sb.append(tag + " "); } sb.setLength(sb.length() - 1); @@ -872,9 +585,19 @@ } /** - * Perform all necessary initialization, including setup of the XML configuration and loading of conditions - * onto the Detector. This is called from the {@link #setDetector(String, int)} method to setup the manager - * for a new run or detector. + * True if there is a conditions record with the given name. + * + * @param name the conditions record name (usually will match to table name) + * @return <code>true</code> if a conditions record exists with the given name + */ + public boolean hasConditionsRecord(final String name) { + return !findConditionsRecords(name).isEmpty(); + } + + /** + * Perform all necessary initialization, including setup of the XML configuration and loading of conditions onto the + * Detector. This is called from the {@link #setDetector(String, int)} method to setup the manager for a new run or + * detector. * * @param detectorName the name of the detector model * @param runNumber the run number @@ -885,7 +608,7 @@ logger.config("initializing with detector " + detectorName + " and run " + runNumber); // Is not configured yet? - if (!isConfigured) { + if (!this.isConfigured) { if (isTestRun(runNumber)) { // This looks like the Test Run so use the custom configuration for it. setXmlConfig(DatabaseConditionsManager.TEST_RUN_CONFIG); @@ -903,8 +626,8 @@ registerConverters(); // Enable or disable the setup of the SVT detector. - logger.fine("enabling SVT setup: " + setupSvtDetector); - svtSetup.setEnabled(setupSvtDetector); + logger.fine("enabling SVT setup: " + this.setupSvtDetector); + this.svtSetup.setEnabled(this.setupSvtDetector); // Open the database connection. openConnection(); @@ -914,26 +637,26 @@ super.setDetector(detectorName, runNumber); // Should all conditions sets be cached? - if (cacheAllConditions) { + if (this.cacheAllConditions) { // Cache the conditions sets of all registered converters. logger.fine("caching all conditions sets ..."); cacheConditionsSets(); } - if (closeConnectionAfterInitialize) { + if (this.closeConnectionAfterInitialize) { logger.fine("closing connection after initialization"); // Close the connection. closeConnection(); } // Should the conditions system be frozen now? - if (freezeAfterInitialize) { + if (this.freezeAfterInitialize) { // Freeze the conditions system so subsequent updates will be ignored. freeze(); logger.config("system was frozen after initialization"); } - isInitialized = true; + this.isInitialized = true; logger.info("conditions system initialized successfully"); @@ -942,59 +665,183 @@ } /** - * Register the conditions converters with the manager. - */ - private void registerConverters() { - if (svtConverter != null) { - // Remove old SVT converter. - removeConditionsConverter(svtConverter); - } - - if (ecalConverter != null) { - // Remove old ECAL converter. - registerConditionsConverter(ecalConverter); - } - - // Is configured for TestRun? - if (isTestRun()) { - // Load Test Run specific converters. - svtConverter = new TestRunSvtConditionsConverter(); - ecalConverter = new TestRunEcalConditionsConverter(); - logger.config("registering Test Run conditions converters"); - } else { - // Load the default converters. - svtConverter = new SvtConditionsConverter(); - ecalConverter = new EcalConditionsConverter(); - logger.config("registering default conditions converters"); - } - registerConditionsConverter(svtConverter); - registerConditionsConverter(ecalConverter); - } - - /** - * Set the name of the ECAL sub-detector. - * - * @param ecalName the name of the ECAL subdetector - */ - private void setEcalName(final String ecalName) { - if (ecalName == null) { - throw new IllegalArgumentException("The ecalName is null"); - } - this.ecalName = ecalName; - logger.info("ECAL name set to " + ecalName); - } - - /** - * Set the name of the SVT subdetector. - * - * @param svtName the name of the SVT subdetector - */ - private void setSvtName(final String svtName) { - if (svtName == null) { - throw new IllegalArgumentException("The svtName is null"); - } - this.svtName = svtName; - logger.info("SVT name set to " + ecalName); + * Insert a collection of ConditionsObjects into the database. + * + * @param collection the collection to insert + * @param <ObjectType> the type of the conditions object + * @throws SQLException if there is a database or SQL error + * @throws ConditionsObjectException if there is a problem inserting the object + */ + public <ObjectType extends ConditionsObject> void insertCollection( + final ConditionsObjectCollection<ObjectType> collection) throws SQLException, ConditionsObjectException { + + if (collection == null) { + throw new IllegalArgumentException("The collection is null."); + } + if (collection.size() == 0) { + throw new IllegalArgumentException("The collection is empty."); + } + + TableMetaData tableMetaData = collection.getTableMetaData(); + if (tableMetaData == null) { + final List<TableMetaData> metaDataList = this.tableRegistry.findByCollectionType(collection.getClass()); + if (metaDataList == null) { + // This is a fatal error because no meta data is available for the type. + throw new ConditionsObjectException("Failed to find meta data for type: " + collection.getClass()); + } + tableMetaData = metaDataList.get(0); + } + if (collection.getCollectionId() == -1) { + try { + collection.setCollectionId(getNextCollectionID(tableMetaData.getTableName())); + } catch (final ConditionsObjectException e) { + throw new RuntimeException(e); + } + } + // FIXME: If collection ID is already set this should be an error! + + logger.info("inserting collection with ID " + collection.getCollectionId() + " and key " + + tableMetaData.getKey() + " into table " + tableMetaData.getTableName()); + + final boolean openedConnection = openConnection(); + + PreparedStatement preparedStatement = null; + + try { + this.connection.setAutoCommit(false); + logger.fine("starting insert transaction"); + final String sql = QueryBuilder.buildPreparedInsert(tableMetaData.getTableName(), collection.iterator() + .next()); + preparedStatement = this.connection.prepareStatement(sql); + logger.fine("using prepared statement: " + sql); + for (final ConditionsObject object : collection) { + int parameterIndex = 1; + for (final Entry<String, Object> entry : object.getFieldValues().entrySet()) { + preparedStatement.setObject(parameterIndex, entry.getValue()); + ++parameterIndex; + } + preparedStatement.executeUpdate(); + } + this.connection.commit(); + logger.fine("committed transaction"); + } catch (final Exception e) { + e.printStackTrace(); + logger.warning(e.getMessage()); + logger.warning("rolling back transaction"); + this.connection.rollback(); + logger.warning("transaction was rolled back"); + } finally { + this.connection.setAutoCommit(true); + } + + try { + preparedStatement.close(); + } catch (final Exception e) { + } + + closeConnection(openedConnection); + } + + /** + * Check if connected to the database. + * + * @return <code>true</code> if connected + */ + public boolean isConnected() { + return this.isConnected; + } + + /** + * True if conditions system is frozen + * + * @return <code>true</code> if conditions system is currently frozen + */ + public boolean isFrozen() { + return this.isFrozen; + } + + /** + * True if conditions manager is properly initialized. + * + * @return <code>true</code> if the manager is initialized + */ + public boolean isInitialized() { + return this.isInitialized; + } + + /** + * Return <code>true</code> if Test Run configuration is active + * + * @return <code>true</code> if Test Run configuration is active + */ + public boolean isTestRun() { + return this.isTestRun; + } + + /** + * Load configuration information from an XML document. + * + * @param document the XML document + */ + private void loadConfiguration(final Document document) { + + final Element node = document.getRootElement().getChild("configuration"); + + if (node == null) { + return; + } + + Element element = node.getChild("setupSvtDetector"); + if (element != null) { + this.setupSvtDetector = Boolean.parseBoolean(element.getText()); + logger.config("setupSvtDetector = " + this.setupSvtDetector); + } + + element = node.getChild("ecalName"); + if (element != null) { + setEcalName(element.getText()); + } + + element = node.getChild("svtName"); + if (element != null) { + setSvtName(element.getText()); + } + + element = node.getChild("freezeAfterInitialize"); + if (element != null) { + this.freezeAfterInitialize = Boolean.parseBoolean(element.getText()); + logger.config("freezeAfterInitialize = " + this.freezeAfterInitialize); + } + + element = node.getChild("cacheAllCondition"); + if (element != null) { + this.cacheAllConditions = Boolean.parseBoolean(element.getText()); + logger.config("cacheAllConditions = " + this.cacheAllConditions); + } + + element = node.getChild("isTestRun"); + if (element != null) { + this.isTestRun = Boolean.parseBoolean(element.getText()); + logger.config("isTestRun = " + this.isTestRun); + } + + element = node.getChild("logLevel"); + if (element != null) { + setLogLevel(Level.parse(element.getText())); + } + + element = node.getChild("closeConnectionAfterInitialize"); + if (element != null) { + this.closeConnectionAfterInitialize = Boolean.parseBoolean(element.getText()); + logger.config("closeConnectionAfterInitialize = " + this.closeConnectionAfterInitialize); + } + + element = node.getChild("loginTimeout"); + if (element != null) { + final Integer timeout = Integer.parseInt(element.getText()); + DriverManager.setLoginTimeout(timeout); + logger.config("loginTimeout = " + timeout); + } } /** @@ -1013,47 +860,197 @@ // If there is a tag set but the record has no tag, it is rejected. return false; } - return tag.equals(recordTag); - } - - /** - * Cache conditions sets for all known tables. - */ - private void cacheConditionsSets() { - for (TableMetaData meta : tableRegistry.values()) { - try { - logger.fine("caching conditions " + meta.getKey() + " with type " + meta.getCollectionClass().getCanonicalName()); - getCachedConditions(meta.getCollectionClass(), meta.getKey()); - } catch (Exception e) { - logger.warning("could not cache conditions " + meta.getKey()); - } - } - } - - /** - * Configure this class from an <code>InputStream</code> which should point to an XML document. - * - * @param in the InputStream which should be in XML format - */ - private void configure(final InputStream in) { - if (!isConfigured) { - final SAXBuilder builder = new SAXBuilder(); - Document config = null; - try { - config = builder.build(in); - } catch (JDOMException | IOException e) { - throw new RuntimeException(e); - } - loadConfiguration(config); - try { - in.close(); - } catch (IOException e) { - logger.warning(e.getMessage()); - } - isConfigured = true; + return this.tag.equals(recordTag); + } + + /** + * Open the database connection. + * + * @return <code>true</code> if a connection was opened; <code>false</code> if using an existing connection. + */ + public synchronized boolean openConnection() { + boolean openedConnection = false; + if (!this.isConnected) { + // Do the connection parameters need to be figured out automatically? + if (this.connectionParameters == null) { + // Setup the default read-only connection, which will choose a SLAC or JLab database. + this.connectionParameters = ConnectionParameters.fromResource(DEFAULT_CONNECTION_PROPERTIES_RESOURCE); + } + + if (!this.loggedConnectionParameters) { + // Print out detailed info to the log on first connection within the job. + logger.info("opening connection ... " + '\n' + "connection: " + + this.connectionParameters.getConnectionString() + '\n' + "host: " + + this.connectionParameters.getHostname() + '\n' + "port: " + + this.connectionParameters.getPort() + '\n' + "user: " + this.connectionParameters.getUser() + + '\n' + "database: " + this.connectionParameters.getDatabase()); + this.loggedConnectionParameters = true; + } + + // Create the connection using the parameters. + this.connection = this.connectionParameters.createConnection(); + this.isConnected = true; + openedConnection = true; + } + logger.info("connection opened successfully"); + + // Flag to indicate whether an existing connection was used or not. + return openedConnection; + } + + /** + * Register the conditions converters with the manager. + */ + private void registerConverters() { + if (this.svtConverter != null) { + // Remove old SVT converter. + removeConditionsConverter(this.svtConverter); + } + + if (this.ecalConverter != null) { + // Remove old ECAL converter. + registerConditionsConverter(this.ecalConverter); + } + + // Is configured for TestRun? + if (isTestRun()) { + // Load Test Run specific converters. + this.svtConverter = new TestRunSvtConditionsConverter(); + this.ecalConverter = new TestRunEcalConditionsConverter(); + logger.config("registering Test Run conditions converters"); } else { - logger.warning("System is already configured, so call to configure is ignored!"); - } + // Load the default converters. + this.svtConverter = new SvtConditionsConverter(); + this.ecalConverter = new EcalConditionsConverter(); + logger.config("registering default conditions converters"); + } + registerConditionsConverter(this.svtConverter); + registerConditionsConverter(this.ecalConverter); + } + + /** + * This method can be used to perform a database SELECT query. + * + * @param query the SQL query string + * @return the <code>ResultSet</code> from the query + * @throws RuntimeException if there is a query error + */ + ResultSet selectQuery(final String query) { + logger.fine("executing SQL select query ..." + '\n' + query); + ResultSet result = null; + Statement statement = null; + try { + statement = this.connection.createStatement(); + result = statement.executeQuery(query); + } catch (final SQLException x) { + throw new RuntimeException("Error in query: " + query, x); + } + return result; + } + + /** + * Set the connection parameters of the conditions database. + * + * @param connectionParameters the connection parameters + */ + public void setConnectionParameters(final ConnectionParameters connectionParameters) { + this.connectionParameters = connectionParameters; + } + + /** + * Set the path to a properties file containing connection settings. + * + * @param file the properties file + */ + public void setConnectionProperties(final File file) { + logger.config("setting connection properties file " + file.getPath()); + if (!file.exists()) { + throw new IllegalArgumentException("The connection properties file does not exist: " + + this.connectionPropertiesFile.getPath()); + } + this.connectionParameters = ConnectionParameters.fromProperties(file); + } + + /** + * Set the connection parameters from an embedded resource location. + * + * @param resource the classpath resource location + */ + public void setConnectionResource(final String resource) { + logger.config("setting connection resource " + resource); + this.connectionParameters = ConnectionParameters.fromResource(resource); + } + + /** + * This method handles changes to the detector name and run number. It is called every time an LCSim event is + * created, and so it has internal logic to figure out if the conditions system actually needs to be updated. + */ + @Override + public synchronized void setDetector(final String detectorName, final int runNumber) + throws ConditionsNotFoundException { + + logger.finest("setDetector " + detectorName + " with run number " + runNumber); + + if (detectorName == null) { + throw new IllegalArgumentException("The detectorName argument is null."); + } + + if (!this.isInitialized || !detectorName.equals(getDetector()) || runNumber != getRun()) { + if (!this.isFrozen) { + logger.info("new detector " + detectorName + " and run #" + runNumber); + initialize(detectorName, runNumber); + } else { + logger.finest("Conditions changed but will be ignored because manager is frozen."); + } + } + } + + /** + * Set the name of the ECAL sub-detector. + * + * @param ecalName the name of the ECAL subdetector + */ + private void setEcalName(final String ecalName) { + if (ecalName == null) { + throw new IllegalArgumentException("The ecalName is null"); + } + this.ecalName = ecalName; + logger.info("ECAL name set to " + ecalName); + } + + /** + * Set the log level. + * + * @param level the new log level + */ + public void setLogLevel(final Level level) { + logger.config("setting log level to " + level); + logger.setLevel(level); + logger.getHandlers()[0].setLevel(level); + this.svtSetup.setLogLevel(level); + } + + /** + * Set the name of the SVT subdetector. + * + * @param svtName the name of the SVT subdetector + */ + private void setSvtName(final String svtName) { + if (svtName == null) { + throw new IllegalArgumentException("The svtName is null"); + } + this.svtName = svtName; + logger.info("SVT name set to " + this.ecalName); + } + + /** + * Set a tag used to filter the accessible conditions records + * + * @param tag the tag value used to filter returned conditions records + */ + public void setTag(final String tag) { + this.tag = tag; + logger.info("using conditions tag: " + tag); } /** @@ -1075,68 +1072,66 @@ } /** - * Load configuration information from an XML document. - * - * @param document the XML document - */ - private void loadConfiguration(final Document document) { - - final Element node = document.getRootElement().getChild("configuration"); - - if (node == null) { - return; - } - - Element element = node.getChild("setupSvtDetector"); - if (element != null) { - setupSvtDetector = Boolean.parseBoolean(element.getText()); - logger.config("setupSvtDetector = " + setupSvtDetector); - } - - element = node.getChild("ecalName"); - if (element != null) { - setEcalName(element.getText()); - } - - element = node.getChild("svtName"); - if (element != null) { - setSvtName(element.getText()); - } - - element = node.getChild("freezeAfterInitialize"); - if (element != null) { - freezeAfterInitialize = Boolean.parseBoolean(element.getText()); - logger.config("freezeAfterInitialize = " + freezeAfterInitialize); - } - - element = node.getChild("cacheAllCondition"); - if (element != null) { - cacheAllConditions = Boolean.parseBoolean(element.getText()); - logger.config("cacheAllConditions = " + cacheAllConditions); - } - - element = node.getChild("isTestRun"); - if (element != null) { - isTestRun = Boolean.parseBoolean(element.getText()); - logger.config("isTestRun = " + isTestRun); - } - - element = node.getChild("logLevel"); - if (element != null) { - setLogLevel(Level.parse(element.getText())); - } - - element = node.getChild("closeConnectionAfterInitialize"); - if (element != null) { - closeConnectionAfterInitialize = Boolean.parseBoolean(element.getText()); - logger.config("closeConnectionAfterInitialize = " + closeConnectionAfterInitialize); - } - - element = node.getChild("loginTimeout"); - if (element != null) { - final Integer timeout = Integer.parseInt(element.getText()); - DriverManager.setLoginTimeout(timeout); - logger.config("loginTimeout = " + timeout); - } + * Configure some properties of this object from an XML file + * + * @param file the XML file + */ + public void setXmlConfig(final File file) { + logger.config("setting XML config from file " + file.getPath()); + if (!file.exists()) { + throw new IllegalArgumentException("The config file does not exist: " + file.getPath()); + } + try { + configure(new FileInputStream(file)); + } catch (final FileNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + * Configure this object from an embedded XML resource. + * + * @param resource the embedded XML resource + */ + public void setXmlConfig(final String resource) { + logger.config("setting XML config from resource " + resource); + final InputStream is = getClass().getResourceAsStream(resource); + configure(is); + } + + /** + * Un-freeze the conditions system so that updates will be received again. + */ + public synchronized void unfreeze() { + this.isFrozen = false; + logger.info("conditions system unfrozen"); + } + + /** + * Perform a SQL query with an update command like INSERT, DELETE or UPDATE. + * + * @param query the SQL query string + * @return the keys of the rows affected + */ + public List<Integer> updateQuery(final String query) { + final boolean openedConnection = openConnection(); + logger.fine("executing SQL update query ..." + '\n' + query); + final List<Integer> keys = new ArrayList<Integer>(); + Statement statement = null; + ResultSet resultSet = null; + try { + statement = this.connection.createStatement(); + statement.executeUpdate(query, Statement.RETURN_GENERATED_KEYS); + resultSet = statement.getGeneratedKeys(); + while (resultSet.next()) { + final int key = resultSet.getInt(1); + keys.add(key); + } + } catch (final SQLException x) { + throw new RuntimeException("Error in SQL query: " + query, x); + } + DatabaseUtilities.cleanup(resultSet); + closeConnection(openedConnection); + return keys; } }