1 /* |
1 /* |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. |
3 * |
3 * |
4 * Copyright 2018 Mike Becker. All rights reserved. |
4 * Copyright 2018 Mike Becker. All rights reserved. |
5 * |
5 * |
6 * Redistribution and use in source and binary forms, with or without |
6 * Redistribution and use in source and binary forms, with or without |
7 * modification, are permitted provided that the following conditions are met: |
7 * modification, are permitted provided that the following conditions are met: |
8 * |
8 * |
9 * 1. Redistributions of source code must retain the above copyright |
9 * 1. Redistributions of source code must retain the above copyright |
10 * notice, this list of conditions and the following disclaimer. |
10 * notice, this list of conditions and the following disclaimer. |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
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.dao.CoreDAOFactory; |
|
32 import de.uapcore.lightpit.dao.ModuleDao; |
|
33 import de.uapcore.lightpit.entities.Module; |
31 import de.uapcore.lightpit.entities.Module; |
34 import org.slf4j.Logger; |
32 import org.slf4j.Logger; |
35 import org.slf4j.LoggerFactory; |
33 import org.slf4j.LoggerFactory; |
36 |
34 |
37 import javax.servlet.Registration; |
35 import javax.servlet.Registration; |
48 /** |
46 /** |
49 * Scans registered servlets for LightPIT modules. |
47 * Scans registered servlets for LightPIT modules. |
50 */ |
48 */ |
51 @WebListener |
49 @WebListener |
52 public final class ModuleManager implements ServletContextListener { |
50 public final class ModuleManager implements ServletContextListener { |
53 |
51 |
54 private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class); |
52 private static final Logger LOG = LoggerFactory.getLogger(ModuleManager.class); |
55 |
53 |
56 /** |
54 /** |
57 * The attribute name in the servlet context under which an instance of this class can be found. |
55 * The attribute name in the servlet context under which an instance of this class can be found. |
58 */ |
56 */ |
59 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
57 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
60 private ServletContext sc; |
58 private ServletContext sc; |
61 |
59 |
62 /** |
60 /** |
63 * Maps class names to module information. |
61 * Maps class names to module information. |
64 */ |
62 */ |
65 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
63 private final Map<String, LightPITModule> registeredModules = new HashMap<>(); |
66 |
64 |
67 /** |
65 /** |
68 * This flag is true, when synchronization is needed. |
66 * This flag is true, when synchronization is needed. |
69 */ |
67 */ |
70 private final AtomicBoolean dirty = new AtomicBoolean(true); |
68 private final AtomicBoolean dirty = new AtomicBoolean(true); |
71 |
69 |
72 @Override |
70 @Override |
73 public void contextInitialized(ServletContextEvent sce) { |
71 public void contextInitialized(ServletContextEvent sce) { |
74 sc = sce.getServletContext(); |
72 sc = sce.getServletContext(); |
75 reloadAll(); |
73 reloadAll(); |
76 sc.setAttribute(SC_ATTR_NAME, this); |
74 sc.setAttribute(SC_ATTR_NAME, this); |
79 |
77 |
80 @Override |
78 @Override |
81 public void contextDestroyed(ServletContextEvent sce) { |
79 public void contextDestroyed(ServletContextEvent sce) { |
82 unloadAll(); |
80 unloadAll(); |
83 } |
81 } |
84 |
82 |
85 private Optional<LightPITModule> getModuleInfo(Registration reg) { |
83 private Optional<LightPITModule> getModuleInfo(Registration reg) { |
86 try { |
84 try { |
87 final Class<?> scclass = Class.forName(reg.getClassName()); |
85 final Class<?> scclass = Class.forName(reg.getClassName()); |
88 |
86 |
89 final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass); |
87 final boolean lpservlet = AbstractLightPITServlet.class.isAssignableFrom(scclass); |
90 final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class); |
88 final boolean lpmodule = scclass.isAnnotationPresent(LightPITModule.class); |
91 |
89 |
92 if (lpservlet && !lpmodule) { |
90 if (lpservlet && !lpmodule) { |
93 LOG.warn( |
91 LOG.warn( |
94 "{} is a LightPIT Servlet but is missing the module annotation.", |
92 "{} is a LightPIT Servlet but is missing the module annotation.", |
95 reg.getClassName() |
93 reg.getClassName() |
96 ); |
94 ); |
97 } else if (!lpservlet && lpmodule) { |
95 } else if (!lpservlet && lpmodule) { |
98 LOG.warn( |
96 LOG.warn( |
99 "{} is annotated as a LightPIT Module but does not extend {}.", |
97 "{} is annotated as a LightPIT Module but does not extend {}.", |
100 reg.getClassName(), |
98 reg.getClassName(), |
101 AbstractLightPITServlet.class.getSimpleName() |
99 AbstractLightPITServlet.class.getSimpleName() |
102 ); |
100 ); |
103 } |
101 } |
104 |
102 |
105 if (lpservlet && lpmodule) { |
103 if (lpservlet && lpmodule) { |
106 final LightPITModule moduleInfo = scclass.getAnnotation(LightPITModule.class); |
104 final LightPITModule moduleInfo = scclass.getAnnotation(LightPITModule.class); |
107 return Optional.of(moduleInfo); |
105 return Optional.of(moduleInfo); |
108 } else { |
106 } else { |
109 return Optional.empty(); |
107 return Optional.empty(); |
113 "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})", |
111 "Servlet registration refers to class {} which cannot be found by the class loader (Reason: {})", |
114 reg.getClassName(), |
112 reg.getClassName(), |
115 ex.getMessage() |
113 ex.getMessage() |
116 ); |
114 ); |
117 return Optional.empty(); |
115 return Optional.empty(); |
118 } |
116 } |
119 } |
117 } |
120 |
118 |
121 private void handleServletRegistration(String name, Registration reg) { |
119 private void handleServletRegistration(String name, Registration reg) { |
122 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); |
120 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); |
123 if (moduleInfo.isPresent()) { |
121 if (moduleInfo.isPresent()) { |
124 registeredModules.put(reg.getClassName(), moduleInfo.get()); |
122 registeredModules.put(reg.getClassName(), moduleInfo.get()); |
125 LOG.info("Module detected: {}", name); |
123 LOG.info("Module detected: {}", name); |
126 } else { |
124 } else { |
127 LOG.debug("Servlet {} is no module, skipping.", name); |
125 LOG.debug("Servlet {} is no module, skipping.", name); |
128 } |
126 } |
129 } |
127 } |
130 |
128 |
131 /** |
129 /** |
132 * Scans for modules and reloads them all. |
130 * Scans for modules and reloads them all. |
133 */ |
131 */ |
134 public void reloadAll() { |
132 public void reloadAll() { |
135 registeredModules.clear(); |
133 registeredModules.clear(); |
136 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
134 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
137 |
135 |
138 // TODO: implement dependency resolver |
136 // TODO: implement dependency resolver |
139 |
137 |
140 dirty.set(true); |
138 dirty.set(true); |
141 LOG.info("Modules loaded."); |
139 LOG.info("Modules loaded."); |
142 } |
140 } |
143 |
141 |
144 /** |
142 /** |
155 */ |
153 */ |
156 public void syncWithDatabase(DatabaseFacade db) { |
154 public void syncWithDatabase(DatabaseFacade db) { |
157 if (dirty.compareAndSet(true, false)) { |
155 if (dirty.compareAndSet(true, false)) { |
158 if (db.getDataSource().isPresent()) { |
156 if (db.getDataSource().isPresent()) { |
159 try (Connection conn = db.getDataSource().get().getConnection()) { |
157 try (Connection conn = db.getDataSource().get().getConnection()) { |
160 final ModuleDao moduleDao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); |
158 db.getDataAccessObjects() |
161 moduleDao.syncRegisteredModuleClasses(conn, registeredModules.entrySet()); |
159 .getModuleDao() |
|
160 .syncRegisteredModuleClasses(conn, registeredModules.entrySet()); |
162 } catch (SQLException ex) { |
161 } catch (SQLException ex) { |
163 LOG.error("Unexpected SQL Exception", ex); |
162 LOG.error("Unexpected SQL Exception", ex); |
164 } |
163 } |
165 } else { |
164 } else { |
166 LOG.error("No datasource present. Cannot sync module information with database."); |
165 LOG.error("No datasource present. Cannot sync module information with database."); |
167 } |
166 } |
168 } else { |
167 } else { |
169 LOG.trace("Module information clean - no synchronization required."); |
168 LOG.trace("Module information clean - no synchronization required."); |
170 } |
169 } |
171 } |
170 } |
172 |
171 |
173 /** |
172 /** |
174 * Unloads all found modules. |
173 * Unloads all found modules. |
175 */ |
174 */ |
176 public void unloadAll() { |
175 public void unloadAll() { |
177 registeredModules.clear(); |
176 registeredModules.clear(); |
178 LOG.info("All modules unloaded."); |
177 LOG.info("All modules unloaded."); |
179 } |
178 } |
180 |
179 |
181 /** |
180 /** |
182 * Returns the main menu. |
181 * Returns the main menu. |
183 * |
182 * |
184 * @param db the interface to the database |
183 * @param db the interface to the database |
185 * @return a list of menus belonging to the main menu |
184 * @return a list of menus belonging to the main menu |
186 */ |
185 */ |
187 public List<Menu> getMainMenu(DatabaseFacade db) { |
186 public List<Menu> getMainMenu(DatabaseFacade db) { |
188 // TODO: user specific menu |
187 // TODO: user specific menu |
189 |
188 |
190 if (db.getDataSource().isPresent()) { |
189 if (db.getDataSource().isPresent()) { |
191 try (Connection conn = db.getDataSource().get().getConnection()) { |
190 try (Connection conn = db.getDataSource().get().getConnection()) { |
192 final ModuleDao dao = CoreDAOFactory.getModuleDao(db.getSQLDialect()); |
191 final List<Module> modules = db.getDataAccessObjects().getModuleDao().list(conn); |
193 final List<Module> modules = dao.listAll(conn); |
|
194 |
192 |
195 return modules |
193 return modules |
196 .stream() |
194 .stream() |
197 .filter(Module::isVisible) |
195 .filter(Module::isVisible) |
198 .sorted(new Module.PriorityComparator()) |
196 .sorted(new Module.PriorityComparator()) |
209 } |
207 } |
210 } else { |
208 } else { |
211 return Collections.emptyList(); |
209 return Collections.emptyList(); |
212 } |
210 } |
213 } |
211 } |
214 |
212 |
215 /** |
213 /** |
216 * Returns an unmodifiable map of all registered modules. |
214 * Returns an unmodifiable map of all registered modules. |
217 * |
215 * <p> |
218 * The key is the classname of the module. |
216 * The key is the classname of the module. |
219 * |
217 * |
220 * @return the map of registered modules |
218 * @return the map of registered modules |
221 */ |
219 */ |
222 public Map<String, LightPITModule> getRegisteredModules() { |
220 public Map<String, LightPITModule> getRegisteredModules() { |
223 return Collections.unmodifiableMap(registeredModules); |
221 return Collections.unmodifiableMap(registeredModules); |
224 } |
222 } |