src/main/java/de/uapcore/lightpit/ModuleManager.java

Sat, 09 May 2020 14:37:15 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 09 May 2020 14:37:15 +0200
changeset 30
fb30f7b78f9b
parent 29
27a0fdd7bca7
child 31
58f78f0142e8
permissions
-rw-r--r--

moves DAO classes to different package

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  * 
     4  * Copyright 2018 Mike Becker. All rights reserved.
     5  * 
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  * 
    28  */
    29 package de.uapcore.lightpit;
    31 import de.uapcore.lightpit.dao.CoreDAOFactory;
    32 import de.uapcore.lightpit.dao.ModuleDao;
    33 import de.uapcore.lightpit.entities.Module;
    34 import org.slf4j.Logger;
    35 import org.slf4j.LoggerFactory;
    37 import javax.servlet.Registration;
    38 import javax.servlet.ServletContext;
    39 import javax.servlet.ServletContextEvent;
    40 import javax.servlet.ServletContextListener;
    41 import javax.servlet.annotation.WebListener;
    42 import java.sql.Connection;
    43 import java.sql.SQLException;
    44 import java.util.*;
    45 import java.util.concurrent.atomic.AtomicBoolean;
    46 import java.util.stream.Collectors;
    48 /**
    49  * Scans registered servlets for LightPIT modules.
    50  */
    51 @WebListener
    52 public final class ModuleManager implements ServletContextListener {
    54     private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class);
    56     /**
    57      * The attribute name in the servlet context under which an instance of this class can be found.
    58      */
    59     public static final String SC_ATTR_NAME = ModuleManager.class.getName();
    60     private ServletContext sc;
    62     /**
    63      * Maps class names to module information.
    64      */
    65     private final Map<String, LightPITModule> registeredModules = new HashMap<>();
    67     /**
    68      * This flag is true, when synchronization is needed.
    69      */
    70     private final AtomicBoolean dirty = new AtomicBoolean(true);
    72     @Override
    73     public void contextInitialized(ServletContextEvent sce) {
    74         sc = sce.getServletContext();
    75         reloadAll();
    76         sc.setAttribute(SC_ATTR_NAME, this);
    77         LOG.info("Module manager injected into ServletContext.");
    78     }
    80     @Override
    81     public void contextDestroyed(ServletContextEvent sce) {
    82         unloadAll();
    83     }
    85     private Optional<LightPITModule> getModuleInfo(Registration reg) {
    86         try {
    87             final Class scclass = Class.forName(reg.getClassName());
    89             final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass);
    90             final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class);
    92             if (lpservlet && !lpmodule) {
    93                 LOG.warn(
    94                     "{} is a LightPIT Servlet but is missing the module annotation.",
    95                     reg.getClassName()
    96                 );
    97             } else if (!lpservlet && lpmodule) {
    98                 LOG.warn(
    99                     "{} is annotated as a LightPIT Module but does not extend {}.",
   100                     reg.getClassName(),
   101                     AbstractLightPITServlet.class.getSimpleName()
   102                 );
   103             }
   105             if (lpservlet && lpmodule) {
   106                 final Class<? extends AbstractLightPITServlet> clazz = scclass;
   107                 final LightPITModule moduleInfo = clazz.getAnnotation(LightPITModule.class);
   108                 return Optional.of(moduleInfo);
   109             } else {
   110                 return Optional.empty();
   111             }
   112         } catch (ClassNotFoundException ex) {
   113             LOG.error(
   114                     "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})",
   115                     reg.getClassName(),
   116                     ex.getMessage()
   117             );
   118             return Optional.empty();
   119         }        
   120     }
   122     private void handleServletRegistration(String name, Registration reg) {
   123         final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
   124         if (moduleInfo.isPresent()) {
   125             registeredModules.put(reg.getClassName(), moduleInfo.get());            
   126             LOG.info("Module detected: {}", name);
   127         } else {
   128             LOG.debug("Servlet {} is no module, skipping.", name);
   129         }
   130     }
   132     /**
   133      * Scans for modules and reloads them all.
   134      */
   135     public void reloadAll() {
   136         registeredModules.clear();
   137         sc.getServletRegistrations().forEach(this::handleServletRegistration);
   139         // TODO: implement dependency resolver
   141         dirty.set(true);
   142         LOG.info("Modules loaded.");
   143     }
   145     /**
   146      * Synchronizes module information with the database.
   147      * <p>
   148      * This must be called from the {@link AbstractLightPITServlet}.
   149      * Admittedly the call will perform the synchronization once after reload
   150      * and be a no-op, afterwards.
   151      * However, since the DatabaseFacade might be loaded after the module
   152      * manager, we must defer the synchronization to the first request
   153      * handled by the Servlet.
   154      *
   155      * @param db interface to the database
   156      */
   157     public void syncWithDatabase(DatabaseFacade db) {
   158         if (dirty.compareAndSet(true, false)) {
   159             if (db.getDataSource().isPresent()) {
   160                 try (Connection conn = db.getDataSource().get().getConnection()) {
   161                     final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   162                     moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet());
   163                 } catch (SQLException ex) {
   164                     LOG.error("Unexpected SQL Exception", ex);
   165                 }
   166             } else {
   167                 LOG.error("No datasource present. Cannot sync module information with database.");
   168             }
   169         } else {
   170             LOG.trace("Module information clean - no synchronization required.");
   171         }
   172     }
   174     /**
   175      * Unloads all found modules.
   176      */
   177     public void unloadAll() {
   178         registeredModules.clear();
   179         LOG.info("All modules unloaded.");
   180     }
   182     /**
   183      * Returns the main menu.
   184      * 
   185      * @param db the interface to the database
   186      * @return a list of menus belonging to the main menu
   187      */
   188     public List<Menu> getMainMenu(DatabaseFacade db) {
   189         // TODO: user specific menu
   191         if (db.getDataSource().isPresent()) {
   192             try (Connection conn = db.getDataSource().get().getConnection()) {
   193                 final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   194                 final List<Module> modules = dao.listAll(conn);
   196                 final List<Menu> menu = modules
   197                     .stream()
   198                     .filter((mod) -> mod.isVisible())
   199                     .collect(Collectors.mapping(
   200                             (mod) -> new Menu(
   201                                     mod.getClassname(),
   202                                     new ResourceKey(
   203                                             registeredModules.get(mod.getClassname()).bundleBaseName(),
   204                                             registeredModules.get(mod.getClassname()).menuKey()),
   205                                     registeredModules.get(mod.getClassname()).modulePath()),
   206                             Collectors.toList())
   207                     );
   208                 return menu;
   209             } catch (SQLException ex) {
   210                 LOG.error("Unexpected SQLException when loading the main menu", ex);
   211                 return Collections.emptyList();
   212             }
   213         } else {
   214             return Collections.emptyList();
   215         }
   216     }
   218     /**
   219      * Returns an unmodifiable map of all registered modules.
   220      * 
   221      * The key is the classname of the module.
   222      * 
   223      * @return the map of registered modules
   224      */
   225     public Map<String, LightPITModule> getRegisteredModules() {
   226         return Collections.unmodifiableMap(registeredModules);
   227     }
   228 }

mercurial