/* * Copyright 2012-2013, CMM, University of Queensland. * * This file is part of Paul. * * Paul is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Paul 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Paul. If not, see <http://www.gnu.org/licenses/>. */ package au.edu.uq.cmm.paul; import java.io.IOException; import java.util.Objects; import javax.persistence.EntityManagerFactory; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.context.Lifecycle; import au.edu.uq.cmm.aclslib.message.AclsClient; import au.edu.uq.cmm.aclslib.proxy.AclsHelper; import au.edu.uq.cmm.aclslib.service.Service; import au.edu.uq.cmm.aclslib.service.ServiceBase; import au.edu.uq.cmm.aclslib.service.ServiceException; import au.edu.uq.cmm.eccles.EcclesUserDetailsManager; import au.edu.uq.cmm.eccles.ProxyConfiguration; import au.edu.uq.cmm.eccles.StaticEcclesProxyConfiguration; import au.edu.uq.cmm.eccles.UserDetailsManager; import au.edu.uq.cmm.paul.queue.AtomFeed; import au.edu.uq.cmm.paul.queue.QueueExpirer; import au.edu.uq.cmm.paul.queue.QueueManager; import au.edu.uq.cmm.paul.servlet.ConfigurationManager; import au.edu.uq.cmm.paul.servlet.Status; import au.edu.uq.cmm.paul.status.FacilityStatusManager; import au.edu.uq.cmm.paul.watcher.FileWatcher; import au.edu.uq.cmm.paul.watcher.SambaUncPathnameMapper; import au.edu.uq.cmm.paul.watcher.UncPathnameMapper; /** * This class represents the Paul application as a whole, with getters that * allow the different components to access each other. The class also * implements the ACSLib "Service" API. * * @author scrawley */ public class Paul extends ServiceBase implements Lifecycle { // FIXME - need to do something to hook this class into the servlet lifecycle // so that it gets told when the servlet is being shutdown. private static final Logger LOG = LoggerFactory.getLogger(Paul.class); private final FacilityStatusManager statusManager; private final UncPathnameMapper uncNameMapper; private final EntityManagerFactory entityManagerFactory; private final QueueManager queueManager; private final QueueExpirer queueExpirer; private final FileWatcher fileWatcher; private final UserDetailsManager userDetailsManager; private final ConfigurationManager configManager; private final PaulFacilityMapper facilityMapper; private final AclsHelper aclsHelper; private final AtomFeed atomFeed; private final PaulControl control; public Paul(StaticPaulConfiguration staticConfig, StaticEcclesProxyConfiguration staticProxyConfig, StaticPaulFacilities staticFacilities, EntityManagerFactory entityManagerFactory) throws IOException { this(staticConfig, staticProxyConfig, staticFacilities, entityManagerFactory, new SambaUncPathnameMapper()); } public Paul(StaticPaulConfiguration staticConfig, StaticEcclesProxyConfiguration staticProxyConfig, StaticPaulFacilities staticFacilities, EntityManagerFactory entityManagerFactory, UncPathnameMapper uncNameMapper) throws IOException { this.entityManagerFactory = entityManagerFactory; this.uncNameMapper = uncNameMapper; this.configManager = new ConfigurationManager(this, entityManagerFactory, staticConfig, staticProxyConfig, staticFacilities); this.facilityMapper = new PaulFacilityMapper(entityManagerFactory); ProxyConfiguration proxyConfig = configManager.getActiveProxyConfig(); this.aclsHelper = new AclsHelper( proxyConfig.getProxyHost(), proxyConfig.getProxyPort(), /* * Use double the default timeout, because the proxy potentially * has to timeout the downstream ACLS server. (Hack) */ AclsClient.ACLS_REQUEST_TIMEOUT * 2, false); this.userDetailsManager = new EcclesUserDetailsManager(entityManagerFactory, proxyConfig.getFallbackMode()); this.statusManager = new FacilityStatusManager(this); this.fileWatcher = new FileWatcher(this); this.queueManager = new QueueManager(this); this.queueExpirer = new QueueExpirer(this); this.control = PaulControl.load(entityManagerFactory); this.atomFeed = new AtomFeed(control); setupDefaultUncaughtExceptionHandler(); } private void setupDefaultUncaughtExceptionHandler() { if (Thread.getDefaultUncaughtExceptionHandler() == null) { LOG.info("Setting the uncaught exception handler"); } else { LOG.info("Changing the uncaught exception handler"); } Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { LOG.error("Thread " + t.getName() + " died with an uncaught exception", e); } }); } @Override protected void doShutdown() throws InterruptedException { LOG.info("Shutdown started"); queueExpirer.shutdown(); fileWatcher.shutdown(); LOG.info("Shutdown completed"); } @Override protected void doStartup() throws ServiceException { LOG.info("Startup started"); if (control.isFileWatcherEnabled()) { fileWatcher.startup(); } queueExpirer.startup(); LOG.info("Startup completed"); } public FacilityStatusManager getFacilityStatusManager() { return statusManager; } public PaulConfiguration getConfiguration() { return Objects.requireNonNull(configManager.getActiveConfig()); } public QueueManager getQueueManager() { return queueManager; } public ConfigurationManager getConfigManager() { return configManager; } public FileWatcher getFileWatcher() { return fileWatcher; } public EntityManagerFactory getEntityManagerFactory() { return entityManagerFactory; } public UncPathnameMapper getUncNameMapper() { return uncNameMapper; } public UserDetailsManager getUserDetailsManager() { return userDetailsManager; } public PaulFacilityMapper getFacilityMapper() { return facilityMapper; } public AclsHelper getAclsHelper() { return aclsHelper; } public AtomFeed getAtomFeed() { return atomFeed; } public final PaulControl getControl() { return control; } @Override public void start() { startup(); } @Override public void stop() { try { shutdown(); } catch (InterruptedException ex) { LOG.warn("Shutdown interrupted", ex); } } @Override public boolean isRunning() { return getState() == State.STARTED; } public void processStatusChange(String serviceName, String param) { Service service; switch (serviceName) { case "atomFeed": service = atomFeed; break; case "watcher": service = fileWatcher; break; default: throw new PaulException("unknown service: " + serviceName); } Service.State current = service.getState(); if (param == null) { return; } Status target = Status.valueOf(param); if (target == Status.forState(current) || Status.forState(current) == Status.TRANSITIONAL) { return; } if (target == Status.ON) { service.startStartup(); } else { service.startShutdown(); } switch (serviceName) { case "atomFeed": control.setAtomFeedEnabled(target == Status.ON); break; case "watcher": control.setFileWatcherEnabled(target == Status.ON); break; } PaulControl.save(control, entityManagerFactory); } }