Sat, 23 May 2020 14:13:09 +0200
bloat removal 3/3 - LightPITModule annotation and ModuleManager
--- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 23 14:13:09 2020 +0200 @@ -50,7 +50,7 @@ /** * A special implementation of a HTTPServlet which is focused on implementing - * the necessary functionality for {@link LightPITModule}s. + * the necessary functionality for LightPIT pages. */ public abstract class AbstractLightPITServlet extends HttpServlet { @@ -90,15 +90,6 @@ private final Map<HttpMethod, Map<String, Method>> mappings = new HashMap<>(); /** - * Gives implementing modules access to the {@link ModuleManager}. - * - * @return the module manager - */ - protected final ModuleManager getModuleManager() { - return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); - } - - /** * Returns the name of the resource bundle associated with this servlet. * @return the resource bundle base name */ @@ -332,7 +323,12 @@ private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - final var mainMenu = new ArrayList<MenuEntry>(getModuleManager().getMainMenu()); + final String lightpitBundle = "localization.lightpit"; + final var mainMenu = List.of( + new MenuEntry(new ResourceKey(lightpitBundle, "menu.projects"), "projects/"), + new MenuEntry(new ResourceKey(lightpitBundle, "menu.users"), "teams/"), + new MenuEntry(new ResourceKey(lightpitBundle, "menu.languages"), "language/") + ); for (var entry : mainMenu) { if (Functions.fullPath(req).startsWith("/" + entry.getPathName())) { entry.setActive(true);
--- a/src/main/java/de/uapcore/lightpit/LightPITModule.java Sat May 23 13:52:04 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,101 +0,0 @@ -/* - * 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 javax.servlet.annotation.WebServlet; -import java.lang.annotation.*; - - -/** - * Contains information about a LightPIT module. - * <p> - * This annotation is typically used to annotate the {@link WebServlet} which - * implements the module's functionality. - */ -@Documented -@Retention(RetentionPolicy.RUNTIME) -@Target(ElementType.TYPE) -public @interface LightPITModule { - /** - * Base name of the module specific resource bundle. - * - * @return a base name suitable for the JSTL tag 'setBundle'. - */ - String bundleBaseName(); - - /** - * The path for this module, which will also be used for the menu entry. - * <p> - * This path must adhere to the URL pattern of the Servlet but must not - * contain any starting or trailing slashes. - * - * @return the relative module path - */ - String modulePath(); - - /** - * If set to <code>true</code>, this module is always loaded, but never - * visible in the menu or the Web UI module manager. - * - * @return true, if this is a system module - */ - boolean systemModule() default false; - - /** - * Optionally specifies a default priority for this module. - * The priority is used to order the menu entries. - * - * @return an integer priority - */ - int defaultPriority() default 1000; - - /** - * Class representing the annotation. - * This is necessary, because the EL resolver cannot deal with - * annotation objects. - * <p> - * Note, that only the properties which are interesting for the JSP pages - * are proxied by this object. - */ - class ELProxy { - private final String bundleBaseName, modulePath; - - public ELProxy(LightPITModule annotation) { - bundleBaseName = annotation.bundleBaseName(); - modulePath = annotation.modulePath(); - } - - public String getBundleBaseName() { - return bundleBaseName; - } - public String getModulePath() { - return modulePath; - } - } -}
--- a/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat May 23 14:13:09 2020 +0200 @@ -28,16 +28,7 @@ */ package de.uapcore.lightpit; -import java.util.Objects; - -/** - * Maps a resource key for the menu label to the path name for the underlying - * site. - * <p> - * Objects of this class are internally instantiated by the - * {@link ModuleManager}. - */ -public class MenuEntry implements Comparable<MenuEntry> { +public class MenuEntry { /** * Resource key for the menu label. @@ -55,27 +46,20 @@ private final String pathName; /** - * Sequence number to determine the ordering of the menu. - */ - private final int sequence; - - /** * True if this menu entry is active. */ private boolean active = false; - public MenuEntry(ResourceKey resourceKey, String pathName, int sequence) { + public MenuEntry(ResourceKey resourceKey, String pathName) { this.text = null; this.resourceKey = resourceKey; this.pathName = pathName; - this.sequence = sequence; } - public MenuEntry(String text, String pathName, int sequence) { + public MenuEntry(String text, String pathName) { this.text = text; this.resourceKey = null; this.pathName = pathName; - this.sequence = sequence; } public ResourceKey getResourceKey() { @@ -90,10 +74,6 @@ return pathName; } - public int getSequence() { - return sequence; - } - public boolean isActive() { return this.active; } @@ -102,22 +82,4 @@ this.active = true; } - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - MenuEntry menuEntry = (MenuEntry) o; - return resourceKey.equals(menuEntry.resourceKey) && - pathName.equals(menuEntry.pathName); - } - - @Override - public int hashCode() { - return Objects.hash(resourceKey, pathName); - } - - @Override - public int compareTo(MenuEntry menuEntry) { - return Integer.compare(this.sequence, menuEntry.sequence); - } }
--- a/src/main/java/de/uapcore/lightpit/ModuleManager.java Sat May 23 13:52:04 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -/* - * 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(), - "menuLabel"), - 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); - } -}
--- a/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Sat May 23 14:13:09 2020 +0200 @@ -28,21 +28,16 @@ */ package de.uapcore.lightpit.modules; -import de.uapcore.lightpit.*; +import de.uapcore.lightpit.AbstractLightPITServlet; +import de.uapcore.lightpit.HttpMethod; +import de.uapcore.lightpit.RequestMapping; +import de.uapcore.lightpit.ResponseType; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.util.Optional; -/** - * Entry point for the application. - */ -@LightPITModule( - bundleBaseName = "localization.error", - modulePath = "error", - systemModule = true -) @WebServlet( name = "ErrorModule", urlPatterns = "/error/*"
--- a/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 23 14:13:09 2020 +0200 @@ -38,12 +38,6 @@ import javax.servlet.http.HttpServletResponse; import java.util.*; - -@LightPITModule( - bundleBaseName = "localization.language", - modulePath = "language", - defaultPriority = 20000 -) @WebServlet( name = "LanguageModule", urlPatterns = "/language/*"
--- a/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ProjectsModule.java Sat May 23 14:13:09 2020 +0200 @@ -49,11 +49,6 @@ import static de.uapcore.lightpit.Functions.fqn; -@LightPITModule( - bundleBaseName = "localization.projects", - modulePath = "projects", - defaultPriority = 20 -) @WebServlet( name = "ProjectsModule", urlPatterns = "/projects/*" @@ -149,18 +144,18 @@ MenuEntry entry; final var breadcrumbs = new ArrayList<MenuEntry>(); - entry = new MenuEntry(new ResourceKey("localization.projects", "menuLabel"), - "projects/", 0); + entry = new MenuEntry(new ResourceKey("localization.lightpit", "menu.projects"), + "projects/"); breadcrumbs.add(entry); if (level == 0) entry.setActive(true); if (sessionSelection.project != null) { if (sessionSelection.project.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.create"), - "projects/edit", 1); + "projects/edit"); } else { entry = new MenuEntry(sessionSelection.project.getName(), - "projects/view?pid=" + sessionSelection.project.getId(), 1); + "projects/view?pid=" + sessionSelection.project.getId()); } if (level == 1) entry.setActive(true); breadcrumbs.add(entry); @@ -169,11 +164,11 @@ if (sessionSelection.version != null) { if (sessionSelection.version.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.version.create"), - "projects/versions/edit", 2); + "projects/versions/edit"); } else { entry = new MenuEntry(sessionSelection.version.getName(), // TODO: change link to issue overview for that version - "projects/versions/edit?id=" + sessionSelection.version.getId(), 2); + "projects/versions/edit?id=" + sessionSelection.version.getId()); } if (level == 2) entry.setActive(true); breadcrumbs.add(entry); @@ -182,15 +177,15 @@ if (sessionSelection.issue != null) { entry = new MenuEntry(new ResourceKey("localization.projects", "menu.issues"), // TODO: change link to a separate issue view (maybe depending on the selected version) - "projects/view?pid=" + sessionSelection.issue.getProject().getId(), 3); + "projects/view?pid=" + sessionSelection.issue.getProject().getId()); breadcrumbs.add(entry); if (sessionSelection.issue.getId() < 0) { entry = new MenuEntry(new ResourceKey("localization.projects", "button.issue.create"), - "projects/issues/edit", 2); + "projects/issues/edit"); } else { entry = new MenuEntry("#" + sessionSelection.issue.getId(), // TODO: maybe change link to a view rather than directly opening the editor - "projects/issues/edit?id=" + sessionSelection.issue.getId(), 4); + "projects/issues/edit?id=" + sessionSelection.issue.getId()); } if (level == 3) entry.setActive(true); breadcrumbs.add(entry);
--- a/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Sat May 23 13:52:04 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/UsersModule.java Sat May 23 14:13:09 2020 +0200 @@ -40,11 +40,6 @@ import java.sql.SQLException; import java.util.NoSuchElementException; -@LightPITModule( - bundleBaseName = "localization.users", - modulePath = "teams", - defaultPriority = 100 -) @WebServlet( name = "UsersModule", urlPatterns = "/teams/*"
--- a/src/main/resources/localization/language.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/language.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=Language Selection -menuLabel=Language submit = Switch language browserLanguage = Browser language
--- a/src/main/resources/localization/language_de.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/language_de.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=Sprachauswahl -menuLabel = Sprache submit = Sprache ausw\u00e4hlen browserLanguage = Browsersprache
--- a/src/main/resources/localization/lightpit.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/lightpit.properties Sat May 23 14:13:09 2020 +0200 @@ -21,10 +21,14 @@ # 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. -version=LightPIT - Version 0.1 (Snapshot) +version=LightPIT - Version 0.1 (Snapshot) button.okay=OK button.cancel=Cancel commit.success=Operation successful - you will be redirected in a second. -commit.redirect-link=If redirection does not work, click the following link: \ No newline at end of file +commit.redirect-link=If redirection does not work, click the following link: + +menu.projects=Projects +menu.users=Developer +menu.languages=Language
--- a/src/main/resources/localization/lightpit_de.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/lightpit_de.properties Sat May 23 14:13:09 2020 +0200 @@ -28,3 +28,7 @@ commit.success=Operation erfolgreich - Sie werden jeden Moment weitergeleitet. commit.redirect-link=Falls die Weiterleitung nicht klappt, klicken Sie bitte hier: + +menu.projects=Projekte +menu.users=Entwickler +menu.languages=Sprache
--- a/src/main/resources/localization/projects.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/projects.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=Project Tracking -menuLabel=Projects button.create=New Project button.version.create=New Version
--- a/src/main/resources/localization/projects_de.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/projects_de.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=Projektverwaltung -menuLabel=Projekte button.create=Neues Projekt button.version.create=Neue Version
--- a/src/main/resources/localization/users.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/users.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=User Management -menuLabel=Developer button.create=Add Developer
--- a/src/main/resources/localization/users_de.properties Sat May 23 13:52:04 2020 +0200 +++ b/src/main/resources/localization/users_de.properties Sat May 23 14:13:09 2020 +0200 @@ -22,7 +22,6 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pageTitle=Benutzerverwaltung -menuLabel=Entwickler button.create=Neuer Entwickler
--- a/src/main/webapp/index.jsp Sat May 23 13:52:04 2020 +0200 +++ b/src/main/webapp/index.jsp Sat May 23 14:13:09 2020 +0200 @@ -26,5 +26,5 @@ --%> <% response.setStatus(response.SC_MOVED_TEMPORARILY); - response.setHeader("Location", "/projects/"); + response.setHeader("Location", "projects/"); %>