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

Sat, 09 May 2020 14:26:31 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 09 May 2020 14:26:31 +0200
changeset 29
27a0fdd7bca7
parent 27
src/java/de/uapcore/lightpit/ModuleManager.java@1f2a96efa69f
child 30
fb30f7b78f9b
permissions
-rw-r--r--

converts to maven project

     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.entities.CoreDAOFactory;
    32 import de.uapcore.lightpit.entities.Module;
    33 import de.uapcore.lightpit.entities.ModuleDao;
    34 import java.sql.Connection;
    35 import java.sql.SQLException;
    36 import java.util.Collections;
    37 import java.util.HashMap;
    38 import java.util.List;
    39 import java.util.Map;
    40 import java.util.Optional;
    41 import java.util.concurrent.atomic.AtomicBoolean;
    42 import java.util.stream.Collectors;
    43 import javax.servlet.Registration;
    44 import javax.servlet.ServletContext;
    45 import javax.servlet.ServletContextEvent;
    46 import javax.servlet.ServletContextListener;
    47 import javax.servlet.annotation.WebListener;
    48 import org.slf4j.Logger;
    49 import org.slf4j.LoggerFactory;
    51 /**
    52  * Scans registered servlets for LightPIT modules.
    53  */
    54 @WebListener
    55 public final class ModuleManager implements ServletContextListener {
    57     private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class);
    59     /**
    60      * The attribute name in the servlet context under which an instance of this class can be found.
    61      */
    62     public static final String SC_ATTR_NAME = ModuleManager.class.getName();
    63     private ServletContext sc;
    65     /**
    66      * Maps class names to module information.
    67      */
    68     private final Map<String, LightPITModule> registeredModules = new HashMap<>();
    70     /**
    71      * This flag is true, when synchronization is needed.
    72      */
    73     private final AtomicBoolean dirty = new AtomicBoolean(true);
    75     @Override
    76     public void contextInitialized(ServletContextEvent sce) {
    77         sc = sce.getServletContext();
    78         reloadAll();
    79         sc.setAttribute(SC_ATTR_NAME, this);
    80         LOG.info("Module manager injected into ServletContext.");
    81     }
    83     @Override
    84     public void contextDestroyed(ServletContextEvent sce) {
    85         unloadAll();
    86     }
    88     private Optional<LightPITModule> getModuleInfo(Registration reg) {
    89         try {
    90             final Class scclass = Class.forName(reg.getClassName());
    92             final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass);
    93             final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class);
    95             if (lpservlet && !lpmodule) {
    96                 LOG.warn(
    97                     "{} is a LightPIT Servlet but is missing the module annotation.",
    98                     reg.getClassName()
    99                 );
   100             } else if (!lpservlet && lpmodule) {
   101                 LOG.warn(
   102                     "{} is annotated as a LightPIT Module but does not extend {}.",
   103                     reg.getClassName(),
   104                     AbstractLightPITServlet.class.getSimpleName()
   105                 );
   106             }
   108             if (lpservlet && lpmodule) {
   109                 final Class<? extends AbstractLightPITServlet> clazz = scclass;
   110                 final LightPITModule moduleInfo = clazz.getAnnotation(LightPITModule.class);
   111                 return Optional.of(moduleInfo);
   112             } else {
   113                 return Optional.empty();
   114             }
   115         } catch (ClassNotFoundException ex) {
   116             LOG.error(
   117                     "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})",
   118                     reg.getClassName(),
   119                     ex.getMessage()
   120             );
   121             return Optional.empty();
   122         }        
   123     }
   125     private void handleServletRegistration(String name, Registration reg) {
   126         final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
   127         if (moduleInfo.isPresent()) {
   128             registeredModules.put(reg.getClassName(), moduleInfo.get());            
   129             LOG.info("Module detected: {}", name);
   130         } else {
   131             LOG.debug("Servlet {} is no module, skipping.", name);
   132         }
   133     }
   135     /**
   136      * Scans for modules and reloads them all.
   137      */
   138     public void reloadAll() {
   139         registeredModules.clear();
   140         sc.getServletRegistrations().forEach(this::handleServletRegistration);
   142         // TODO: implement dependency resolver
   144         dirty.set(true);
   145         LOG.info("Modules loaded.");
   146     }
   148     /**
   149      * Synchronizes module information with the database.
   150      * 
   151      * This must be called from the {@link AbstractLightPITServlet}.
   152      * Admittedly the call will perform the synchronization once after reload
   153      * and be a no-op, afterwards.
   154      * However, we since the DatabaseFacade might be loaded after the module
   155      * manager, we must defer the synchronization to the first request
   156      * handled by the Servlet.
   157      * 
   158      * @param db interface to the database
   159      */
   160     public void syncWithDatabase(DatabaseFacade db) {
   161         if (dirty.compareAndSet(true, false)) {
   162             if (db.getDataSource().isPresent()) {
   163                 try (Connection conn = db.getDataSource().get().getConnection()) {
   164                     final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   165                     moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet());
   166                 } catch (SQLException ex) {
   167                     LOG.error("Unexpected SQL Exception", ex);
   168                 }
   169             } else {
   170                 LOG.error("No datasource present. Cannot sync module information with database.");
   171             }
   172         } else {
   173             LOG.trace("Module information clean - no synchronization required.");
   174         }
   175     }
   177     /**
   178      * Unloads all found modules.
   179      */
   180     public void unloadAll() {
   181         registeredModules.clear();
   182         LOG.info("All modules unloaded.");
   183     }
   185     /**
   186      * Returns the main menu.
   187      * 
   188      * @param db the interface to the database
   189      * @return a list of menus belonging to the main menu
   190      */
   191     public List<Menu> getMainMenu(DatabaseFacade db) {
   192         // TODO: user specific menu
   194         if (db.getDataSource().isPresent()) {
   195             try (Connection conn = db.getDataSource().get().getConnection()) {
   196                 final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect());
   197                 final List<Module> modules = dao.listAll(conn);
   199                 final List<Menu> menu = modules
   200                     .stream()
   201                     .filter((mod) -> mod.isVisible())
   202                     .collect(Collectors.mapping(
   203                             (mod) -> new Menu(
   204                                     mod.getClassname(),
   205                                     new ResourceKey(
   206                                             registeredModules.get(mod.getClassname()).bundleBaseName(),
   207                                             registeredModules.get(mod.getClassname()).menuKey()),
   208                                     registeredModules.get(mod.getClassname()).modulePath()),
   209                             Collectors.toList())
   210                     );
   211                 return menu;
   212             } catch (SQLException ex) {
   213                 LOG.error("Unexpected SQLException when loading the main menu", ex);
   214                 return Collections.emptyList();
   215             }
   216         } else {
   217             return Collections.emptyList();
   218         }
   219     }
   221     /**
   222      * Returns an unmodifiable map of all registered modules.
   223      * 
   224      * The key is the classname of the module.
   225      * 
   226      * @return the map of registered modules
   227      */
   228     public Map<String, LightPITModule> getRegisteredModules() {
   229         return Collections.unmodifiableMap(registeredModules);
   230     }
   231 }

mercurial