Sat, 31 Mar 2018 19:35:04 +0200
module synchronization with database
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/setup/postgres/psql_create_tables.sql Sat Mar 31 19:35:04 2018 +0200 1.3 @@ -0,0 +1,27 @@ 1.4 +-- This script creates the module management tables 1.5 +-- 1.6 + 1.7 +create table lpitcore_module ( 1.8 + modid serial primary key, 1.9 + classname varchar(100) not null unique, 1.10 + visible boolean not null default(true) 1.11 +); 1.12 + 1.13 +create table lpitcore_user ( 1.14 + userid serial primary key, 1.15 + username varchar(50) not null unique, 1.16 + lastname varchar(50), 1.17 + givenname varchar(50) 1.18 +); 1.19 + 1.20 +create table lpitcore_authorization ( 1.21 + modid integer not null references lpitcore_modules(modid) on delete cascade, 1.22 + userid integer not null references lpitcore_user(userid) on delete cascade, 1.23 + power integer not null check(power >= 0) 1.24 +); 1.25 + 1.26 +create table lpitcore_menu ( 1.27 + modid integer not null references lpitcore_modules(modid) on delete cascade, 1.28 + userid integer not null references lpitcore_user(userid) on delete cascade, 1.29 + seq integer not null 1.30 +);
2.1 --- a/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Mar 31 18:11:09 2018 +0200 2.2 +++ b/src/java/de/uapcore/lightpit/AbstractLightPITServlet.java Sat Mar 31 19:35:04 2018 +0200 2.3 @@ -249,9 +249,11 @@ 2.4 private void doProcess(HttpMethod method, HttpServletRequest req, HttpServletResponse resp) 2.5 throws ServletException, IOException { 2.6 2.7 - HttpSession session = req.getSession(); 2.8 + // Synchronize module information with database 2.9 + getModuleManager().syncWithDatabase(getDatabaseFacade()); 2.10 2.11 // choose the requested language as session language (if available) or fall back to english, otherwise 2.12 + HttpSession session = req.getSession(); 2.13 if (session.getAttribute(Constants.SESSION_ATTR_LANGUAGE) == null) { 2.14 Optional<List<String>> availableLanguages = Functions.availableLanguages(getServletContext()).map(Arrays::asList); 2.15 Optional<Locale> reqLocale = Optional.of(req.getLocale());
3.1 --- a/src/java/de/uapcore/lightpit/DatabaseFacade.java Sat Mar 31 18:11:09 2018 +0200 3.2 +++ b/src/java/de/uapcore/lightpit/DatabaseFacade.java Sat Mar 31 19:35:04 2018 +0200 3.3 @@ -151,7 +151,7 @@ 3.4 try { 3.5 dialect = Dialect.valueOf(dbDialect); 3.6 } catch (IllegalArgumentException ex) { 3.7 - LOG.error(String.format("Unknown or unsupported database dialect %s. Defaulting to %s.", dbDialect, dialect)); 3.8 + LOG.error("Unknown or unsupported database dialect {}. Defaulting to {}.", dbDialect, dialect); 3.9 } 3.10 } 3.11
4.1 --- a/src/java/de/uapcore/lightpit/LightPITModule.java Sat Mar 31 18:11:09 2018 +0200 4.2 +++ b/src/java/de/uapcore/lightpit/LightPITModule.java Sat Mar 31 19:35:04 2018 +0200 4.3 @@ -89,6 +89,14 @@ 4.4 String titleKey() default "menuLabel"; 4.5 4.6 /** 4.7 + * If set to <code>true</code>, this module is always loaded, but never 4.8 + * visible in the menu or the Web UI module manager. 4.9 + * 4.10 + * @return true, if this is a system module 4.11 + */ 4.12 + boolean systemModule() default false; 4.13 + 4.14 + /** 4.15 * Class representing the annotation. 4.16 * This is necessary, because the EL resolver cannot deal with 4.17 * annotation objects.
5.1 --- a/src/java/de/uapcore/lightpit/ModuleManager.java Sat Mar 31 18:11:09 2018 +0200 5.2 +++ b/src/java/de/uapcore/lightpit/ModuleManager.java Sat Mar 31 19:35:04 2018 +0200 5.3 @@ -28,11 +28,19 @@ 5.4 */ 5.5 package de.uapcore.lightpit; 5.6 5.7 +import java.sql.Connection; 5.8 +import java.sql.PreparedStatement; 5.9 +import java.sql.ResultSet; 5.10 +import java.sql.SQLException; 5.11 import java.util.ArrayList; 5.12 import java.util.Collections; 5.13 +import java.util.HashMap; 5.14 import java.util.List; 5.15 -import java.util.Objects; 5.16 +import java.util.Map; 5.17 +import java.util.Map.Entry; 5.18 import java.util.Optional; 5.19 +import java.util.concurrent.CopyOnWriteArrayList; 5.20 +import java.util.concurrent.atomic.AtomicBoolean; 5.21 import javax.servlet.Registration; 5.22 import javax.servlet.ServletContext; 5.23 import javax.servlet.ServletContextEvent; 5.24 @@ -55,9 +63,19 @@ 5.25 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); 5.26 private ServletContext sc; 5.27 5.28 - private final List<Menu> mainMenu = new ArrayList<>(); 5.29 + /** 5.30 + * This flag is true, when synchronization is needed. 5.31 + */ 5.32 + private AtomicBoolean dirty = new AtomicBoolean(true); 5.33 + 5.34 + private final CopyOnWriteArrayList<Menu> mainMenu = new CopyOnWriteArrayList<>(); 5.35 private final List<Menu> immutableMainMenu = Collections.unmodifiableList(mainMenu); 5.36 5.37 + /** 5.38 + * Maps class names to module information. 5.39 + */ 5.40 + private final Map<String, LightPITModule> registeredModules = new HashMap<>(); 5.41 + 5.42 @Override 5.43 public void contextInitialized(ServletContextEvent sce) { 5.44 sc = sce.getServletContext(); 5.45 @@ -108,24 +126,10 @@ 5.46 } 5.47 } 5.48 5.49 - private void addModuleToMenu(String moduleClassName, LightPITModule moduleInfo) { 5.50 - final Menu menu = new Menu( 5.51 - moduleClassName, 5.52 - new ResourceKey(moduleInfo.bundleBaseName(), moduleInfo.menuKey()), 5.53 - moduleInfo.modulePath() 5.54 - ); 5.55 - mainMenu.add(menu); 5.56 - } 5.57 - 5.58 private void handleServletRegistration(String name, Registration reg) { 5.59 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); 5.60 - if (moduleInfo.isPresent()) { 5.61 - // TODO: implement dependency resolver 5.62 - 5.63 - if (!moduleInfo.get().menuKey().isEmpty()) { 5.64 - addModuleToMenu(reg.getClassName(), moduleInfo.get()); 5.65 - } 5.66 - 5.67 + if (moduleInfo.isPresent()) { 5.68 + registeredModules.put(reg.getClassName(), moduleInfo.get()); 5.69 LOG.info("Module detected: {}", name); 5.70 } else { 5.71 LOG.debug("Servlet {} is no module, skipping.", name); 5.72 @@ -137,10 +141,66 @@ 5.73 */ 5.74 public void reloadAll() { 5.75 sc.getServletRegistrations().forEach(this::handleServletRegistration); 5.76 + 5.77 + // TODO: implement dependency resolver 5.78 + 5.79 LOG.info("Modules loaded."); 5.80 } 5.81 5.82 /** 5.83 + * Synchronizes module information with the database. 5.84 + * 5.85 + * @param db interface to the database 5.86 + */ 5.87 + public void syncWithDatabase(DatabaseFacade db) { 5.88 + if (dirty.compareAndSet(true, false)) { 5.89 + if (db.getDataSource().isPresent()) { 5.90 + try (Connection conn = db.getDataSource().get().getConnection()) { 5.91 + PreparedStatement 5.92 + check = conn.prepareStatement("SELECT visible FROM lpitcore_module WHERE classname = ?"), 5.93 + insert = conn.prepareStatement("INSERT INTO lpitcore_module (classname, visible) VALUES (?, ?)"); 5.94 + insert.setBoolean(2, true); 5.95 + // update/delete not required, we do this in the module management UI 5.96 + 5.97 + final List<Menu> updatedMenu = new ArrayList<>(); 5.98 + 5.99 + for (Entry<String, LightPITModule> mod : registeredModules.entrySet()) { 5.100 + if (mod.getValue().systemModule()) continue; 5.101 + 5.102 + check.setString(1, mod.getKey()); 5.103 + try (ResultSet r = check.executeQuery()) { 5.104 + final boolean addToMenu; 5.105 + if (r.next()) { 5.106 + addToMenu = r.getBoolean(1); 5.107 + } else { 5.108 + insert.setString(1, mod.getKey()); 5.109 + insert.executeUpdate(); 5.110 + addToMenu = !mod.getValue().menuKey().isEmpty(); 5.111 + } 5.112 + if (addToMenu) { 5.113 + updatedMenu.add(new Menu( 5.114 + mod.getKey(), 5.115 + new ResourceKey(mod.getValue().bundleBaseName(), mod.getValue().menuKey()), 5.116 + mod.getValue().modulePath() 5.117 + )); 5.118 + } 5.119 + } 5.120 + } 5.121 + 5.122 + mainMenu.removeIf((e) -> !updatedMenu.contains(e)); 5.123 + mainMenu.addAllAbsent(updatedMenu); 5.124 + } catch (SQLException ex) { 5.125 + LOG.error("Unexpected SQL Exception", ex); 5.126 + } 5.127 + } else { 5.128 + LOG.warn("No datasource present. Cannot sync module information with database."); 5.129 + } 5.130 + } else { 5.131 + LOG.debug("Module information clean - no synchronization required."); 5.132 + } 5.133 + } 5.134 + 5.135 + /** 5.136 * Unloads all found modules. 5.137 */ 5.138 public void unloadAll() {
6.1 --- a/src/java/de/uapcore/lightpit/modules/ErrorModule.java Sat Mar 31 18:11:09 2018 +0200 6.2 +++ b/src/java/de/uapcore/lightpit/modules/ErrorModule.java Sat Mar 31 19:35:04 2018 +0200 6.3 @@ -43,8 +43,8 @@ 6.4 @LightPITModule( 6.5 bundleBaseName = "de.uapcore.lightpit.resources.localization.error", 6.6 modulePath = "error", 6.7 - menuKey = "", 6.8 - titleKey = "title" 6.9 + titleKey = "title", 6.10 + systemModule = true 6.11 ) 6.12 @WebServlet( 6.13 name = "ErrorModule",