27 * |
27 * |
28 */ |
28 */ |
29 package de.uapcore.lightpit; |
29 package de.uapcore.lightpit; |
30 |
30 |
31 import de.uapcore.lightpit.entities.CoreDAOFactory; |
31 import de.uapcore.lightpit.entities.CoreDAOFactory; |
|
32 import de.uapcore.lightpit.entities.Module; |
32 import de.uapcore.lightpit.entities.ModuleDao; |
33 import de.uapcore.lightpit.entities.ModuleDao; |
33 import java.sql.Connection; |
34 import java.sql.Connection; |
34 import java.sql.SQLException; |
35 import java.sql.SQLException; |
35 import java.util.Collections; |
36 import java.util.Collections; |
36 import java.util.HashMap; |
37 import java.util.HashMap; |
37 import java.util.List; |
38 import java.util.List; |
38 import java.util.Map; |
39 import java.util.Map; |
39 import java.util.Map.Entry; |
|
40 import java.util.Optional; |
40 import java.util.Optional; |
41 import java.util.concurrent.CopyOnWriteArrayList; |
|
42 import java.util.concurrent.atomic.AtomicBoolean; |
41 import java.util.concurrent.atomic.AtomicBoolean; |
43 import java.util.stream.Collectors; |
42 import java.util.stream.Collectors; |
44 import javax.servlet.Registration; |
43 import javax.servlet.Registration; |
45 import javax.servlet.ServletContext; |
44 import javax.servlet.ServletContext; |
46 import javax.servlet.ServletContextEvent; |
45 import javax.servlet.ServletContextEvent; |
62 */ |
61 */ |
63 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
62 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
64 private ServletContext sc; |
63 private ServletContext sc; |
65 |
64 |
66 /** |
65 /** |
|
66 * Maps class names to module information. |
|
67 */ |
|
68 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
|
69 |
|
70 /** |
67 * This flag is true, when synchronization is needed. |
71 * This flag is true, when synchronization is needed. |
68 */ |
72 */ |
69 private final AtomicBoolean dirty = new AtomicBoolean(true); |
73 private final AtomicBoolean dirty = new AtomicBoolean(true); |
70 |
|
71 private final CopyOnWriteArrayList<Menu> mainMenu = new CopyOnWriteArrayList<>(); |
|
72 private final List<Menu> immutableMainMenu = Collections.unmodifiableList(mainMenu); |
|
73 |
|
74 /** |
|
75 * Maps class names to module information. |
|
76 */ |
|
77 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
|
78 |
74 |
79 @Override |
75 @Override |
80 public void contextInitialized(ServletContextEvent sce) { |
76 public void contextInitialized(ServletContextEvent sce) { |
81 sc = sce.getServletContext(); |
77 sc = sce.getServletContext(); |
82 reloadAll(); |
78 reloadAll(); |
150 } |
146 } |
151 |
147 |
152 /** |
148 /** |
153 * Synchronizes module information with the database. |
149 * Synchronizes module information with the database. |
154 * |
150 * |
|
151 * This must be called from the {@link AbstractLightPITServlet}. |
|
152 * Admittedly the call will perform the synchronization once after reload |
|
153 * and be a no-op, afterwards. |
|
154 * However, we since the DatabaseFacade might be loaded after the module |
|
155 * manager, we must defer the synchronization to the first request |
|
156 * handled by the Servlet. |
|
157 * |
155 * @param db interface to the database |
158 * @param db interface to the database |
156 */ |
159 */ |
157 public void syncWithDatabase(DatabaseFacade db) { |
160 public void syncWithDatabase(DatabaseFacade db) { |
158 if (dirty.compareAndSet(true, false)) { |
161 if (dirty.compareAndSet(true, false)) { |
159 if (db.getDataSource().isPresent()) { |
162 if (db.getDataSource().isPresent()) { |
160 try (Connection conn = db.getDataSource().get().getConnection()) { |
163 try (Connection conn = db.getDataSource().get().getConnection()) { |
161 final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); |
164 final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); |
162 |
165 moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); |
163 final List<Entry<String, LightPITModule>> visibleModules = |
|
164 moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); |
|
165 |
|
166 final List<Menu> updatedMenu = visibleModules |
|
167 .stream() |
|
168 .collect(Collectors.mapping( |
|
169 (mod) -> new Menu( |
|
170 mod.getKey(), |
|
171 new ResourceKey(mod.getValue().bundleBaseName(), mod.getValue().menuKey()), |
|
172 mod.getValue().modulePath()), |
|
173 Collectors.toList()) |
|
174 ); |
|
175 |
|
176 mainMenu.removeIf((e) -> !updatedMenu.contains(e)); |
|
177 mainMenu.addAllAbsent(updatedMenu); |
|
178 } catch (SQLException ex) { |
166 } catch (SQLException ex) { |
179 LOG.error("Unexpected SQL Exception", ex); |
167 LOG.error("Unexpected SQL Exception", ex); |
180 } |
168 } |
181 } else { |
169 } else { |
182 LOG.warn("No datasource present. Cannot sync module information with database."); |
170 LOG.error("No datasource present. Cannot sync module information with database."); |
183 } |
171 } |
184 } else { |
172 } else { |
185 LOG.trace("Module information clean - no synchronization required."); |
173 LOG.trace("Module information clean - no synchronization required."); |
186 } |
174 } |
187 } |
175 } |
188 |
176 |
189 /** |
177 /** |
190 * Unloads all found modules. |
178 * Unloads all found modules. |
191 */ |
179 */ |
192 public void unloadAll() { |
180 public void unloadAll() { |
193 mainMenu.clear(); |
|
194 registeredModules.clear(); |
181 registeredModules.clear(); |
195 LOG.info("All modules unloaded."); |
182 LOG.info("All modules unloaded."); |
196 } |
183 } |
197 |
184 |
198 /** |
185 /** |
199 * Returns the main menu. |
186 * Returns the main menu. |
|
187 * |
|
188 * @param db the interface to the database |
200 * @return a list of menus belonging to the main menu |
189 * @return a list of menus belonging to the main menu |
201 */ |
190 */ |
202 public List<Menu> getMainMenu() { |
191 public List<Menu> getMainMenu(DatabaseFacade db) { |
203 return immutableMainMenu; |
192 // TODO: user specific menu |
|
193 |
|
194 if (db.getDataSource().isPresent()) { |
|
195 try (Connection conn = db.getDataSource().get().getConnection()) { |
|
196 final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); |
|
197 final List<Module> modules = dao.listAll(conn); |
|
198 |
|
199 final List<Menu> menu = modules |
|
200 .stream() |
|
201 .filter((mod) -> mod.isVisible()) |
|
202 .collect(Collectors.mapping( |
|
203 (mod) -> new Menu( |
|
204 mod.getClassname(), |
|
205 new ResourceKey( |
|
206 registeredModules.get(mod.getClassname()).bundleBaseName(), |
|
207 registeredModules.get(mod.getClassname()).menuKey()), |
|
208 registeredModules.get(mod.getClassname()).modulePath()), |
|
209 Collectors.toList()) |
|
210 ); |
|
211 return menu; |
|
212 } catch (SQLException ex) { |
|
213 LOG.error("Unexpected SQLException when loading the main menu", ex); |
|
214 return Collections.emptyList(); |
|
215 } |
|
216 } else { |
|
217 return Collections.emptyList(); |
|
218 } |
204 } |
219 } |
205 |
220 |
206 /** |
221 /** |
207 * Returns an unmodifiable map of all registered modules. |
222 * Returns an unmodifiable map of all registered modules. |
208 * |
223 * |