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 java.sql.Connection; |
|
32 import java.sql.PreparedStatement; |
|
33 import java.sql.ResultSet; |
|
34 import java.sql.SQLException; |
31 import java.util.ArrayList; |
35 import java.util.ArrayList; |
32 import java.util.Collections; |
36 import java.util.Collections; |
|
37 import java.util.HashMap; |
33 import java.util.List; |
38 import java.util.List; |
34 import java.util.Objects; |
39 import java.util.Map; |
|
40 import java.util.Map.Entry; |
35 import java.util.Optional; |
41 import java.util.Optional; |
|
42 import java.util.concurrent.CopyOnWriteArrayList; |
|
43 import java.util.concurrent.atomic.AtomicBoolean; |
36 import javax.servlet.Registration; |
44 import javax.servlet.Registration; |
37 import javax.servlet.ServletContext; |
45 import javax.servlet.ServletContext; |
38 import javax.servlet.ServletContextEvent; |
46 import javax.servlet.ServletContextEvent; |
39 import javax.servlet.ServletContextListener; |
47 import javax.servlet.ServletContextListener; |
40 import javax.servlet.annotation.WebListener; |
48 import javax.servlet.annotation.WebListener; |
53 * The attribute name in the servlet context under which an instance of this class can be found. |
61 * The attribute name in the servlet context under which an instance of this class can be found. |
54 */ |
62 */ |
55 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
63 public static final String SC_ATTR_NAME = ModuleManager.class.getName(); |
56 private ServletContext sc; |
64 private ServletContext sc; |
57 |
65 |
58 private final List<Menu> mainMenu = new ArrayList<>(); |
66 /** |
|
67 * This flag is true, when synchronization is needed. |
|
68 */ |
|
69 private AtomicBoolean dirty = new AtomicBoolean(true); |
|
70 |
|
71 private final CopyOnWriteArrayList<Menu> mainMenu = new CopyOnWriteArrayList<>(); |
59 private final List<Menu> immutableMainMenu = Collections.unmodifiableList(mainMenu); |
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<>(); |
60 |
78 |
61 @Override |
79 @Override |
62 public void contextInitialized(ServletContextEvent sce) { |
80 public void contextInitialized(ServletContextEvent sce) { |
63 sc = sce.getServletContext(); |
81 sc = sce.getServletContext(); |
64 reloadAll(); |
82 reloadAll(); |
106 ); |
124 ); |
107 return Optional.empty(); |
125 return Optional.empty(); |
108 } |
126 } |
109 } |
127 } |
110 |
128 |
111 private void addModuleToMenu(String moduleClassName, LightPITModule moduleInfo) { |
|
112 final Menu menu = new Menu( |
|
113 moduleClassName, |
|
114 new ResourceKey(moduleInfo.bundleBaseName(), moduleInfo.menuKey()), |
|
115 moduleInfo.modulePath() |
|
116 ); |
|
117 mainMenu.add(menu); |
|
118 } |
|
119 |
|
120 private void handleServletRegistration(String name, Registration reg) { |
129 private void handleServletRegistration(String name, Registration reg) { |
121 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); |
130 final Optional<LightPITModule> moduleInfo = getModuleInfo(reg); |
122 if (moduleInfo.isPresent()) { |
131 if (moduleInfo.isPresent()) { |
123 // TODO: implement dependency resolver |
132 registeredModules.put(reg.getClassName(), moduleInfo.get()); |
124 |
|
125 if (!moduleInfo.get().menuKey().isEmpty()) { |
|
126 addModuleToMenu(reg.getClassName(), moduleInfo.get()); |
|
127 } |
|
128 |
|
129 LOG.info("Module detected: {}", name); |
133 LOG.info("Module detected: {}", name); |
130 } else { |
134 } else { |
131 LOG.debug("Servlet {} is no module, skipping.", name); |
135 LOG.debug("Servlet {} is no module, skipping.", name); |
132 } |
136 } |
133 } |
137 } |
135 /** |
139 /** |
136 * Scans for modules and reloads them all. |
140 * Scans for modules and reloads them all. |
137 */ |
141 */ |
138 public void reloadAll() { |
142 public void reloadAll() { |
139 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
143 sc.getServletRegistrations().forEach(this::handleServletRegistration); |
|
144 |
|
145 // TODO: implement dependency resolver |
|
146 |
140 LOG.info("Modules loaded."); |
147 LOG.info("Modules loaded."); |
|
148 } |
|
149 |
|
150 /** |
|
151 * Synchronizes module information with the database. |
|
152 * |
|
153 * @param db interface to the database |
|
154 */ |
|
155 public void syncWithDatabase(DatabaseFacade db) { |
|
156 if (dirty.compareAndSet(true, false)) { |
|
157 if (db.getDataSource().isPresent()) { |
|
158 try (Connection conn = db.getDataSource().get().getConnection()) { |
|
159 PreparedStatement |
|
160 check = conn.prepareStatement("SELECT visible FROM lpitcore_module WHERE classname = ?"), |
|
161 insert = conn.prepareStatement("INSERT INTO lpitcore_module (classname, visible) VALUES (?, ?)"); |
|
162 insert.setBoolean(2, true); |
|
163 // update/delete not required, we do this in the module management UI |
|
164 |
|
165 final List<Menu> updatedMenu = new ArrayList<>(); |
|
166 |
|
167 for (Entry<String, LightPITModule> mod : registeredModules.entrySet()) { |
|
168 if (mod.getValue().systemModule()) continue; |
|
169 |
|
170 check.setString(1, mod.getKey()); |
|
171 try (ResultSet r = check.executeQuery()) { |
|
172 final boolean addToMenu; |
|
173 if (r.next()) { |
|
174 addToMenu = r.getBoolean(1); |
|
175 } else { |
|
176 insert.setString(1, mod.getKey()); |
|
177 insert.executeUpdate(); |
|
178 addToMenu = !mod.getValue().menuKey().isEmpty(); |
|
179 } |
|
180 if (addToMenu) { |
|
181 updatedMenu.add(new Menu( |
|
182 mod.getKey(), |
|
183 new ResourceKey(mod.getValue().bundleBaseName(), mod.getValue().menuKey()), |
|
184 mod.getValue().modulePath() |
|
185 )); |
|
186 } |
|
187 } |
|
188 } |
|
189 |
|
190 mainMenu.removeIf((e) -> !updatedMenu.contains(e)); |
|
191 mainMenu.addAllAbsent(updatedMenu); |
|
192 } catch (SQLException ex) { |
|
193 LOG.error("Unexpected SQL Exception", ex); |
|
194 } |
|
195 } else { |
|
196 LOG.warn("No datasource present. Cannot sync module information with database."); |
|
197 } |
|
198 } else { |
|
199 LOG.debug("Module information clean - no synchronization required."); |
|
200 } |
141 } |
201 } |
142 |
202 |
143 /** |
203 /** |
144 * Unloads all found modules. |
204 * Unloads all found modules. |
145 */ |
205 */ |