Java tutorial
/* * Copyright The Sett Ltd, 2005 to 2014. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.thesett.catalogue.config; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Iterator; import java.util.concurrent.atomic.AtomicLong; import org.apache.log4j.Logger; import org.hibernate.Session; import org.hibernate.Transaction; import org.hibernate.jdbc.Work; import com.thesett.aima.attribute.impl.EnumeratedStringAttribute; import com.thesett.aima.attribute.impl.HierarchyAttribute; import com.thesett.aima.attribute.impl.HierarchyType; import com.thesett.aima.state.ComponentType; import com.thesett.aima.state.Type; import com.thesett.catalogue.model.Catalogue; import com.thesett.catalogue.model.ViewType; import com.thesett.common.config.ConfigBeanContext; import com.thesett.common.config.ConfigException; import com.thesett.common.util.ReflectionUtils; import com.thesett.common.util.StringUtils; import com.thesett.index.config.IndexStoreConfigBean; /** * CatalogueConfigBeanImpl performs application start-up time configurations to prepare a catalogue model for use. It * loads and validates the in-memory knowledge level model of the catalogue. * * <p/>'Reference types' are types that specify a restricted set of values that are enumerated in a database. For * example enumerations such as { male; female } or hierarchical labelings and so on are called reference types. * * <p/>The start-up configuration consists of checking that the reference types have been created and populated in the * database. If the database has been created from scratch, then the reference tables need to be filled in (this is * usually only done during testing against a temporary database). For existing reference tables, the data is loaded * into memory for each type, to establish type in memory and possibly finalize it. * * <p/>This config bean depends on {@link ModelLoaderConfigBean}, {@link HibernateConfigBean}, {@link ModeConfigBean} * and {@link IndexStoreConfigBean} having been succesfully configured first. * * <p/>Configuration is handled differently depending on the value of the development mode switch: * * <ul> * <li>In development mode the database is created (by {@link HibernateConfigBean}) and any reference types, are * populated by this bean. The database will always be empty at the end of the catalogue configuration. * <li>In production mode the database schema is verified against the catalogue model and reference type data is * verified against the catalogue model and loaded into memory by this bean. At the moment the free text search * {@link com.thesett.index.Index} implementations used do not persist their data so all the data in the database is * re-indexed during configuration too. Once persistent indexes are implemented this step will be changed to an optional * verification step that checks that the indexes and database are in synch and corrects any mismatches.</li> * </ul> * * <pre><p/><table id="crc"><caption>CRC Card</caption> * <tr><th> Responsibilities <th> Collaborations * <tr><td> Transform the raw catalogue model into the knowledge level catalogue model. * <tr><td> Verify or populate the database reference types. * <tr><td> Re-index all text search indexes. * </table></pre> * * @author Rupert Smith */ public class CatalogueConfigBeanImpl implements Serializable, CatalogueConfigBean { /** Used for logging. */ private static final Logger log = Logger.getLogger(CatalogueConfigBeanImpl.class); /** Flag used to indicate that this config bean has been succesfully run. */ private final boolean configured = false; /** Used to hold the package name under which the model has been generated. */ private String modelPackage; /** Holds the catalogue as a first order logic model. */ private Catalogue model; /** Holds a referece to the hibernate config bean to get hibernate sessions from. */ protected HibernateConfigBean hibernateBean; /** * Checks whether or not the config bean has been succesfully run and is in a configured state. * * @return True if the config bean has run its configuration succesfully. */ public boolean getConfigured() { return configured; } /** {@inheritDoc} */ public Catalogue getCatalogue() { return model; } /** {@inheritDoc} */ public String getModelPackage() { return modelPackage; } /** {@inheritDoc} */ public void setModelPackage(String packageName) { this.modelPackage = packageName; } /** * Ensures that all hierarchy attribute classes are established in the database and in memory. * * @param force Setting this to true tells the config bean to re-run its configuration action even if * it has already been run. * @param configBeanContext A reference to the configurator that is managing the whole configuration process. * * @throws ConfigException If some error occurs that means that the configuration cannot be succesfully completed. */ public void doConfigure(boolean force, ConfigBeanContext configBeanContext) throws ConfigException { // If already configured then only reconfigure if force is set to true if (configured && !force) { return; } // Ensure that the catalogue model loader config bean has been run, and get a reference to it. ModelLoaderConfigBean loaderBean = (ModelLoaderConfigBean) configBeanContext .getConfiguredBean("com.thesett.catalogue.config.ModelLoaderConfigBean"); // Ensure that the hibernate config bean has been set up, and get a reference to it. hibernateBean = (HibernateConfigBean) configBeanContext .getConfiguredBean("com.thesett.catalogue.config.HibernateConfigBean"); // Ensure that the mode config bean has been set up, and get a reference to it. ModeConfigBean modeBean = (ModeConfigBean) configBeanContext .getConfiguredBean("com.thesett.catalogue.config.ModeConfigBean"); // Create the catalogue logical model from the raw model. model = loaderBean.getCatalogue(); // Set up a static references to the component types on each component in the model. initializeAllTypes(); // Check if running in development mode. if (modeBean.isDevMode()) { log.debug("In dev mode, creating reference types in the database."); createReferenceTypes(); } // Not in development mode so running in production mode. else { log.warn("Todo: In production mode, loading and checking reference types from the database."); } // Rebuild the indexes from the database if necessary. if (!modeBean.isDevMode()) { log.warn("Todo: In production mode, re-building indexes from the database."); } } /** * Passes the catalogue model to all component type class implementations, so that they can reference back to their * component types in the knowledge level of the catalogue model. */ private void initializeAllTypes() { log.debug("private void initializeComponentTypes(): called"); for (ComponentType componentType : model.getAllComponentTypes()) { setStaticCatalogue(componentType); } for (HierarchyType hierarchyType : model.getAllHierarchyTypes()) { setStaticCatalogue(hierarchyType); } for (EnumeratedStringAttribute.EnumeratedStringType enumerationType : model.getAllEnumTypes()) { setStaticCatalogue(enumerationType); } } /** * Calls the static 'setCatalogue' method on a type, so that the type can reach the catalogue. * * @param type The type to call the static 'setCatalogue' method on. */ private void setStaticCatalogue(Type type) { try { String typeName = type.getName(); String typeClassName = StringUtils.toCamelCaseUpper(typeName); Class clz = null; if (type instanceof ViewType) { clz = ReflectionUtils.forName(model.getModelPackage() + "." + typeClassName + "Impl"); } else { clz = ReflectionUtils.forName(model.getModelPackage() + "." + typeClassName); } //Class clz = type.getBaseClass(); Method setCatalogueMethod = clz.getMethod("setModel", Catalogue.class); ReflectionUtils.callStaticMethod(setCatalogueMethod, new Object[] { model }); } catch (NoSuchMethodException e) { throw new IllegalStateException(e); } } /** Initialized all reference types in the database. */ private void createReferenceTypes() { createHierarchyReferenceTypes(); createEnumerationReferenceTypes(); } /** Initializes all hierarchy reference types in the database. */ private void createHierarchyReferenceTypes() { log.debug("private void createHierarchyReferenceTypes(): called"); for (HierarchyType hierarchyType : model.getAllHierarchyTypes()) { String hierarchyName = hierarchyType.getName(); String hierarchyClassName = StringUtils.toCamelCaseUpper(hierarchyName); Class theBeanClass = ReflectionUtils.forName(model.getModelPackage() + "." + hierarchyClassName); for (Iterator<HierarchyAttribute> hierarchyIterator = hierarchyType .getAllPossibleValuesIterator(false); hierarchyIterator.hasNext();) { HierarchyAttribute hierarchyAttribute = hierarchyIterator.next(); Session session = hibernateBean.getSecondarySession(); Transaction transaction = session.beginTransaction(); // Create an instance of the hierarchy bean class using a constructor on the hierarchy value. Class[] arguments = new Class[] { HierarchyAttribute.class }; Constructor beanConstructor = ReflectionUtils.getConstructor(theBeanClass, arguments); Object theBean = ReflectionUtils.newInstance(beanConstructor, new Object[] { hierarchyAttribute }); log.debug("Created hierarchy bean: " + theBean); // Store the hierarchy value in the database. session.save(theBean); transaction.commit(); session.close(); } } } /** Initializes all enumeration reference types in the database. */ private void createEnumerationReferenceTypes() { log.debug("private void createEnumerationReferenceTypes(): called"); final AtomicLong id = new AtomicLong(); for (EnumeratedStringAttribute.EnumeratedStringType enumType : model.getAllEnumTypes()) { Session session = hibernateBean.getSecondarySession(); Transaction transaction = session.beginTransaction(); for (Iterator<EnumeratedStringAttribute> enumIterator = enumType .getAllPossibleValuesIterator(false); enumIterator.hasNext();) { final EnumeratedStringAttribute enumAttribute = enumIterator.next(); String enumName = enumAttribute.getType().getName(); String enumClassName = StringUtils.toCamelCaseUpper(enumName); // Create an instance of the enumeration bean class using a constructor on the enumeration value. Class theBeanClass = ReflectionUtils.forName(model.getModelPackage() + "." + enumClassName); Class[] arguments = new Class[] { EnumeratedStringAttribute.class }; Constructor beanConstructor = ReflectionUtils.getConstructor(theBeanClass, arguments); enumAttribute.setId(id.get()); Object theBean = ReflectionUtils.newInstance(beanConstructor, new Object[] { enumAttribute }); log.debug("Created enum bean: " + theBean); // Store the hierarchy value in the database. final String tableName = enumClassName + "_enumeration"; session.doWork(new Work() { public void execute(Connection connection) throws SQLException { PreparedStatement sql = null; sql = connection.prepareStatement("DELETE FROM " + tableName); sql.execute(); String value = enumAttribute.getStringValue(); sql = connection.prepareStatement("INSERT INTO " + tableName + " VALUES (?, ?)"); sql.setLong(1, id.getAndIncrement()); sql.setString(2, value); sql.execute(); } }); } transaction.commit(); session.close(); } } }