src/main/java/de/uapcore/lightpit/ModuleManager.java

changeset 36
0f4f8f255c32
parent 34
824d4042c857
child 45
cc7f082c5ef3
equal deleted inserted replaced
35:4fa33bfa8fb9 36:0f4f8f255c32
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 }

mercurial