26 * POSSIBILITY OF SUCH DAMAGE. |
26 * POSSIBILITY OF SUCH DAMAGE. |
27 * |
27 * |
28 */ |
28 */ |
29 package de.uapcore.lightpit; |
29 package de.uapcore.lightpit; |
30 |
30 |
31 import de.uapcore.lightpit.entities.Module; |
|
32 import org.slf4j.Logger; |
31 import org.slf4j.Logger; |
33 import org.slf4j.LoggerFactory; |
32 import org.slf4j.LoggerFactory; |
34 |
33 |
35 import javax.servlet.Registration; |
34 import javax.servlet.Registration; |
36 import javax.servlet.ServletContext; |
35 import javax.servlet.ServletContext; |
37 import javax.servlet.ServletContextEvent; |
36 import javax.servlet.ServletContextEvent; |
38 import javax.servlet.ServletContextListener; |
37 import javax.servlet.ServletContextListener; |
39 import javax.servlet.annotation.WebListener; |
38 import javax.servlet.annotation.WebListener; |
40 import java.sql.Connection; |
|
41 import java.sql.SQLException; |
|
42 import java.util.*; |
39 import java.util.*; |
43 import java.util.concurrent.atomic.AtomicBoolean; |
|
44 import java.util.stream.Collectors; |
|
45 |
40 |
46 /** |
41 /** |
47 * Scans registered servlets for LightPIT modules. |
42 * Scans registered servlets for LightPIT modules. |
48 */ |
43 */ |
49 @WebListener |
44 @WebListener |
61 * Maps class names to module information. |
56 * Maps class names to module information. |
62 */ |
57 */ |
63 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
58 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
64 |
59 |
65 /** |
60 /** |
66 * This flag is true, when synchronization is needed. |
61 * Contains the menu entries for the loaded modules. |
67 */ |
62 */ |
68 private final AtomicBoolean dirty = new AtomicBoolean(true); |
63 private final List<MenuEntry> mainMenu = new ArrayList<>(); |
69 |
64 |
70 @Override |
65 @Override |
71 public void contextInitialized(ServletContextEvent sce) { |
66 public void contextInitialized(ServletContextEvent sce) { |
72 sc = sce.getServletContext(); |
67 sc = sce.getServletContext(); |
73 reloadAll(); |
68 reloadAll(); |
130 * Scans for modules and reloads them all. |
125 * Scans for modules and reloads them all. |
131 */ |
126 */ |
132 public void reloadAll() { |
127 public void reloadAll() { |
133 registeredModules.clear(); |
128 registeredModules.clear(); |
134 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
129 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
|
130 createMainMenu(); |
135 |
131 |
136 // TODO: implement dependency resolver |
|
137 |
|
138 dirty.set(true); |
|
139 LOG.info("Modules loaded."); |
132 LOG.info("Modules loaded."); |
140 } |
|
141 |
|
142 /** |
|
143 * Synchronizes module information with the database. |
|
144 * <p> |
|
145 * This must be called from the {@link AbstractLightPITServlet}. |
|
146 * Admittedly the call will perform the synchronization once after reload |
|
147 * and be a no-op, afterwards. |
|
148 * However, since the DatabaseFacade might be loaded after the module |
|
149 * manager, we must defer the synchronization to the first request |
|
150 * handled by the Servlet. |
|
151 * |
|
152 * @param db interface to the database |
|
153 */ |
|
154 public void syncWithDatabase(DatabaseFacade db) { |
|
155 if (dirty.compareAndSet(true, false)) { |
|
156 if (db.getDataSource().isPresent()) { |
|
157 try (Connection conn = db.getDataSource().get().getConnection()) { |
|
158 db.getDataAccessObjects() |
|
159 .getModuleDao() |
|
160 .syncRegisteredModuleClasses(conn, registeredModules.entrySet()); |
|
161 } catch (SQLException ex) { |
|
162 LOG.error("Unexpected SQL Exception", ex); |
|
163 } |
|
164 } else { |
|
165 LOG.error("No datasource present. Cannot sync module information with database."); |
|
166 } |
|
167 } else { |
|
168 LOG.trace("Module information clean - no synchronization required."); |
|
169 } |
|
170 } |
133 } |
171 |
134 |
172 /** |
135 /** |
173 * Unloads all found modules. |
136 * Unloads all found modules. |
174 */ |
137 */ |
176 registeredModules.clear(); |
139 registeredModules.clear(); |
177 LOG.info("All modules unloaded."); |
140 LOG.info("All modules unloaded."); |
178 } |
141 } |
179 |
142 |
180 /** |
143 /** |
181 * Returns the main menu. |
144 * Populates the main menu based on the registered modules. |
182 * |
|
183 * @param db the interface to the database |
|
184 * @return a list of menus belonging to the main menu |
|
185 */ |
145 */ |
186 public List<Menu> getMainMenu(DatabaseFacade db) { |
146 private void createMainMenu() { |
187 // TODO: user specific menu |
147 mainMenu.clear(); |
188 |
148 registeredModules.entrySet() |
189 if (db.getDataSource().isPresent()) { |
149 .stream() |
190 try (Connection conn = db.getDataSource().get().getConnection()) { |
150 .filter(mod -> !mod.getValue().systemModule()) |
191 final List<Module> modules = db.getDataAccessObjects().getModuleDao().list(conn); |
151 .map(mod -> new MenuEntry( |
192 |
152 mod.getKey(), |
193 return modules |
153 new ResourceKey( |
194 .stream() |
154 mod.getValue().bundleBaseName(), |
195 .filter(Module::isVisible) |
155 mod.getValue().menuKey()), |
196 .sorted(new Module.PriorityComparator()) |
156 mod.getValue().modulePath(), |
197 .map(mod -> new Menu( |
157 mod.getValue().defaultPriority())) |
198 mod.getClassname(), |
158 .sorted() |
199 new ResourceKey( |
159 .forEachOrdered(mainMenu::add); |
200 registeredModules.get(mod.getClassname()).bundleBaseName(), |
|
201 registeredModules.get(mod.getClassname()).menuKey()), |
|
202 registeredModules.get(mod.getClassname()).modulePath())) |
|
203 .collect(Collectors.toList()); |
|
204 } catch (SQLException ex) { |
|
205 LOG.error("Unexpected SQLException when loading the main menu", ex); |
|
206 return Collections.emptyList(); |
|
207 } |
|
208 } else { |
|
209 return Collections.emptyList(); |
|
210 } |
|
211 } |
160 } |
212 |
161 |
213 /** |
162 /** |
214 * Returns an unmodifiable map of all registered modules. |
163 * Returns the main menu. |
215 * <p> |
|
216 * The key is the classname of the module. |
|
217 * |
164 * |
218 * @return the map of registered modules |
165 * @return a list of menu items |
219 */ |
166 */ |
220 public Map<String, LightPITModule> getRegisteredModules() { |
167 public List<MenuEntry> getMainMenu() { |
221 return Collections.unmodifiableMap(registeredModules); |
168 return Collections.unmodifiableList(mainMenu); |
222 } |
169 } |
223 } |
170 } |