/*
* 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);
}
}