1.1 --- a/src/java/de/uapcore/lightpit/ModuleManager.java Sun Apr 08 16:51:15 2018 +0200 1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 1.3 @@ -1,231 +0,0 @@ 1.4 -/* 1.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 1.6 - * 1.7 - * Copyright 2018 Mike Becker. All rights reserved. 1.8 - * 1.9 - * Redistribution and use in source and binary forms, with or without 1.10 - * modification, are permitted provided that the following conditions are met: 1.11 - * 1.12 - * 1. Redistributions of source code must retain the above copyright 1.13 - * notice, this list of conditions and the following disclaimer. 1.14 - * 1.15 - * 2. Redistributions in binary form must reproduce the above copyright 1.16 - * notice, this list of conditions and the following disclaimer in the 1.17 - * documentation and/or other materials provided with the distribution. 1.18 - * 1.19 - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 1.20 - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1.21 - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1.22 - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 1.23 - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 1.24 - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 1.25 - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 1.26 - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 1.27 - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 1.28 - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 1.29 - * POSSIBILITY OF SUCH DAMAGE. 1.30 - * 1.31 - */ 1.32 -package de.uapcore.lightpit; 1.33 - 1.34 -import de.uapcore.lightpit.entities.CoreDAOFactory; 1.35 -import de.uapcore.lightpit.entities.Module; 1.36 -import de.uapcore.lightpit.entities.ModuleDao; 1.37 -import java.sql.Connection; 1.38 -import java.sql.SQLException; 1.39 -import java.util.Collections; 1.40 -import java.util.HashMap; 1.41 -import java.util.List; 1.42 -import java.util.Map; 1.43 -import java.util.Optional; 1.44 -import java.util.concurrent.atomic.AtomicBoolean; 1.45 -import java.util.stream.Collectors; 1.46 -import javax.servlet.Registration; 1.47 -import javax.servlet.ServletContext; 1.48 -import javax.servlet.ServletContextEvent; 1.49 -import javax.servlet.ServletContextListener; 1.50 -import javax.servlet.annotation.WebListener; 1.51 -import org.slf4j.Logger; 1.52 -import org.slf4j.LoggerFactory; 1.53 - 1.54 -/** 1.55 - * Scans registered servlets for LightPIT modules. 1.56 - */ 1.57 -@WebListener 1.58 -public final class ModuleManager implements ServletContextListener { 1.59 - 1.60 - private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class); 1.61 - 1.62 - /** 1.63 - * The attribute name in the servlet context under which an instance of this class can be found. 1.64 - */ 1.65 - public static final String SC_ATTR_NAME = ModuleManager.class.getName(); 1.66 - private ServletContext sc; 1.67 - 1.68 - /** 1.69 - * Maps class names to module information. 1.70 - */ 1.71 - private final Map<String, LightPITModule> registeredModules = new HashMap<>(); 1.72 - 1.73 - /** 1.74 - * This flag is true, when synchronization is needed. 1.75 - */ 1.76 - private final AtomicBoolean dirty = new AtomicBoolean(true); 1.77 - 1.78 - @Override 1.79 - public void contextInitialized(ServletContextEvent sce) { 1.80 - sc = sce.getServletContext(); 1.81 - reloadAll(); 1.82 - sc.setAttribute(SC_ATTR_NAME, this); 1.83 - LOG.info("Module manager injected into ServletContext."); 1.84 - } 1.85 - 1.86 - @Override 1.87 - public void contextDestroyed(ServletContextEvent sce) { 1.88 - unloadAll(); 1.89 - } 1.90 - 1.91 - private Optional<LightPITModule> getModuleInfo(Registration reg) { 1.92 - try { 1.93 - final Class scclass = Class.forName(reg.getClassName()); 1.94 - 1.95 - final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass); 1.96 - final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class); 1.97 - 1.98 - if (lpservlet && !lpmodule) { 1.99 - LOG.warn( 1.100 - "{} is a LightPIT Servlet but is missing the module annotation.", 1.101 - reg.getClassName() 1.102 - ); 1.103 - } else if (!lpservlet && lpmodule) { 1.104 - LOG.warn( 1.105 - "{} is annotated as a LightPIT Module but does not extend {}.", 1.106 - reg.getClassName(), 1.107 - AbstractLightPITServlet.class.getSimpleName() 1.108 - ); 1.109 - } 1.110 - 1.111 - if (lpservlet && lpmodule) { 1.112 - final Class<? extends AbstractLightPITServlet> clazz = scclass; 1.113 - final LightPITModule moduleInfo = clazz.getAnnotation(LightPITModule.class); 1.114 - return Optional.of(moduleInfo); 1.115 - } else { 1.116 - return Optional.empty(); 1.117 - } 1.118 - } catch (ClassNotFoundException ex) { 1.119 - LOG.error( 1.120 - "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})", 1.121 - reg.getClassName(), 1.122 - ex.getMessage() 1.123 - ); 1.124 - return Optional.empty(); 1.125 - } 1.126 - } 1.127 - 1.128 - private void handleServletRegistration(String name, Registration reg) { 1.129 - final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); 1.130 - if (moduleInfo.isPresent()) { 1.131 - registeredModules.put(reg.getClassName(), moduleInfo.get()); 1.132 - LOG.info("Module detected: {}", name); 1.133 - } else { 1.134 - LOG.debug("Servlet {} is no module, skipping.", name); 1.135 - } 1.136 - } 1.137 - 1.138 - /** 1.139 - * Scans for modules and reloads them all. 1.140 - */ 1.141 - public void reloadAll() { 1.142 - registeredModules.clear(); 1.143 - sc.getServletRegistrations().forEach(this::handleServletRegistration); 1.144 - 1.145 - // TODO: implement dependency resolver 1.146 - 1.147 - dirty.set(true); 1.148 - LOG.info("Modules loaded."); 1.149 - } 1.150 - 1.151 - /** 1.152 - * Synchronizes module information with the database. 1.153 - * 1.154 - * This must be called from the {@link AbstractLightPITServlet}. 1.155 - * Admittedly the call will perform the synchronization once after reload 1.156 - * and be a no-op, afterwards. 1.157 - * However, we since the DatabaseFacade might be loaded after the module 1.158 - * manager, we must defer the synchronization to the first request 1.159 - * handled by the Servlet. 1.160 - * 1.161 - * @param db interface to the database 1.162 - */ 1.163 - public void syncWithDatabase(DatabaseFacade db) { 1.164 - if (dirty.compareAndSet(true, false)) { 1.165 - if (db.getDataSource().isPresent()) { 1.166 - try (Connection conn = db.getDataSource().get().getConnection()) { 1.167 - final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); 1.168 - moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); 1.169 - } catch (SQLException ex) { 1.170 - LOG.error("Unexpected SQL Exception", ex); 1.171 - } 1.172 - } else { 1.173 - LOG.error("No datasource present. Cannot sync module information with database."); 1.174 - } 1.175 - } else { 1.176 - LOG.trace("Module information clean - no synchronization required."); 1.177 - } 1.178 - } 1.179 - 1.180 - /** 1.181 - * Unloads all found modules. 1.182 - */ 1.183 - public void unloadAll() { 1.184 - registeredModules.clear(); 1.185 - LOG.info("All modules unloaded."); 1.186 - } 1.187 - 1.188 - /** 1.189 - * Returns the main menu. 1.190 - * 1.191 - * @param db the interface to the database 1.192 - * @return a list of menus belonging to the main menu 1.193 - */ 1.194 - public List<Menu> getMainMenu(DatabaseFacade db) { 1.195 - // TODO: user specific menu 1.196 - 1.197 - if (db.getDataSource().isPresent()) { 1.198 - try (Connection conn = db.getDataSource().get().getConnection()) { 1.199 - final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); 1.200 - final List<Module> modules = dao.listAll(conn); 1.201 - 1.202 - final List<Menu> menu = modules 1.203 - .stream() 1.204 - .filter((mod) -> mod.isVisible()) 1.205 - .collect(Collectors.mapping( 1.206 - (mod) -> new Menu( 1.207 - mod.getClassname(), 1.208 - new ResourceKey( 1.209 - registeredModules.get(mod.getClassname()).bundleBaseName(), 1.210 - registeredModules.get(mod.getClassname()).menuKey()), 1.211 - registeredModules.get(mod.getClassname()).modulePath()), 1.212 - Collectors.toList()) 1.213 - ); 1.214 - return menu; 1.215 - } catch (SQLException ex) { 1.216 - LOG.error("Unexpected SQLException when loading the main menu", ex); 1.217 - return Collections.emptyList(); 1.218 - } 1.219 - } else { 1.220 - return Collections.emptyList(); 1.221 - } 1.222 - } 1.223 - 1.224 - /** 1.225 - * Returns an unmodifiable map of all registered modules. 1.226 - * 1.227 - * The key is the classname of the module. 1.228 - * 1.229 - * @return the map of registered modules 1.230 - */ 1.231 - public Map<String, LightPITModule> getRegisteredModules() { 1.232 - return Collections.unmodifiableMap(registeredModules); 1.233 - } 1.234 -}