diff --git a/Kitodo/src/main/java/org/kitodo/production/KitodoProduction.java b/Kitodo/src/main/java/org/kitodo/production/KitodoProduction.java new file mode 100644 index 00000000000..43ee9c3d9ae --- /dev/null +++ b/Kitodo/src/main/java/org/kitodo/production/KitodoProduction.java @@ -0,0 +1,157 @@ +/* + * (c) Kitodo. Key to digital objects e. V. + * + * This file is part of the Kitodo project. + * + * It is licensed under GNU General Public License version 3 or later. + * + * For the full copyright and license information, please read the + * GPL3-License.txt file that was distributed with this source code. + */ + +package org.kitodo.production; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Objects; +import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; +import java.util.jar.Manifest; + +import javax.servlet.ServletContext; +import javax.servlet.ServletContextEvent; +import javax.servlet.ServletContextListener; +import javax.servlet.annotation.WebListener; +import javax.servlet.http.HttpSessionEvent; +import javax.servlet.http.HttpSessionListener; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.kitodo.config.ConfigCore; +import org.kitodo.config.enums.ParameterCore; +import org.kitodo.production.helper.tasks.TaskManager; +import org.kitodo.production.interfaces.activemq.ActiveMQDirector; +import org.kitodo.production.security.SecurityUserDetails; +import org.kitodo.production.services.ServiceManager; +import org.springframework.security.core.context.SecurityContextImpl; + +/** + * Kitodo.Production is the workflow management module of the Kitodo suite. It + * supports the digitization process of various types of materials. + */ +@WebListener +public class KitodoProduction implements ServletContextListener, HttpSessionListener { + private static final Logger logger = LogManager.getLogger(KitodoProduction.class); + private static CompletableFuture instance = new CompletableFuture<>(); + private ServletContext context; + private Optional manifest; + private KitodoVersion version = new KitodoVersion(); + private ActiveMQDirector activeMQDirector; + + /* This is the main() function. The class is instantiated by the servlet + * container, which then calls this function. */ + @Override + public void contextInitialized(ServletContextEvent event) { + context = event.getServletContext(); + manifest = retrieveManifestFileAsStream(context); + manifest.ifPresent(version::setupFromManifest); + instance.complete(this); + startActiveMQ(); + } + + private static final Optional retrieveManifestFileAsStream(ServletContext context) { + try (InputStream manifestResource = context.getResourceAsStream("/META-INF/MANIFEST.MF")) { + if (Objects.nonNull(manifestResource)) { + return Optional.of(new Manifest(manifestResource)); + } + } catch (IOException e) { + logger.error(e.getMessage(), e); + context.log(e.getMessage()); + } + return Optional.empty(); + } + + /** + * Start up the active MQ connection. + */ + private void startActiveMQ() { + if (ConfigCore.getOptionalString(ParameterCore.ACTIVE_MQ_HOST_URL).isPresent()) { + activeMQDirector = new ActiveMQDirector(); + Thread connectAsynchronously = new Thread(activeMQDirector); + connectAsynchronously.setName(ActiveMQDirector.class.getSimpleName()); + connectAsynchronously.setDaemon(true); + connectAsynchronously.start(); + } + } + + /** + * Returns the application’s main class. + * + * @return the main class + * @throws UndeclaredThrowableException + * if the servlet container is shut down before the web + * application is started + */ + public static KitodoProduction getInstance() { + try { + return instance.get(); + } catch (InterruptedException | ExecutionException e) { + logger.fatal(e); + throw new UndeclaredThrowableException(e); + } + } + + /** + * Returns the servlet context. + * + * @return the servlet context + */ + public ServletContext getServletContext() { + return context; + } + + /** + * Returns the manifest. If there is one. Otherwise, it can also be empty. + * + * @return the manifest + */ + public Optional getManifest() { + return manifest; + } + + /** + * Returns application version information. + * + * @return version information + */ + public KitodoVersion getVersionInformation() { + return version; + } + + @Override + public void contextDestroyed(ServletContextEvent sce) { + TaskManager.shutdownNow(); + if (Objects.nonNull(activeMQDirector)) { + activeMQDirector.shutDown(); + } + } + + @Override + public void sessionCreated(HttpSessionEvent se) { + // nothing is done here + } + + @Override + public void sessionDestroyed(HttpSessionEvent se) { + Object securityContextObject = se.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); + if (securityContextObject instanceof SecurityContextImpl) { + SecurityContextImpl securityContext = (SecurityContextImpl) securityContextObject; + Object principal = securityContext.getAuthentication().getPrincipal(); + if (principal instanceof SecurityUserDetails) { + ServiceManager.getSessionService().expireSessionsOfUser((SecurityUserDetails) principal); + } + } + } +} diff --git a/Kitodo/src/main/java/org/kitodo/production/version/KitodoVersion.java b/Kitodo/src/main/java/org/kitodo/production/KitodoVersion.java similarity index 68% rename from Kitodo/src/main/java/org/kitodo/production/version/KitodoVersion.java rename to Kitodo/src/main/java/org/kitodo/production/KitodoVersion.java index 39e897756ce..d64a81520fe 100644 --- a/Kitodo/src/main/java/org/kitodo/production/version/KitodoVersion.java +++ b/Kitodo/src/main/java/org/kitodo/production/KitodoVersion.java @@ -9,7 +9,7 @@ * GPL3-License.txt file that was distributed with this source code. */ -package org.kitodo.production.version; +package org.kitodo.production; import java.util.jar.Attributes; import java.util.jar.Manifest; @@ -17,30 +17,21 @@ public class KitodoVersion { private static String version = "N/A"; - private static String buildVersion = "N/A"; private static String buildDate = "N/A"; - /** - * Private constructor to hide the implicit public one. - */ - private KitodoVersion() { - - } - /** * Setup KitodoVersion form manifest. * * @param manifest as Manifest */ - public static void setupFromManifest(Manifest manifest) { + void setupFromManifest(Manifest manifest) { Attributes mainAttributes = manifest.getMainAttributes(); version = getValueOrThrowException(mainAttributes, "Implementation-Version"); - buildVersion = version; buildDate = getValueOrThrowException(mainAttributes, "Implementation-Build-Date"); } - private static String getValueOrThrowException(Attributes attributes, String attributeName) { + private String getValueOrThrowException(Attributes attributes, String attributeName) { String value = attributes.getValue(attributeName); if (null == value) { throw new IllegalArgumentException( @@ -49,15 +40,11 @@ private static String getValueOrThrowException(Attributes attributes, String att return value; } - public static String getVersion() { + public String getVersion() { return version; } - public static String getBuildVersion() { - return buildVersion; - } - - public static String getBuildDate() { + public String getBuildDate() { return buildDate; } } diff --git a/Kitodo/src/main/java/org/kitodo/production/forms/HelperForm.java b/Kitodo/src/main/java/org/kitodo/production/forms/HelperForm.java index 4f058288ea6..39d6d6b6026 100644 --- a/Kitodo/src/main/java/org/kitodo/production/forms/HelperForm.java +++ b/Kitodo/src/main/java/org/kitodo/production/forms/HelperForm.java @@ -18,8 +18,8 @@ import org.kitodo.config.ConfigCore; import org.kitodo.config.enums.ParameterCore; +import org.kitodo.production.KitodoProduction; import org.kitodo.production.helper.Helper; -import org.kitodo.production.version.KitodoVersion; /** * Helper form - used for some single methods which don't match yet to other @@ -30,7 +30,7 @@ public class HelperForm implements Serializable { public String getVersion() { - return KitodoVersion.getBuildVersion(); + return KitodoProduction.getInstance().getVersionInformation().getVersion(); } public boolean getAnonymized() { diff --git a/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskManager.java b/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskManager.java index 947a52b273e..2790c1a84a5 100644 --- a/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskManager.java +++ b/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskManager.java @@ -223,7 +223,7 @@ static synchronized TaskManager singleton() { * exit the task manager as well as its managed threads during container * shutdown. */ - static void shutdownNow() { + public static void shutdownNow() { stopAndDeleteAllTasks(); singleton().taskSitter.shutdownNow(); } diff --git a/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskSitter.java b/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskSitter.java index 0aab4f25493..d7221fa0e0a 100644 --- a/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskSitter.java +++ b/Kitodo/src/main/java/org/kitodo/production/helper/tasks/TaskSitter.java @@ -41,8 +41,7 @@ * constructor is private) a caring class is needed which will be available for * instantiation to the servlet container. */ -@WebListener -public class TaskSitter implements Runnable, ServletContextListener { +public class TaskSitter implements Runnable { /** * The field autoRunLimit holds the number of threads which at most are * allowed to be started automatically. It is by default initialised by the @@ -55,27 +54,6 @@ public class TaskSitter implements Runnable, ServletContextListener { setAutoRunningThreads(true); } - /** - * When the servlet is unloaded, i.e. on container shutdown, the TaskManager - * shall be shut down gracefully. - * - * @see javax.servlet.ServletContextListener#contextDestroyed(javax.servlet.ServletContextEvent) - */ - @Override - public void contextDestroyed(ServletContextEvent arg) { - TaskManager.shutdownNow(); - } - - /** - * Currently, there is nothing to do when the servlet is loading. - * - * @see javax.servlet.ServletContextListener#contextInitialized(javax.servlet.ServletContextEvent) - */ - @Override - public void contextInitialized(ServletContextEvent argument) { - // nothing is done here - } - /** * Returns whether the TaskManager’s * autorun mode is on or not. diff --git a/Kitodo/src/main/java/org/kitodo/production/interfaces/activemq/ActiveMQDirector.java b/Kitodo/src/main/java/org/kitodo/production/interfaces/activemq/ActiveMQDirector.java index 73a2a316b61..93ae0c4399a 100644 --- a/Kitodo/src/main/java/org/kitodo/production/interfaces/activemq/ActiveMQDirector.java +++ b/Kitodo/src/main/java/org/kitodo/production/interfaces/activemq/ActiveMQDirector.java @@ -49,8 +49,7 @@ * The class ActiveMQDirector also provides a basic ExceptionListener * implementation as required for the connection. */ -@WebListener -public class ActiveMQDirector implements Runnable, ServletContextListener { +public class ActiveMQDirector implements Runnable { private static final Logger logger = LogManager.getLogger(ActiveMQDirector.class); // When implementing new services, add them to this list @@ -64,21 +63,6 @@ public class ActiveMQDirector implements Runnable, ServletContextListener { private static Session session = null; private static MessageProducer resultsTopic; - /** - * The method is called by the web container on startup - * and is used to start up the active MQ connection. All processors from - * {@link #services} are registered. - */ - @Override - public void contextInitialized(ServletContextEvent initialisation) { - if (ConfigCore.getOptionalString(ParameterCore.ACTIVE_MQ_HOST_URL).isPresent()) { - Thread connectAsynchronously = new Thread(new ActiveMQDirector()); - connectAsynchronously.setName(ActiveMQDirector.class.getSimpleName()); - connectAsynchronously.setDaemon(true); - connectAsynchronously.start(); - } - } - @Override public void run() { String activeMQHost = ConfigCore.getParameter(ParameterCore.ACTIVE_MQ_HOST_URL); @@ -226,8 +210,7 @@ public static MessageProducer getResultsTopic() { * The method contextDestroyed is called by the web container on shutdown. * It shuts down all listeners, the session and last, the connection. */ - @Override - public void contextDestroyed(ServletContextEvent destruction) { + public void shutDown() { // Shut down all message consumers on any queues for (ActiveMQProcessor service : services) { MessageConsumer messageConsumer = service.getMessageConsumer(); diff --git a/Kitodo/src/main/java/org/kitodo/production/version/KitodoVersionListener.java b/Kitodo/src/main/java/org/kitodo/production/version/KitodoVersionListener.java deleted file mode 100644 index 25174daca80..00000000000 --- a/Kitodo/src/main/java/org/kitodo/production/version/KitodoVersionListener.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * (c) Kitodo. Key to digital objects e. V. - * - * This file is part of the Kitodo project. - * - * It is licensed under GNU General Public License version 3 or later. - * - * For the full copyright and license information, please read the - * GPL3-License.txt file that was distributed with this source code. - */ - -package org.kitodo.production.version; - -import java.io.IOException; -import java.io.InputStream; -import java.util.Objects; -import java.util.jar.Manifest; - -import javax.servlet.ServletContext; -import javax.servlet.ServletContextEvent; -import javax.servlet.ServletContextListener; -import javax.servlet.annotation.WebListener; -import javax.servlet.http.HttpSessionAttributeListener; -import javax.servlet.http.HttpSessionBindingEvent; -import javax.servlet.http.HttpSessionEvent; -import javax.servlet.http.HttpSessionListener; - -import org.kitodo.production.security.SecurityUserDetails; -import org.kitodo.production.services.ServiceManager; -import org.springframework.security.core.context.SecurityContextImpl; - -/** - * Listener to set up Kitodo versioning information from Manifest on application - * startup. - */ -@WebListener -public class KitodoVersionListener implements ServletContextListener, HttpSessionListener { - - @Override - public void contextInitialized(ServletContextEvent sce) { - // Retrieve Manifest file as Stream - ServletContext context = sce.getServletContext(); - InputStream rs = context.getResourceAsStream("/META-INF/MANIFEST.MF"); - // Use Manifest to setup version information - if (Objects.nonNull(rs)) { - try { - Manifest m = new Manifest(rs); - KitodoVersion.setupFromManifest(m); - } catch (IOException e) { - context.log(e.getMessage()); - } - } - } - - @Override - public void contextDestroyed(ServletContextEvent sce) { - // nothing is done here - } - - @Override - public void sessionCreated(HttpSessionEvent se) { - // nothing is done here - } - - @Override - public void sessionDestroyed(HttpSessionEvent se) { - Object securityContextObject = se.getSession().getAttribute("SPRING_SECURITY_CONTEXT"); - if (securityContextObject instanceof SecurityContextImpl) { - SecurityContextImpl securityContext = (SecurityContextImpl) securityContextObject; - Object principal = securityContext.getAuthentication().getPrincipal(); - if (principal instanceof SecurityUserDetails) { - ServiceManager.getSessionService().expireSessionsOfUser((SecurityUserDetails) principal); - } - } - } -} diff --git a/Kitodo/src/test/java/org/kitodo/production/version/KitodoVersionTest.java b/Kitodo/src/test/java/org/kitodo/production/KitodoVersionTest.java similarity index 59% rename from Kitodo/src/test/java/org/kitodo/production/version/KitodoVersionTest.java rename to Kitodo/src/test/java/org/kitodo/production/KitodoVersionTest.java index dde8224f736..be6af91214d 100644 --- a/Kitodo/src/test/java/org/kitodo/production/version/KitodoVersionTest.java +++ b/Kitodo/src/test/java/org/kitodo/production/KitodoVersionTest.java @@ -9,7 +9,7 @@ * GPL3-License.txt file that was distributed with this source code. */ -package org.kitodo.production.version; +package org.kitodo.production; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -25,31 +25,27 @@ public class KitodoVersionTest { @Test public void shouldThrowExceptionIfKitodoSectionIsMissingInManifest() { - assertThrows(IllegalArgumentException.class, () -> KitodoVersion.setupFromManifest(new Manifest())); + assertThrows(IllegalArgumentException.class, () -> new KitodoVersion().setupFromManifest(new Manifest())); } @Test public void attributeVersionShouldBeEqualToImplementationVersion() { Manifest manifest = createManifestWithValues(); - KitodoVersion.setupFromManifest(manifest); + KitodoVersion version = new KitodoVersion(); + version.setupFromManifest(manifest); - assertEquals(VERSION, KitodoVersion.getVersion(), "Version attribute should be equal to Implementation-Version as specified in the given Manifest."); - } - - @Test - public void attributeBuildVersionShouldBeEqualToImplementationVersion() { - Manifest manifest = createManifestWithValues(); - KitodoVersion.setupFromManifest(manifest); - - assertEquals(VERSION, KitodoVersion.getBuildVersion(), "BuildVersion attribute should be equal to Implementation-Version as specified in the given Manifest."); + assertEquals(VERSION, version.getVersion(), + "Version attribute should be equal to Implementation-Version as specified in the given Manifest."); } @Test public void attributeBuildDateShouldBeEqualToImplementationBuildDate() { Manifest manifest = createManifestWithValues(); - KitodoVersion.setupFromManifest(manifest); + KitodoVersion version = new KitodoVersion(); + version.setupFromManifest(manifest); - assertEquals(BUILD_DATE, KitodoVersion.getBuildDate(), "BuildDate attribute should be equal to Implementation-Build-Date as specified in the given Manifest."); + assertEquals(BUILD_DATE, version.getBuildDate(), + "BuildDate attribute should be equal to Implementation-Build-Date as specified in the given Manifest."); } private Manifest createManifestWithValues() {