# HG changeset patch # User Mike Becker # Date 1589036489 -7200 # Node ID 824d4042c8572c9bc912931ee17b6c18b292b00f # Parent fd8c40ff78c38f1af8af1adbaf8e1beab379479a cleanup and simplification of database access layer diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java --- a/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -46,9 +46,9 @@ * the necessary functionality for {@link LightPITModule}s. */ public abstract class AbstractLightPITServlet extends HttpServlet { - + private static final Logger LOG = LoggerFactory.getLogger(AbstractLightPITServlet.class); - + private static final String HTML_FULL_DISPATCHER = Functions.jspPath("html_full"); /** @@ -66,14 +66,14 @@ private interface HandlerMethod { ResponseType apply(HttpServletRequest t, HttpServletResponse u) throws IOException; } - + /** * Invocation mapping gathered from the {@link RequestMapping} annotations. - * + *

* Paths in this map must always start with a leading slash, although * the specification in the annotation must not start with a leading slash. - * - * The reason for this is the different handling of empty paths in + *

+ * The reason for this is the different handling of empty paths in * {@link HttpServletRequest#getPathInfo()}. */ private final Map> mappings = new HashMap<>(); @@ -87,6 +87,11 @@ return (ModuleManager) getServletContext().getAttribute(ModuleManager.SC_ATTR_NAME); } + /** + * Returns the annotated module information. + * + * @return the module annotation + */ public final LightPITModule getModuleInfo() { return moduleInfo; } @@ -152,8 +157,8 @@ if (params.length == 2 && HttpServletRequest.class.isAssignableFrom(params[0]) && HttpServletResponse.class.isAssignableFrom(params[1])) { - - final String requestPath = "/"+mapping.get().requestPath(); + + final String requestPath = "/" + mapping.get().requestPath(); if (mappings.computeIfAbsent(mapping.get().method(), k -> new HashMap<>()). putIfAbsent(requestPath, @@ -187,73 +192,72 @@ mappings.clear(); LOG.trace("{} destroyed", getServletName()); } - + /** * Sets the name of the dynamic fragment. - * + *

* It is sufficient to specify the name without any extension. The extension * is added automatically if not specified. - * + *

* The fragment must be located in the dynamic fragments folder. - * - * @param req the servlet request object + * + * @param req the servlet request object * @param fragmentName the name of the fragment * @see Constants#DYN_FRAGMENT_PATH_PREFIX */ public void setDynamicFragment(HttpServletRequest req, String fragmentName) { req.setAttribute(Constants.REQ_ATTR_FRAGMENT, Functions.dynFragmentPath(fragmentName)); } - + /** * Specifies the name of an additional stylesheet used by the module. - * + *

* Setting an additional stylesheet is optional, but quite common for HTML * output. - * + *

* It is sufficient to specify the name without any extension. The extension * is added automatically if not specified. - * - * @param req the servlet request object + * + * @param req the servlet request object * @param stylesheet the name of the stylesheet */ public void setStylesheet(HttpServletRequest req, String stylesheet) { req.setAttribute(Constants.REQ_ATTR_STYLESHEET, Functions.enforceExt(stylesheet, ".css")); } - + private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - + req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu(getDatabaseFacade())); req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp); } - + private Optional findMapping(HttpMethod method, HttpServletRequest req) { return Optional.ofNullable(mappings.get(method)).map( (rm) -> rm.get(Optional.ofNullable(req.getPathInfo()).orElse("/")) ); } - - private void forwardAsSepcified(ResponseType type, HttpServletRequest req, HttpServletResponse resp) + + private void forwardAsSpecified(ResponseType type, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { switch (type) { - case NONE: return; + case NONE: + return; case HTML_FULL: forwardToFullView(req, resp); return; // TODO: implement remaining response types default: - // this code should be unreachable - LOG.error("ResponseType switch is not exhaustive - this is a bug!"); - throw new UnsupportedOperationException(); + throw new AssertionError("ResponseType switch is not exhaustive - this is a bug!"); } } - + private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // Synchronize module information with database getModuleManager().syncWithDatabase(getDatabaseFacade()); - + // choose the requested language as session language (if available) or fall back to english, otherwise HttpSession session = req.getSession(); if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { @@ -261,28 +265,28 @@ Optional reqLocale = Optional.of(req.getLocale()); Locale sessionLocale = reqLocale.filter((rl) -> availableLanguages.map((al) -> al.contains(rl.getLanguage())).orElse(false)).orElse(Locale.ENGLISH); session.setAttribute(Constants.SESSION_ATTR_LANGUAGE, sessionLocale); - LOG.debug("Settng language for new session {}: {}", session.getId(), sessionLocale.getDisplayLanguage()); + LOG.debug("Setting language for new session {}: {}", session.getId(), sessionLocale.getDisplayLanguage()); } else { Locale sessionLocale = (Locale) session.getAttribute(Constants.SESSION_ATTR_LANGUAGE); resp.setLocale(sessionLocale); LOG.trace("Continuing session {} with language {}", session.getId(), sessionLocale); } - + // set some internal request attributes req.setAttribute(Constants.REQ_ATTR_PATH, Functions.fullPath(req)); req.setAttribute(Constants.REQ_ATTR_MODULE_CLASSNAME, this.getClass().getName()); Optional.ofNullable(moduleInfoELProxy).ifPresent((proxy) -> req.setAttribute(Constants.REQ_ATTR_MODULE_INFO, proxy)); - - + + // call the handler, if available, or send an HTTP 404 error Optional mapping = findMapping(method, req); if (mapping.isPresent()) { - forwardAsSepcified(mapping.get().apply(req, resp), req, resp); + forwardAsSpecified(mapping.get().apply(req, resp), req, resp); } else { resp.sendError(HttpServletResponse.SC_NOT_FOUND); } } - + @Override protected final void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/Constants.java --- a/src/main/java/de/uapcore/lightpit/Constants.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Constants.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -32,52 +32,52 @@ /** * Contains all non-local scope constants used by the this application. - * + *

* Constants with (class) local scope are defined in their respective classes. */ public final class Constants { public static final String JSP_PATH_PREFIX = "/WEB-INF/jsp/"; - + public static final String JSPF_PATH_PREFIX = "/WEB-INF/jspf/"; - + public static final String DYN_FRAGMENT_PATH_PREFIX = "/WEB-INF/dynamic_fragments/"; - - + + /** * Name for the context parameter specifying the available languages. */ public static final String CTX_ATTR_LANGUAGES = "available-languages"; - + /** * Name for the context parameter optionally specifying the JNDI context; */ public static final String CTX_ATTR_JNDI_CONTEXT = "jndi-context"; - + /** * Name for the context parameter optionally specifying a database schema. */ public static final String CTX_ATTR_DB_SCHEMA = "db-schema"; - + /** * Name for the context parameter optionally specifying a database dialect. */ public static final String CTX_ATTR_DB_DIALECT = "db-dialect"; - + /** * Key for the request attribute containing the class name of the currently dispatching module. */ public static final String REQ_ATTR_MODULE_CLASSNAME = fqn(AbstractLightPITServlet.class, "moduleClassname"); - + /** * Key for the request attribute containing the {@link LightPITModule} information of the currently dispatching module. */ public static final String REQ_ATTR_MODULE_INFO = fqn(AbstractLightPITServlet.class, "moduleInfo"); - + /** * Key for the request attribute containing the menu list. */ public static final String REQ_ATTR_MENU = fqn(AbstractLightPITServlet.class, "mainMenu"); - + /** * Key for the request attribute containing the full path information (servlet path + path info). */ @@ -85,22 +85,23 @@ /** * Key for the name of the fragment which should be rendered. - */ + */ public static final String REQ_ATTR_FRAGMENT = fqn(AbstractLightPITServlet.class, "fragment"); - + /** * Key for the name of the additional stylesheet used by a module. - */ + */ public static final String REQ_ATTR_STYLESHEET = fqn(AbstractLightPITServlet.class, "extraCss"); - - + + /** * Key for the current language selection within the session. */ public static final String SESSION_ATTR_LANGUAGE = fqn(AbstractLightPITServlet.class, "language"); - + /** * This class is not instantiatable. */ - private Constants() {} + private Constants() { + } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/DatabaseFacade.java --- a/src/main/java/de/uapcore/lightpit/DatabaseFacade.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/DatabaseFacade.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,10 +24,12 @@ * 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 de.uapcore.lightpit.dao.DataAccessObjects; +import de.uapcore.lightpit.dao.postgres.PGDataAccessObjects; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -57,6 +59,9 @@ */ private static final int DB_TEST_TIMEOUT = 10; + /** + * Specifies the database dialect. + */ public enum Dialect { Postgres } @@ -64,21 +69,21 @@ /** * The database dialect to use. *

- * May be override by context parameter. + * May be overridden by context parameter. * * @see Constants#CTX_ATTR_DB_DIALECT */ private Dialect dialect = Dialect.Postgres; - + /** * The default schema to test against when validating the connection. - * + *

* May be overridden by context parameter. - * + * * @see Constants#CTX_ATTR_DB_SCHEMA */ private static final String DB_DEFAULT_SCHEMA = "lightpit"; - + /** * The attribute name in the Servlet context under which an instance of this class can be found. */ @@ -86,21 +91,31 @@ private static final String DS_JNDI_NAME = "jdbc/lightpit/app"; private DataSource dataSource; - + private DataAccessObjects dataAccessObjects; + /** * Returns the data source. - * + *

* The Optional returned should never be empty. However, if something goes * wrong during initialization, the data source might be absent. * Hence, users of this data source are forced to check the existence. - * + * * @return a data source */ public Optional getDataSource() { // TODO: this should not be an optional, if an empty optional is actually an exception return Optional.ofNullable(dataSource); } - + + /** + * Returns the data access objects. + * + * @return an interface to obtain the data access objects + */ + public DataAccessObjects getDataAccessObjects() { + return dataAccessObjects; + } + public Dialect getSQLDialect() { return dialect; } @@ -138,9 +153,9 @@ @Override public void contextInitialized(ServletContextEvent sce) { ServletContext sc = sce.getServletContext(); - + dataSource = null; - + final String contextName = Optional .ofNullable(sc.getInitParameter(Constants.CTX_ATTR_JNDI_CONTEXT)) .orElse("java:comp/env"); @@ -156,6 +171,8 @@ } } + dataAccessObjects = createDataAccessObjects(dialect); + try { LOG.debug("Trying to access JNDI context {}...", contextName); Context initialCtx = new InitialContext(); @@ -169,13 +186,22 @@ } catch (NamingException | ClassCastException ex) { LOG.error("Cannot access JNDI resources.", ex); } - + sc.setAttribute(SC_ATTR_NAME, this); LOG.info("Database facade injected into ServletContext."); } + private static DataAccessObjects createDataAccessObjects(Dialect dialect) { + switch (dialect) { + case Postgres: + return new PGDataAccessObjects(); + default: + throw new AssertionError("Non-exhaustive switch - this is a bug."); + } + } + @Override public void contextDestroyed(ServletContextEvent sce) { dataSource = null; - } + } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/Functions.java --- a/src/main/java/de/uapcore/lightpit/Functions.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Functions.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -39,13 +39,13 @@ * Contains common static functions. */ public final class Functions { - + private static final Logger LOG = LoggerFactory.getLogger(Functions.class); public static Optional availableLanguages(ServletContext ctx) { return Optional.ofNullable(ctx.getInitParameter(Constants.CTX_ATTR_LANGUAGES)).map((x) -> x.split("\\s*,\\s*")); } - + public static String enforceExt(String filename, String ext) { return filename.endsWith(ext) ? filename : filename + ext; } @@ -53,23 +53,23 @@ public static String jspPath(String filename) { return enforceExt(Constants.JSP_PATH_PREFIX + filename, ".jsp"); } - + public static String jspfPath(String filename) { return enforceExt(Constants.JSPF_PATH_PREFIX + filename, ".jspf"); } - + public static String dynFragmentPath(String filename) { return enforceExt(Constants.DYN_FRAGMENT_PATH_PREFIX + filename, ".jsp"); } - + public static String fqn(String base, String name) { - return base+"."+name; + return base + "." + name; } public static String fqn(Class clazz, String name) { return fqn(clazz.getName(), name); } - + public static String fullPath(LightPITModule module, RequestMapping mapping) { StringBuilder sb = new StringBuilder(); sb.append(module.modulePath()); @@ -80,17 +80,17 @@ } return sb.toString(); } - + public static String fullPath(HttpServletRequest req) { return req.getServletPath() + Optional.ofNullable(req.getPathInfo()).orElse(""); } - + /** * Returns the module path of the given LightPIT Servlet module. - * + *

* If you specify a malformed LightPIT servlet, which does not have a module * annotation, the path to the /error/404.html page is returned. - * + * * @param clazz the servlet class * @return the module path */ @@ -106,9 +106,10 @@ return "/error/404.html"; } } - + /** * This class is not instantiatable. */ - private Functions() {} + private Functions() { + } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/HttpMethod.java --- a/src/main/java/de/uapcore/lightpit/HttpMethod.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/HttpMethod.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/LightPITModule.java --- a/src/main/java/de/uapcore/lightpit/LightPITModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/LightPITModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -34,7 +34,7 @@ /** * Contains information about a LightPIT module. - * + *

* This annotation is typically used to annotate the {@link WebServlet} which * implements the module's functionality. */ @@ -44,57 +44,59 @@ public @interface LightPITModule { /** * Base name of the module specific resource bundle. + * * @return a base name suitable for the JSTL tag 'setBundle'. */ String bundleBaseName(); - + /** * An array of required modules, identified by the string representation of * their class names. + * * @return an array of class names of required modules */ String[] requires() default {}; - + /** * The path for this module, which will also be used for the menu entry. - * + *

* 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(); - + /** * Returns the properties key for the module name. - * + * * @return the properties key */ String nameKey() default "name"; - + /** * Returns the properties key for the module description. - * + * * @return the properties key */ String descKey() default "description"; - - + + /** * Returns the properties key for the menu label. - * + *

* Set this string to empty string, if the module should be hidden from * the menu. - * + * * @return the properties key */ String menuKey() default "menuLabel"; - + /** * Returns the properties key for the page title. - * + *

* By default this is the same as the menu label. - * + * * @return the properties key */ String titleKey() default "menuLabel"; @@ -169,6 +171,6 @@ public String getDescKey() { return descKey; } - + } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/Menu.java --- a/src/main/java/de/uapcore/lightpit/Menu.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/Menu.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -35,30 +35,30 @@ /** * Maps a resource key for the menu label to the path name for the underlying * site. - * + *

* Objects of this class are internally instantiated by the * {@link ModuleManager}. */ public class Menu extends MenuEntry { - + private final List entries = new ArrayList<>(); private final List immutableEntries = Collections.unmodifiableList(entries); - + /** * Class name of the module for which this menu is built. */ private String moduleClassName; - - + + public Menu() { super(); } - + public Menu(String moduleClassName, ResourceKey resourceKey, String pathName) { super(resourceKey, pathName); this.moduleClassName = moduleClassName; } - + public void setModuleClassName(String moduleClassName) { this.moduleClassName = moduleClassName; } @@ -69,7 +69,7 @@ /** * Sets a new list of menu entries for this menu. - * + * * @param entries the list of new menu entries */ public void setEntries(List entries) { @@ -80,15 +80,16 @@ /** * Retrieves an immutable list of menu entries for this menu. - * + * * @return the list of menu entries */ public List getEntries() { return immutableEntries; } - + /** * Adds a new menu entry to this menu. + * * @param entry the menu entry to add */ public void addMenuEntry(MenuEntry entry) { diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/MenuEntry.java --- a/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/MenuEntry.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,24 +24,24 @@ * 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; /** * Maps a resource key for the menu label to the path name for the underlying * site. - * + *

* Objects of this class are internally instantiated by the * {@link ModuleManager}. */ public class MenuEntry { - + /** * Resource key for the menu label. */ private ResourceKey resourceKey; - + /** * Path name of the module, linked by this menu entry. */ @@ -70,5 +70,5 @@ public String getPathName() { return pathName; } - + } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/ModuleManager.java --- a/src/main/java/de/uapcore/lightpit/ModuleManager.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/ModuleManager.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,12 +24,10 @@ * 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 de.uapcore.lightpit.dao.CoreDAOFactory; -import de.uapcore.lightpit.dao.ModuleDao; import de.uapcore.lightpit.entities.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,25 +48,25 @@ */ @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 Map registeredModules = new HashMap<>(); - + /** * This flag is true, when synchronization is needed. */ private final AtomicBoolean dirty = new AtomicBoolean(true); - + @Override public void contextInitialized(ServletContextEvent sce) { sc = sce.getServletContext(); @@ -81,27 +79,27 @@ public void contextDestroyed(ServletContextEvent sce) { unloadAll(); } - + private Optional 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() + "{} 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() + "{} 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); @@ -115,28 +113,28 @@ ex.getMessage() ); return Optional.empty(); - } + } } - + private void handleServletRegistration(String name, Registration reg) { final Optional moduleInfo = getModuleInfo(reg); if (moduleInfo.isPresent()) { - registeredModules.put(reg.getClassName(), moduleInfo.get()); + registeredModules.put(reg.getClassName(), 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); - + // TODO: implement dependency resolver - + dirty.set(true); LOG.info("Modules loaded."); } @@ -157,8 +155,9 @@ if (dirty.compareAndSet(true, false)) { if (db.getDataSource().isPresent()) { try (Connection conn = db.getDataSource().get().getConnection()) { - final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); - moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); + db.getDataAccessObjects() + .getModuleDao() + .syncRegisteredModuleClasses(conn, registeredModules.entrySet()); } catch (SQLException ex) { LOG.error("Unexpected SQL Exception", ex); } @@ -169,7 +168,7 @@ LOG.trace("Module information clean - no synchronization required."); } } - + /** * Unloads all found modules. */ @@ -180,17 +179,16 @@ /** * Returns the main menu. - * + * * @param db the interface to the database * @return a list of menus belonging to the main menu */ public List

getMainMenu(DatabaseFacade db) { // TODO: user specific menu - + if (db.getDataSource().isPresent()) { try (Connection conn = db.getDataSource().get().getConnection()) { - final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); - final List modules = dao.listAll(conn); + final List modules = db.getDataAccessObjects().getModuleDao().list(conn); return modules .stream() @@ -211,12 +209,12 @@ return Collections.emptyList(); } } - + /** * Returns an unmodifiable map of all registered modules. - * + *

* The key is the classname of the module. - * + * * @return the map of registered modules */ public Map getRegisteredModules() { diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/RequestMapping.java --- a/src/main/java/de/uapcore/lightpit/RequestMapping.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/RequestMapping.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,20 +24,16 @@ * 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 java.lang.annotation.Documented; -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; +import java.lang.annotation.*; /** * Maps requests to methods. - * + *

* This annotation is used to annotate methods within classes which * override {@link AbstractLightPITServlet}. */ @@ -45,31 +41,31 @@ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface RequestMapping { - + /** * Specifies the HTTP method. - * + * * @return the HTTP method handled by the annotated Java method */ HttpMethod method(); /** * Specifies the request path relative to the module path. - * + *

* If a menu key is specified, this is also the path, which is linked * by the menu entry. - * + *

* The path must be specified without leading and trailing slash. - * + * * @return the request path the annotated method should handle */ String requestPath() default ""; - + /** * Returns the properties key for the (sub) menu label. - * + *

* This should only be used for {@link HttpMethod#GET} requests. - * + * * @return the properties key */ String menuKey() default ""; diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/ResourceKey.java --- a/src/main/java/de/uapcore/lightpit/ResourceKey.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/ResourceKey.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; @@ -34,9 +34,9 @@ public final class ResourceKey { private String bundle; private String key; - + public ResourceKey() { - + } public ResourceKey(String bundle, String key) { @@ -55,7 +55,7 @@ public void setKey(String key) { this.key = key; } - + public String getKey() { return key; } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/ResponseType.java --- a/src/main/java/de/uapcore/lightpit/ResponseType.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/ResponseType.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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; diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/AbstractDao.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/AbstractDao.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,55 @@ +/* + * 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.dao; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; + +public abstract class AbstractDao implements GenericDao { + + protected abstract PreparedStatement listQuery(Connection connection) throws SQLException; + + protected abstract T mapColumns(ResultSet result) throws SQLException; + + @Override + public List list(Connection conn) throws SQLException { + List list = new ArrayList<>(); + try (PreparedStatement stmt = listQuery(conn); + ResultSet result = stmt.executeQuery()) { + while (result.next()) { + list.add(mapColumns(result)); + } + } + return list; + } +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/CoreDAOFactory.java --- a/src/main/java/de/uapcore/lightpit/dao/CoreDAOFactory.java Sat May 09 15:19:21 2020 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,49 +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.dao; - -import de.uapcore.lightpit.DatabaseFacade; - -public final class CoreDAOFactory { - - private static final ModuleDao moduleDao = new ModuleDao(); - - private CoreDAOFactory() { - } - - public static ModuleDao getModuleDao(DatabaseFacade.Dialect dialect) { - // TODO: this is idiotic, we would not change the dialect while the app is running - switch (dialect) { - case Postgres: - return moduleDao; - default: - throw new AssertionError("Switch was not exhaustive."); - } - } -} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/DataAccessObjects.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,36 @@ +/* + * 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.dao; + +public interface DataAccessObjects { + + ModuleDao getModuleDao(); + + UserDao getUserDao(); +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/GenericDao.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/GenericDao.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,44 @@ +/* + * 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.dao; + +import java.sql.Connection; +import java.sql.SQLException; +import java.util.List; + +public interface GenericDao { + /** + * Returns a list of all entities. + * + * @param connection conn the connection to use + * @return a list of all objects + * @throws SQLException on any kind of SQL errors + */ + List list(Connection connection) throws SQLException; +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/ModuleDao.java --- a/src/main/java/de/uapcore/lightpit/dao/ModuleDao.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/ModuleDao.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,120 +24,29 @@ * 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.dao; import de.uapcore.lightpit.LightPITModule; import de.uapcore.lightpit.entities.Module; -import java.sql.*; -import java.util.ArrayList; -import java.util.List; +import java.sql.Connection; +import java.sql.SQLException; import java.util.Map; import java.util.Set; -public class ModuleDao { +public interface ModuleDao extends GenericDao { /** - * Maps database columns to POJO fields. - * - * @param result the database result set - * @param mod the POJO - * @throws SQLException on any kind of SQL errors - */ - protected void mapColumns(ResultSet result, Module mod) throws SQLException { - mod.setModID(result.getInt("modid")); - mod.setClassname(result.getString("classname")); - mod.setVisible(result.getBoolean("visible")); - mod.setPriority(result.getInt("priority")); - } - - - /** - * Must return a prepared statement for a single object query with the specified properties. - * - *

    - *
  • Parameter 1: classname
  • - *
  • Result field 1: visible
  • - *
- * - * @param conn the connection to use - * @return the prepared statement - * @throws SQLException on any kind of SQL errors - */ - protected PreparedStatement moduleCheckStatement(Connection conn) throws SQLException { - return conn.prepareStatement("SELECT visible FROM lpitcore_module WHERE classname = ?"); - } - - /** - * Must return a prepared statement for insertion with the specified properties. - * - *
    - *
  • Parameter 1: classname
  • - *
  • Parameter 2: visible
  • - *
  • Parameter 3: priority
  • - *
- * - * @param conn the connection to use - * @return the prepared statement - * @throws SQLException on any kind of SQL errors - */ - protected PreparedStatement moduleInsertStatement(Connection conn) throws SQLException { - return conn.prepareStatement("INSERT INTO lpitcore_module (classname, visible, priority) VALUES (?, ?, ?)"); - } - - /** * Synchronizes a set of registered module classes with the database. - * + *

* Inserts module classes which are not known to the database and sets them to be visible by default. * Module classes known to the database, which are not in the given set, are ignored. * - * @param conn the connection to use + * @param conn the connection to use * @param moduleSet the module set to synchronize * @throws SQLException on any kind of SQL errors */ - public final void syncRegisteredModuleClasses(Connection conn, Set> moduleSet) throws SQLException { - - PreparedStatement - check = moduleCheckStatement(conn), - insert = moduleInsertStatement(conn); - insert.setBoolean(2, true); - // update/delete not required, we do this in the module management UI - - for (Map.Entry modEntry : moduleSet) { - if (modEntry.getValue().systemModule()) continue; - - check.setString(1, modEntry.getKey()); - try (ResultSet r = check.executeQuery()) { - if (!r.next()) { - insert.setString(1, modEntry.getKey()); - insert.setInt(3, modEntry.getValue().defaultPriority()); - insert.executeUpdate(); - } - } - } - } - - /** - * Returns a list of all modules known by the database. - * - * Keep in mind, that system modules are never known to the database. - * - * @param conn the connection to use - * @return a list of all modules known by the database - * @throws SQLException on any kind of SQL errors - */ - public List listAll(Connection conn) throws SQLException { - List list = new ArrayList<>(); - try (Statement stmt = conn.createStatement(); - ResultSet result = stmt.executeQuery("SELECT * FROM lpitcore_module")) { - while (result.next()) { - final Module mod = new Module(); - mapColumns(result, mod); - list.add(mod); - } - } - return list; - } + void syncRegisteredModuleClasses(Connection conn, Set> moduleSet) throws SQLException; } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/UserDao.java --- a/src/main/java/de/uapcore/lightpit/dao/UserDao.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/dao/UserDao.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -30,50 +30,6 @@ import de.uapcore.lightpit.entities.User; -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; +public interface UserDao extends GenericDao { -public class UserDao { - - /** - * Maps SQL columns to POJO fields. - * - * @param result the database result set - * @param user the POJO - * @throws SQLException on any kind of SQL errors - */ - protected void mapColumns(ResultSet result, User user) throws SQLException { - user.setUserID(result.getInt("userid")); - user.setUsername(result.getString("username")); - user.setGivenname(result.getString("givenname")); - user.setLastname(result.getString("lastname")); - } - - /** - * Returns a list of all users ordered by their username. - *

- * Does not return reserved system users with negative user IDs. - * - * @param conn the connection to use - * @return a list of all users - * @throws SQLException on any kind of SQL errors - */ - public List listAll(Connection conn) throws SQLException { - List list = new ArrayList<>(); - try ( - Statement stmt = conn.createStatement(); - ResultSet result = stmt.executeQuery( - "SELECT * FROM lpitcore_user WHERE userid >= 0 ORDER BY username")) { - while (result.next()) { - final User user = new User(); - mapColumns(result, user); - list.add(user); - } - } - return list; - } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGDataAccessObjects.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,49 @@ +/* + * 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.dao.postgres; + +import de.uapcore.lightpit.dao.DataAccessObjects; +import de.uapcore.lightpit.dao.ModuleDao; +import de.uapcore.lightpit.dao.UserDao; + +public class PGDataAccessObjects implements DataAccessObjects { + + private final ModuleDao moduleDao = new PGModuleDao(); + private final UserDao userDao = new PGUserDao(); + + @Override + public ModuleDao getModuleDao() { + return moduleDao; + } + + @Override + public UserDao getUserDao() { + return userDao; + } +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/postgres/PGModuleDao.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGModuleDao.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,81 @@ +/* + * 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.dao.postgres; + +import de.uapcore.lightpit.LightPITModule; +import de.uapcore.lightpit.dao.AbstractDao; +import de.uapcore.lightpit.dao.ModuleDao; +import de.uapcore.lightpit.entities.Module; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.Map; +import java.util.Set; + +public final class PGModuleDao extends AbstractDao implements ModuleDao { + + @Override + protected PreparedStatement listQuery(Connection connection) throws SQLException { + return connection.prepareStatement("select * from lpitcore_module"); + } + + @Override + protected Module mapColumns(ResultSet result) throws SQLException { + final var mod = new Module(); + mod.setModID(result.getInt("modid")); + mod.setClassname(result.getString("classname")); + mod.setVisible(result.getBoolean("visible")); + mod.setPriority(result.getInt("priority")); + return mod; + } + + @Override + public void syncRegisteredModuleClasses(Connection conn, Set> moduleSet) throws SQLException { + + var check = conn.prepareStatement("select visible from lpitcore_module where classname = ?"); + var insert = conn.prepareStatement("insert into lpitcore_module (classname, visible, priority) values (?, ?, ?)"); + insert.setBoolean(2, true); + // update/delete not required, we do this in the module management UI + + for (Map.Entry modEntry : moduleSet) { + if (modEntry.getValue().systemModule()) continue; + + check.setString(1, modEntry.getKey()); + try (ResultSet r = check.executeQuery()) { + if (!r.next()) { + insert.setString(1, modEntry.getKey()); + insert.setInt(3, modEntry.getValue().defaultPriority()); + insert.executeUpdate(); + } + } + } + } +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/java/de/uapcore/lightpit/dao/postgres/PGUserDao.java Sat May 09 17:01:29 2020 +0200 @@ -0,0 +1,56 @@ +/* + * 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.dao.postgres; + +import de.uapcore.lightpit.dao.AbstractDao; +import de.uapcore.lightpit.dao.UserDao; +import de.uapcore.lightpit.entities.User; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +public final class PGUserDao extends AbstractDao implements UserDao { + + @Override + protected User mapColumns(ResultSet result) throws SQLException { + final var user = new User(); + user.setUserID(result.getInt("userid")); + user.setUsername(result.getString("username")); + user.setGivenname(result.getString("givenname")); + user.setLastname(result.getString("lastname")); + return user; + } + + @Override + protected PreparedStatement listQuery(Connection conn) throws SQLException { + return conn.prepareStatement("select * from lpitcore_user where userid >= 0 order by username"); + } +} diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/entities/Module.java --- a/src/main/java/de/uapcore/lightpit/entities/Module.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/entities/Module.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/modules/ErrorModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ErrorModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,15 +24,12 @@ * 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.modules; -import de.uapcore.lightpit.LightPITModule; -import de.uapcore.lightpit.AbstractLightPITServlet; -import de.uapcore.lightpit.HttpMethod; -import de.uapcore.lightpit.RequestMapping; -import de.uapcore.lightpit.ResponseType; +import de.uapcore.lightpit.*; + import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -51,28 +48,28 @@ urlPatterns = "/error/*" ) public final class ErrorModule extends AbstractLightPITServlet { - + public static final String REQ_ATTR_ERROR_CODE = "errorCode"; - + private ResponseType handle(HttpServletRequest req, HttpServletResponse resp, int sc) { - + req.setAttribute(REQ_ATTR_ERROR_CODE, sc); setStylesheet(req, "error"); setDynamicFragment(req, "error"); - + return ResponseType.HTML_FULL; } - + @RequestMapping(requestPath = "404", method = HttpMethod.GET) public ResponseType handle404(HttpServletRequest req, HttpServletResponse resp) { return handle(req, resp, 404); } - + @RequestMapping(requestPath = "403", method = HttpMethod.GET) public ResponseType handle403(HttpServletRequest req, HttpServletResponse resp) { return handle(req, resp, 403); } - + @RequestMapping(requestPath = "500", method = HttpMethod.GET) public ResponseType handle500(HttpServletRequest req, HttpServletResponse resp) { return handle(req, resp, 500); diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/modules/HomeModule.java --- a/src/main/java/de/uapcore/lightpit/modules/HomeModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/HomeModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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.modules; @@ -47,10 +47,10 @@ urlPatterns = "/home/*" ) public final class HomeModule extends AbstractLightPITServlet { - + @RequestMapping(method = HttpMethod.GET) public ResponseType handle(HttpServletRequest req, HttpServletResponse resp) { - + return ResponseType.HTML_FULL; } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/modules/LanguageModule.java --- a/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/LanguageModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,7 +24,7 @@ * 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.modules; @@ -49,15 +49,15 @@ urlPatterns = "/language/*" ) public final class LanguageModule extends AbstractLightPITServlet { - + private static final Logger LOG = LoggerFactory.getLogger(LanguageModule.class); - + private final List languages = new ArrayList<>(); @Override public void init() throws ServletException { super.init(); - + Optional langs = Functions.availableLanguages(getServletContext()); if (langs.isPresent()) { for (String lang : langs.get()) { @@ -71,7 +71,7 @@ LOG.warn("Specified lanaguge {} in context parameter cannot be mapped to an existing locale - skipping.", lang); } } - + } else { languages.add(Locale.ENGLISH); LOG.warn("Context parameter 'available-languges' not found. Only english will be available."); @@ -83,28 +83,28 @@ super.destroy(); languages.clear(); } - + @RequestMapping(method = HttpMethod.GET) public ResponseType handle(HttpServletRequest req, HttpServletResponse resp) { req.setAttribute("languages", languages); req.setAttribute("browserLanguage", req.getLocale()); - + setStylesheet(req, "language"); setDynamicFragment(req, "language"); return ResponseType.HTML_FULL; } - + @RequestMapping(method = HttpMethod.POST) public ResponseType switchLanguage(HttpServletRequest req, HttpServletResponse resp) { - + Optional chosenLanguage = Optional.ofNullable(req.getParameter("language")) .map(Locale::forLanguageTag) .filter((l) -> !l.getLanguage().isEmpty()); - + chosenLanguage.ifPresent((l) -> req.getSession().setAttribute(Constants.SESSION_ATTR_LANGUAGE, l)); chosenLanguage.ifPresent(resp::setLocale); - + return handle(req, resp); } } diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/modules/ModuleManagerModule.java --- a/src/main/java/de/uapcore/lightpit/modules/ModuleManagerModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/ModuleManagerModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,13 +24,12 @@ * 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.modules; import de.uapcore.lightpit.*; import de.uapcore.lightpit.LightPITModule.ELProxy; -import de.uapcore.lightpit.dao.CoreDAOFactory; import de.uapcore.lightpit.entities.Module; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -59,25 +58,26 @@ urlPatterns = "/modmgmt/*" ) public final class ModuleManagerModule extends AbstractLightPITServlet { - + private static final Logger LOG = LoggerFactory.getLogger(ModuleManagerModule.class); - + private static final String REQ_ATTR_MODULES = "modules"; - - + + @RequestMapping(method = HttpMethod.GET) public ResponseType handle(HttpServletRequest req, HttpServletResponse resp) throws IOException { - - Optional ds = getDatabaseFacade().getDataSource(); + + DatabaseFacade db = getDatabaseFacade(); + Optional ds = db.getDataSource(); if (ds.isPresent()) { try (Connection conn = ds.get().getConnection()) { - final List modules = CoreDAOFactory.getModuleDao(getDatabaseFacade().getSQLDialect()).listAll(conn); - + final List modules = db.getDataAccessObjects().getModuleDao().list(conn); + final Map registeredModules = getModuleManager().getRegisteredModules(); modules.forEach((mod) -> mod.setAnnotatedInfos(ELProxy.convert(registeredModules.get(mod.getClassname())))); - + req.setAttribute(REQ_ATTR_MODULES, modules); - setDynamicFragment(req, "modules"); + setDynamicFragment(req, "modules"); return ResponseType.HTML_FULL; } catch (SQLException ex) { LOG.error("Unexpected SQL Exception", ex); diff -r fd8c40ff78c3 -r 824d4042c857 src/main/java/de/uapcore/lightpit/modules/VersionsModule.java --- a/src/main/java/de/uapcore/lightpit/modules/VersionsModule.java Sat May 09 15:19:21 2020 +0200 +++ b/src/main/java/de/uapcore/lightpit/modules/VersionsModule.java Sat May 09 17:01:29 2020 +0200 @@ -1,8 +1,8 @@ /* * 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: * @@ -24,15 +24,12 @@ * 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.modules; -import de.uapcore.lightpit.LightPITModule; -import de.uapcore.lightpit.AbstractLightPITServlet; -import de.uapcore.lightpit.HttpMethod; -import de.uapcore.lightpit.RequestMapping; -import de.uapcore.lightpit.ResponseType; +import de.uapcore.lightpit.*; + import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -49,7 +46,7 @@ public final class VersionsModule extends AbstractLightPITServlet { @RequestMapping(method = HttpMethod.GET) public ResponseType handle(HttpServletRequest req, HttpServletResponse resp) { - + return ResponseType.HTML_FULL; } }