/** * Copyright (c) 2009 - 2012 Red Hat, Inc. * * This software is licensed to you under the GNU General Public License, * version 2 (GPLv2). There is NO WARRANTY for this software, express or * implied, including the implied warranties of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2 * along with this software; if not, see * http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. * * Red Hat trademarks are not licensed under GPLv2. No permission is * granted to use or replicate Red Hat trademarks that are incorporated * in this software or its documentation. */ package org.candlepin.guice; import static org.candlepin.config.ConfigProperties.ENCRYPTED_PROPERTIES; import static org.candlepin.config.ConfigProperties.HORNETQ_ENABLED; import static org.candlepin.config.ConfigProperties.PASSPHRASE_SECRET_FILE; import org.candlepin.audit.AMQPBusPublisher; import org.candlepin.audit.HornetqContextListener; import org.candlepin.audit.QpidQmf; import org.candlepin.audit.QpidQmf.QpidStatus; import org.candlepin.common.config.Configuration; import org.candlepin.common.config.ConfigurationException; import org.candlepin.common.config.EncryptedConfiguration; import org.candlepin.common.config.MapConfiguration; import org.candlepin.common.logging.LoggingConfigurator; import org.candlepin.config.ConfigProperties; import org.candlepin.config.DatabaseConfigFactory; import org.candlepin.controller.SuspendModeTransitioner; import org.candlepin.logging.LoggerContextListener; import org.candlepin.pinsetter.core.PinsetterContextListener; import org.candlepin.resteasy.ResourceLocatorMap; import org.candlepin.swagger.CandlepinSwaggerModelConverter; import org.candlepin.util.Util; import com.google.inject.AbstractModule; import com.google.inject.Injector; import com.google.inject.Module; import com.google.inject.Stage; import com.google.inject.util.Modules; import org.apache.commons.lang.StringUtils; import org.hibernate.cfg.beanvalidation.BeanValidationEventListener; import org.hibernate.dialect.PostgreSQLDialect; import org.hibernate.event.service.spi.EventListenerRegistry; import org.hibernate.event.spi.EventType; import org.hibernate.internal.SessionFactoryImpl; import org.hibernate.jpa.HibernateEntityManagerFactory; import org.jboss.resteasy.plugins.guice.GuiceResteasyBootstrapServletContextListener; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.xnap.commons.i18n.I18nManager; import net.sf.ehcache.CacheManager; import net.sf.ehcache.management.ManagementService; import java.io.File; import java.lang.management.ManagementFactory; import java.nio.charset.Charset; import java.util.LinkedList; import java.util.List; import java.util.Locale; import javax.management.MBeanServer; import javax.persistence.EntityManagerFactory; import javax.servlet.ServletContext; import javax.servlet.ServletContextEvent; import io.swagger.converter.ModelConverters; /** * Customized Candlepin version of * {@link GuiceResteasyBootstrapServletContextListener}. * * The base version pulls in Guice modules by class name from web.xml and * instantiates them - however we have a need to add in modules * programmatically for, e.g., servlet filters and the wideplay JPA module. * This context listener overrides some of the module initialization code to * allow for module specification beyond simply listing class names. */ public class CandlepinContextListener extends GuiceResteasyBootstrapServletContextListener { public static final String CONFIGURATION_NAME = Configuration.class.getName(); private HornetqContextListener hornetqListener; private PinsetterContextListener pinsetterListener; private LoggerContextListener loggerListener; // a bit of application-initialization code. Not sure if this is the // best spot for it. static { I18nManager.getInstance().setDefaultLocale(Locale.US); } private static Logger log = LoggerFactory.getLogger(CandlepinContextListener.class); private Configuration config; // getServletContext() from the GuiceServletContextListener is deprecated. // See // https://github.com/google/guice/blob/bf0e7ce902dd97e62ef16679c587d78d59200450 // /extensions/servlet/src/com/google/inject/servlet/GuiceServletContextListener.java#L43-L45 // A typical way of doing this then is to cache the context ourselves: // https://github.com/google/guice/issues/603 // Currently only needed for access to the Configuration. private ServletContext servletContext; private Injector injector; @Override public void contextInitialized(ServletContextEvent sce) { log.info("Candlepin initializing context."); I18nManager.getInstance().setDefaultLocale(Locale.US); servletContext = sce.getServletContext(); try { log.info("Candlepin reading configuration."); config = readConfiguration(servletContext); } catch (ConfigurationException e) { log.error("Could not read configuration file. Aborting initialization.", e); throw new RuntimeException(e); } LoggingConfigurator.init(config); servletContext.setAttribute(CONFIGURATION_NAME, config); log.debug("Candlepin stored config on context."); // set things up BEFORE calling the super class' initialize method. super.contextInitialized(sce); log.info("Candlepin context initialized."); } @Override public void withInjector(Injector injector) { // Must call super.contextInitialized() before accessing injector insertValidationEventListeners(injector); ResourceLocatorMap map = injector.getInstance(ResourceLocatorMap.class); map.init(); if (config.getBoolean(ConfigProperties.AMQP_INTEGRATION_ENABLED) && !config.getBoolean(ConfigProperties.SUSPEND_MODE_ENABLED)) { QpidQmf qmf = injector.getInstance(QpidQmf.class); QpidStatus status = qmf.getStatus(); if (status != QpidStatus.CONNECTED) { log.error("Qpid is in status {}. Please fix Qpid configuration " + "and make sure Qpid is up and running. Candlepin will shutdown " + "now.", status); throw new RuntimeException("Error during Startup: Qpid is in status " + status); } } if (config.getBoolean(ConfigProperties.AMQP_INTEGRATION_ENABLED) && config.getBoolean(ConfigProperties.SUSPEND_MODE_ENABLED)) { SuspendModeTransitioner mw = injector.getInstance(SuspendModeTransitioner.class); mw.transitionAppropriately(); mw.startPeriodicExecutions(); } if (config.getBoolean(HORNETQ_ENABLED)) { try { hornetqListener = injector.getInstance(HornetqContextListener.class); hornetqListener.contextInitialized(injector); } catch (Exception e) { log.error("Exception occurred while trying to load HornetQ", e); } } if (config.getBoolean(ConfigProperties.CACHE_JMX_STATS)) { MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer(); ManagementService.registerMBeans(CacheManager.getInstance(), mBeanServer, true, true, true, true); } pinsetterListener = injector.getInstance(PinsetterContextListener.class); pinsetterListener.contextInitialized(); loggerListener = injector.getInstance(LoggerContextListener.class); /** * Custom ModelConverter to handle our specific serialization requirements */ ModelConverters.getInstance() .addConverter(injector.getInstance(CandlepinSwaggerModelConverter.class)); this.injector = injector; } @Override public void contextDestroyed(ServletContextEvent event) { super.contextDestroyed(event); if (config.getBoolean(HORNETQ_ENABLED)) { hornetqListener.contextDestroyed(); } pinsetterListener.contextDestroyed(); loggerListener.contextDestroyed(); // if amqp is enabled, close all connections. if (config.getBoolean(ConfigProperties.AMQP_INTEGRATION_ENABLED)) { Util.closeSafely(injector.getInstance(AMQPBusPublisher.class), "AMQPBusPublisher"); } } protected Configuration readConfiguration(ServletContext context) throws ConfigurationException { // Use StandardCharsets.UTF_8 when we move to Java 7 Charset utf8 = Charset.forName("UTF-8"); EncryptedConfiguration systemConfig = new EncryptedConfiguration(); systemConfig.setEncoding(utf8); File configFile = new File(ConfigProperties.DEFAULT_CONFIG_FILE); if (configFile.canRead()) { log.debug("Loading system configuration"); // First, read the system configuration systemConfig.load(configFile); log.debug("System configuration: " + systemConfig); } systemConfig.use(PASSPHRASE_SECRET_FILE).toDecrypt(ENCRYPTED_PROPERTIES); // load the defaults MapConfiguration defaults = new MapConfiguration(ConfigProperties.DEFAULT_PROPERTIES); // Default to Postgresql if jpa.config.hibernate.dialect is unset DatabaseConfigFactory.SupportedDatabase db = determinDatabaseConfiguration(systemConfig.getString ("jpa.config.hibernate.dialect", PostgreSQLDialect.class.getName())); log.info("Running under {}", db.getLabel()); Configuration databaseConfig = DatabaseConfigFactory.fetchConfig(db); // merge the defaults with the system configuration. PARAMETER ORDER MATTERS. // systemConfig must be FIRST to override subsequent configs. This set up allows // the systemConfig to override databaseConfig if necessary, but that's not really something // users should be doing unbidden so it is undocumented. return EncryptedConfiguration.merge(systemConfig, databaseConfig, defaults); } private DatabaseConfigFactory.SupportedDatabase determinDatabaseConfiguration(String dialect) { if (StringUtils.containsIgnoreCase( dialect, DatabaseConfigFactory.SupportedDatabase.MYSQL.getLabel())) { return DatabaseConfigFactory.SupportedDatabase.MYSQL; } else if (StringUtils.containsIgnoreCase( dialect, DatabaseConfigFactory.SupportedDatabase.ORACLE.getLabel())) { return DatabaseConfigFactory.SupportedDatabase.ORACLE; } return DatabaseConfigFactory.SupportedDatabase.POSTGRESQL; } @Override protected Stage getStage(ServletContext context) { return Stage.PRODUCTION; } /** * Returns a list of Guice modules to initialize. * @return a list of Guice modules to initialize. */ @Override protected List<Module> getModules(ServletContext context) { List<Module> modules = new LinkedList<Module>(); modules.add(Modules.override(new DefaultConfig()).with(new CustomizableModules().load(config))); modules.add(new AbstractModule() { @Override protected void configure() { bind(Configuration.class).toInstance(config); } }); modules.add(new CandlepinModule(config)); modules.add(new CandlepinFilterModule(config)); return modules; } /** * There's no way to really get Guice to perform injections on stuff that * the JpaPersistModule is creating, so we resort to grabbing the EntityManagerFactory * after the fact and adding the Validation EventListener ourselves. * @param injector */ private void insertValidationEventListeners(Injector injector) { javax.inject.Provider<EntityManagerFactory> emfProvider = injector.getProvider(EntityManagerFactory.class); HibernateEntityManagerFactory hibernateEntityManagerFactory = (HibernateEntityManagerFactory) emfProvider.get(); SessionFactoryImpl sessionFactoryImpl = (SessionFactoryImpl) hibernateEntityManagerFactory.getSessionFactory(); EventListenerRegistry registry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class); javax.inject.Provider<BeanValidationEventListener> listenerProvider = injector.getProvider(BeanValidationEventListener.class); registry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(listenerProvider.get()); registry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(listenerProvider.get()); registry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(listenerProvider.get()); } }