# HG changeset patch # User Mike Becker # Date 1523198462 -7200 # Node ID 1f2a96efa69f285397ad74fd727be0b3642a1611 # Parent 65d5a0ca49ae9316877b3346231e3c5cbbe8aec2 removes caching of main menu diff -r 65d5a0ca49ae -r 1f2a96efa69f src/java/de/uapcore/lightpit/AbstractLightPITServlet.java --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sun Apr 08 15:34:11 2018 +0200 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sun Apr 08 16:41:02 2018 +0200 @@ -222,7 +222,7 @@ private void forwardToFullView(HttpServletRequest req, HttpServletResponse resp) throws IOException, ServletException { - req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu()); + req.setAttribute(Constants.REQ_ATTR_MENU, getModuleManager().getMainMenu(getDatabaseFacade())); req.getRequestDispatcher(HTML_FULL_DISPATCHER).forward(req, resp); } @@ -249,7 +249,7 @@ private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { - + // Synchronize module information with database getModuleManager().syncWithDatabase(getDatabaseFacade()); diff -r 65d5a0ca49ae -r 1f2a96efa69f src/java/de/uapcore/lightpit/ModuleManager.java --- a/src/java/de/uapcore/lightpit/ModuleManager.java Sun Apr 08 15:34:11 2018 +0200 +++ b/src/java/de/uapcore/lightpit/ModuleManager.java Sun Apr 08 16:41:02 2018 +0200 @@ -29,6 +29,7 @@ package de.uapcore.lightpit; import de.uapcore.lightpit.entities.CoreDAOFactory; +import de.uapcore.lightpit.entities.Module; import de.uapcore.lightpit.entities.ModuleDao; import java.sql.Connection; import java.sql.SQLException; @@ -36,9 +37,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; import java.util.Optional; -import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; import javax.servlet.Registration; @@ -64,18 +63,15 @@ 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); - private final CopyOnWriteArrayList mainMenu = new CopyOnWriteArrayList<>(); - private final List immutableMainMenu = Collections.unmodifiableList(mainMenu); - - /** - * Maps class names to module information. - */ - private final Map registeredModules = new HashMap<>(); - @Override public void contextInitialized(ServletContextEvent sce) { sc = sce.getServletContext(); @@ -152,6 +148,13 @@ /** * Synchronizes module information with the database. * + * This must be called from the {@link AbstractLightPITServlet}. + * Admittedly the call will perform the synchronization once after reload + * and be a no-op, afterwards. + * However, we since the DatabaseFacade might be loaded after the module + * manager, we must defer the synchronization to the first request + * handled by the Servlet. + * * @param db interface to the database */ public void syncWithDatabase(DatabaseFacade db) { @@ -159,27 +162,12 @@ if (db.getDataSource().isPresent()) { try (Connection conn = db.getDataSource().get().getConnection()) { final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); - - final List> visibleModules = - moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); - - final List updatedMenu = visibleModules - .stream() - .collect(Collectors.mapping( - (mod) -> new Menu( - mod.getKey(), - new ResourceKey(mod.getValue().bundleBaseName(), mod.getValue().menuKey()), - mod.getValue().modulePath()), - Collectors.toList()) - ); - - mainMenu.removeIf((e) -> !updatedMenu.contains(e)); - mainMenu.addAllAbsent(updatedMenu); + moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); } catch (SQLException ex) { LOG.error("Unexpected SQL Exception", ex); } } else { - LOG.warn("No datasource present. Cannot sync module information with database."); + LOG.error("No datasource present. Cannot sync module information with database."); } } else { LOG.trace("Module information clean - no synchronization required."); @@ -190,17 +178,44 @@ * Unloads all found modules. */ public void unloadAll() { - mainMenu.clear(); registeredModules.clear(); LOG.info("All modules unloaded."); } /** * Returns the main menu. + * + * @param db the interface to the database * @return a list of menus belonging to the main menu */ - public List getMainMenu() { - return immutableMainMenu; + 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 menu = modules + .stream() + .filter((mod) -> mod.isVisible()) + .collect(Collectors.mapping( + (mod) -> new Menu( + mod.getClassname(), + new ResourceKey( + registeredModules.get(mod.getClassname()).bundleBaseName(), + registeredModules.get(mod.getClassname()).menuKey()), + registeredModules.get(mod.getClassname()).modulePath()), + Collectors.toList()) + ); + return menu; + } catch (SQLException ex) { + LOG.error("Unexpected SQLException when loading the main menu", ex); + return Collections.emptyList(); + } + } else { + return Collections.emptyList(); + } } /** diff -r 65d5a0ca49ae -r 1f2a96efa69f src/java/de/uapcore/lightpit/entities/ModuleDao.java --- a/src/java/de/uapcore/lightpit/entities/ModuleDao.java Sun Apr 08 15:34:11 2018 +0200 +++ b/src/java/de/uapcore/lightpit/entities/ModuleDao.java Sun Apr 08 16:41:02 2018 +0200 @@ -34,7 +34,6 @@ import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -88,18 +87,16 @@ } /** - * Synchronizes a set of registered module classes with the database and gives a list of visible modules in return. + * 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 moduleSet the module set to synchronize - * @return a list of elements from the given set, which should be visible in the menu * @throws SQLException */ - public final List> - syncRegisteredModuleClasses(Connection conn, Set> moduleSet) throws SQLException { + public final void syncRegisteredModuleClasses(Connection conn, Set> moduleSet) throws SQLException { PreparedStatement check = moduleCheckStatement(conn), @@ -107,30 +104,17 @@ insert.setBoolean(2, true); // update/delete not required, we do this in the module management UI - final List> visibleModules = new ArrayList<>(); + for (Map.Entry modEntry : moduleSet) { + if (modEntry.getValue().systemModule()) continue; - for (Map.Entry mod : moduleSet) { - if (mod.getValue().systemModule()) continue; - - check.setString(1, mod.getKey()); + check.setString(1, modEntry.getKey()); try (ResultSet r = check.executeQuery()) { - final boolean visible; - if (r.next()) { - visible = r.getBoolean(1); - } else { - insert.setString(1, mod.getKey()); + if (!r.next()) { + insert.setString(1, modEntry.getKey()); insert.executeUpdate(); - visible = !mod.getValue().menuKey().isEmpty(); - } - if (visible) { - visibleModules.add(new AbstractMap.SimpleEntry<>( - mod.getKey(), - mod.getValue() - )); } } } - return visibleModules; } /** @@ -144,8 +128,7 @@ */ public List listAll(Connection conn) throws SQLException { List list = new ArrayList<>(); - try ( - Statement stmt = conn.createStatement(); + try (Statement stmt = conn.createStatement(); ResultSet result = stmt.executeQuery("SELECT * FROM lpitcore_module")) { while (result.next()) { final Module mod = new Module();