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

Sat, 16 May 2020 15:45:06 +0200

author
Mike Becker <universe@uap-core.de>
date
Sat, 16 May 2020 15:45:06 +0200
changeset 53
6a8498291606
parent 45
cc7f082c5ef3
child 57
1262b5433644
permissions
-rw-r--r--

fixes bug where displaying an error page for missing data source would also require that data source (error pages don't try to get database connections now)

also improves error pages in general

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2018 Mike Becker. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 */
package de.uapcore.lightpit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.Registration;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

/**
 * Scans registered servlets for LightPIT modules.
 */
@WebListener
public final class ModuleManager implements ServletContextListener {

    private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class);

    /**
     * The attribute name in the servlet context under which an instance of this class can be found.
     */
    public static final String SC_ATTR_NAME = ModuleManager.class.getName();
    private ServletContext sc;

    /**
     * Maps class names to module information.
     */
    private final List<LightPITModule> registeredModules = new ArrayList<>();

    /**
     * Contains the menu entries for the loaded modules.
     */
    private final List<MenuEntry> mainMenu = new ArrayList<>();

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        sc = sce.getServletContext();
        reloadAll();
        sc.setAttribute(SC_ATTR_NAME, this);
        LOG.info("Module manager injected into ServletContext.");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        unloadAll();
    }

    private Optional<LightPITModule> getModuleInfo(Registration reg) {
        try {
            final Class<?> scclass = Class.forName(reg.getClassName());

            final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass);
            final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class);

            if (lpservlet && !lpmodule) {
                LOG.warn(
                        "{} is a LightPIT Servlet but is missing the module annotation.",
                        reg.getClassName()
                );
            } else if (!lpservlet && lpmodule) {
                LOG.warn(
                        "{} is annotated as a LightPIT Module but does not extend {}.",
                        reg.getClassName(),
                        AbstractLightPITServlet.class.getSimpleName()
                );
            }

            if (lpservlet && lpmodule) {
                final LightPITModule moduleInfo = scclass.getAnnotation(LightPITModule.class);
                return Optional.of(moduleInfo);
            } else {
                return Optional.empty();
            }
        } catch (ClassNotFoundException ex) {
            LOG.error(
                    "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})",
                    reg.getClassName(),
                    ex.getMessage()
            );
            return Optional.empty();
        }
    }

    private void handleServletRegistration(String name, Registration reg) {
        final Optional<LightPITModule> moduleInfo = getModuleInfo(reg);
        if (moduleInfo.isPresent()) {
            registeredModules.add(moduleInfo.get());
            LOG.info("Module detected: {}", name);
        } else {
            LOG.debug("Servlet {} is no module, skipping.", name);
        }
    }

    /**
     * Scans for modules and reloads them all.
     */
    public void reloadAll() {
        registeredModules.clear();
        sc.getServletRegistrations().forEach(this::handleServletRegistration);
        createMainMenu();

        LOG.info("Modules loaded.");
    }

    /**
     * Unloads all found modules.
     */
    public void unloadAll() {
        registeredModules.clear();
        LOG.info("All modules unloaded.");
    }

    /**
     * Populates the main menu based on the registered modules.
     */
    private void createMainMenu() {
        mainMenu.clear();
        registeredModules
                .stream()
                .filter(mod -> !mod.systemModule())
                .map(mod -> new MenuEntry(
                        new ResourceKey(
                                mod.bundleBaseName(),
                                mod.menuKey()),
                        mod.modulePath(),
                        mod.defaultPriority()))
                .sorted()
                .forEachOrdered(mainMenu::add);
    }

    /**
     * Returns the main menu.
     *
     * @return a list of menu items
     */
    public List<MenuEntry> getMainMenu() {
        return Collections.unmodifiableList(mainMenu);
    }
}

mercurial