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

Sat, 09 May 2020 15:19:21 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 09 May 2020 15:19:21 +0200
changeset 33
fd8c40ff78c3
parent 31
58f78f0142e8
child 34
824d4042c857
permissions
-rw-r--r--

fixes several warnings

     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 LightPITModule moduleInfo = scclass.getAnnotation(LightPITModule.class);
   107                 return Optional.of(moduleInfo);
   108             } else {
   109                 return Optional.empty();
   110             }
   111         } catch (ClassNotFoundException ex) {
   112             LOG.error(
   113                     "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})",
   114                     reg.getClassName(),
   115                     ex.getMessage()
   116             );
   117             return Optional.empty();
   118         }        
   119     }
   121     private void handleServletRegistration(String name, Registration reg) {
   122         final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
   123         if (moduleInfo.isPresent()) {
   124             registeredModules.put(reg.getClassName(), moduleInfo.get());            
   125             LOG.info("Module detected: {}", name);
   126         } else {
   127             LOG.debug("Servlet {} is no module, skipping.", name);
   128         }
   129     }
   131     /**
   132      * Scans for modules and reloads them all.
   133      */
   134     public void reloadAll() {
   135         registeredModules.clear();
   136         sc.getServletRegistrations().forEach(this::handleServletRegistration);
   138         // TODO: implement dependency resolver
   140         dirty.set(true);
   141         LOG.info("Modules loaded.");
   142     }
   144     /**
   145      * Synchronizes module information with the database.
   146      * <p>
   147      * This must be called from the {@link AbstractLightPITServlet}.
   148      * Admittedly the call will perform the synchronization once after reload
   149      * and be a no-op, afterwards.
   150      * However, since the DatabaseFacade might be loaded after the module
   151      * manager, we must defer the synchronization to the first request
   152      * handled by the Servlet.
   153      *
   154      * @param db interface to the database
   155      */
   156     public void syncWithDatabase(DatabaseFacade db) {
   157         if (dirty.compareAndSet(true, false)) {
   158             if (db.getDataSource().isPresent()) {
   159                 try (Connection conn = db.getDataSource().get().getConnection()) {
   160                     final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   161                     moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet());
   162                 } catch (SQLException ex) {
   163                     LOG.error("Unexpected SQL Exception", ex);
   164                 }
   165             } else {
   166                 LOG.error("No datasource present. Cannot sync module information with database.");
   167             }
   168         } else {
   169             LOG.trace("Module information clean - no synchronization required.");
   170         }
   171     }
   173     /**
   174      * Unloads all found modules.
   175      */
   176     public void unloadAll() {
   177         registeredModules.clear();
   178         LOG.info("All modules unloaded.");
   179     }
   181     /**
   182      * Returns the main menu.
   183      * 
   184      * @param db the interface to the database
   185      * @return a list of menus belonging to the main menu
   186      */
   187     public List<Menu> getMainMenu(DatabaseFacade db) {
   188         // TODO: user specific menu
   190         if (db.getDataSource().isPresent()) {
   191             try (Connection conn = db.getDataSource().get().getConnection()) {
   192                 final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   193                 final List<Module> modules = dao.listAll(conn);
   195                 return modules
   196                         .stream()
   197                         .filter(Module::isVisible)
   198                         .sorted(new Module.PriorityComparator())
   199                         .map(mod -> new Menu(
   200                                 mod.getClassname(),
   201                                 new ResourceKey(
   202                                         registeredModules.get(mod.getClassname()).bundleBaseName(),
   203                                         registeredModules.get(mod.getClassname()).menuKey()),
   204                                 registeredModules.get(mod.getClassname()).modulePath()))
   205                         .collect(Collectors.toList());
   206             } catch (SQLException ex) {
   207                 LOG.error("Unexpected SQLException when loading the main menu", ex);
   208                 return Collections.emptyList();
   209             }
   210         } else {
   211             return Collections.emptyList();
   212         }
   213     }
   215     /**
   216      * Returns an unmodifiable map of all registered modules.
   217      * 
   218      * The key is the classname of the module.
   219      * 
   220      * @return the map of registered modules
   221      */
   222     public Map<String, LightPITModule> getRegisteredModules() {
   223         return Collections.unmodifiableMap(registeredModules);
   224     }
   225 }

mercurial