/* * EuroCarbDB, a framework for carbohydrate bioinformatics * * Copyright (c) 2006-2009, Eurocarb project, or third-party contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * A copy of this license accompanies this distribution in the file LICENSE.txt. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * Last commit: $Rev: 1986 $ by $Author: glycoslave $ on $Date:: 2010-09-08 #$ */ package org.eurocarbdb.servlet.init; // stdlib imports import java.util.Set; import java.util.List; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import javax.servlet.ServletException; // 3rd party imports import org.apache.log4j.Logger; import org.apache.commons.configuration.ConfigurationUtils; import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.PropertiesConfiguration; import org.apache.commons.configuration.CompositeConfiguration; import com.opensymphony.xwork.ActionProxyFactory; import com.opensymphony.xwork.config.entities.ActionConfig; import com.opensymphony.webwork.sitegraph.XWorkConfigRetriever; // eurocarb imports import org.eurocarbdb.tranche.TrancheAdmin; import org.eurocarbdb.dataaccess.Eurocarb; import org.eurocarbdb.dataaccess.EntityManager; import org.eurocarbdb.dataaccess.HibernateEntityManager; import org.eurocarbdb.dataaccess.core.Contributor; import org.eurocarbdb.dataaccess.core.GlycanSequence; import org.eurocarbdb.dataaccess.core.BiologicalContext; import org.eurocarbdb.dataaccess.core.Reference; import org.eurocarbdb.dataaccess.core.Evidence; // static imports import static org.eurocarbdb.util.StringUtils.join; import static org.eurocarbdb.util.StringUtils.CR; /** * This class is called when the Eurocarbdb web app context is started * or stopped. On startup, it is responsible for the following: *<ol> * <li>Loading the eurocarbdb application properties file</li> * <li>Starting the Tranche server (global file system), * if configured to do so ('ecdb.tranche.enabled' property)</li> * <li>Initialising Hibernate, and ensuring that the database * is receiving connections and can be queried</li> * <li>Checking the current Action configuration is sane</li> *</ol> * * @author mjh * @version $Rev: 1986 $ */ public class EurocarbApplicationContextHandler implements ServletContextListener { //~~~~~~~~~~~~~~~~~~~~~~ STATIC FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~// /** Logging handle. */ static final Logger log = Logger.getLogger( EurocarbApplicationContextHandler.class ); /** Default name of app config file */ public static final String ECDB_APP_PROPERTIES_FILE = "eurocarbdb-application.properties"; //~~~~~~~~~~~~~~~~~~~~~~~~~~ FIELDS ~~~~~~~~~~~~~~~~~~~~~~~~~~~// //~~~~~~~~~~~~~~~~~~~~~~~~~~ METHODS ~~~~~~~~~~~~~~~~~~~~~~~~~~// /** * Called when the servlet context is started. * * @see ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) */ public void contextInitialized( ServletContextEvent arg0 ) { initConfig(); startTrancheIfNeeded(); checkDataStore(); checkActions(); } /* (non-Javadoc) * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) */ public void contextDestroyed(ServletContextEvent arg0) { stopTrancheIfNeeded(); } //~~~~~~~~~~~~~~~~~~~~~~~~ PRIVATE METHODS ~~~~~~~~~~~~~~~~~~~~// /** * Iterates through the list of all currently known actions * and checks that the action class is loadable. */ protected void checkActions() { log.debug("checking actions..."); StringBuilder sb = new StringBuilder(); // get current webwork config Set<String> namespaces = (Set<String>) XWorkConfigRetriever.getNamespaces(); Set<String> action_names; for ( String ns : namespaces ) { action_names = (Set<String>) XWorkConfigRetriever.getActionNames( ns ); sb.append( " namespace " + (ns.length() > 0 ? ns : '/' ) + " has " + action_names.size() + " actions\n" ); List<String> sorted_action_names = new ArrayList( action_names ); Collections.sort( sorted_action_names ); ActionConfig ac; Set<String> result_names; for ( String action_name : sorted_action_names ) { ac = XWorkConfigRetriever.getActionConfig( ns, action_name ); String class_name = ac.getClassName(); // loading of action classes is somewhat redundant since it // appears to have already been done in first call to XWorkConfigRetriever try { Class.forName( class_name ); } catch ( ClassNotFoundException ex ) { log.warn( "Couldn't load action class for action '" + action_name + "': " , ex ); } /* result_names = (Set<String>) ac.getResults().keySet(); sb.append( " " + action_name + " (" + join( ", ", result_names ) + ")\n" ); */ } } log.info( "action config OK:\n" + sb ); return; } /** * Checks that it's possible to connect to the (default) data * store, start a transaction, and perform a basic query. */ protected void checkDataStore() { log.debug("checking data store exists and is queryable..."); try { EntityManager em = Eurocarb.getEntityManager(); em.beginUnitOfWork(); turn_genetic_query_optimiser_off(); int count_cb = em.countAll( Contributor.class ); int count_gs = em.countAll( GlycanSequence.class ); int count_bc = em.countAll( BiologicalContext.class ); int count_rf = em.countAll( Reference.class ); int count_ev = em.countAll( Evidence.class ); if ( em.getQuery( Contributor.class.getName() + ".ACTIVE_ADMINISTRATORS" ).list().size() == 0 ) { throw new Exception( "There are not active administrator users.\nCowardly refusing to start webapp\n"); } em.endUnitOfWork(); log.info( join( "\n " , "data store OK; default data store has:" , count_cb + " contributor(s)" , count_gs + " glycan sequence(s)" , count_bc + " biological context(s)" , count_rf + " reference(s)" , count_ev + " evidence" ) ); } catch ( org.hibernate.MappingNotFoundException ex ) { log.fatal( "Could not query the data store: " + ex + "-- did you perhaps forget to add a new mapping " + "file to the hibernate.cfg.xml?" ); throw new RuntimeException( ex ); } catch ( Exception ex ) { log.fatal("Could not query the data store: " + ex ); throw new RuntimeException( ex ); } } /** * Effectively turn postgres genetic query optimiser off. * This is *critical* for good performance of substructure queries. * This psql option can also be set via psql's own configuration * file but we set it here to make sure. */ private void turn_genetic_query_optimiser_off() { int max = org.eurocarbdb.dataaccess.core.seq.SubstructureQuery.MAX_SUBSTRUCTURE_RESIDUES; try { log.debug("setting genetic query optimiser threshold to " + max ); ((HibernateEntityManager) Eurocarb.getEntityManager()) .getHibernateSession() .createSQLQuery( "set geqo_threshold = " + max ) .executeUpdate(); } catch ( Exception ex ) { log.warn("Caught exception while turning off genetic query optimiser", ex ); } } protected void startTrancheIfNeeded() { if ( Eurocarb.getConfiguration().getBoolean("ecdb.tranche.enabled") ) { log.info("starting the Tranche server (set ecdb.tranche.enabled=false to disable)"); TrancheAdmin.StartServer(); } else log.debug("Tranche not started (ecdb.tranche.enabled=false)"); } protected void stopTrancheIfNeeded() { if ( Eurocarb.getConfiguration().getBoolean("ecdb.tranche.enabled")) { log.info("shutting down the Tranche server"); TrancheAdmin.StopServer(); } } /** * Loads the main application properties file, given by {@link ECDB_APP_PROPERTIES_FILE}. * Note that core-api properties take precedence over application properties; * ideally there shouldn't be any property name collisions in the first place. */ protected void initConfig() { // need to call getConfiguration before adding our config so // that core-api config gets initialised and logged first. CompositeConfiguration config = Eurocarb.getConfiguration(); log.info("adding eurocarbdb application configuration: " + ECDB_APP_PROPERTIES_FILE ); try { config.addConfiguration( new PropertiesConfiguration( ECDB_APP_PROPERTIES_FILE ) ); } catch ( ConfigurationException ex ) { throw new RuntimeException( ex ); } if ( log.isInfoEnabled() ) { log.info( "Configured eurocarb-application properties:\n" + CR + ConfigurationUtils.toString( config ) ); } } } // end class