]> git.argeo.org Git - lgpl/argeo-commons.git/blob - DeployConfig.java
7f8db5a062cd3fca9bd110cf7ff5e250a91a6505
[lgpl/argeo-commons.git] / DeployConfig.java
1 package org.argeo.cms.internal.osgi;
2
3 import java.io.IOException;
4 import java.io.InputStream;
5 import java.io.Writer;
6 import java.nio.file.Files;
7 import java.nio.file.Path;
8 import java.util.ArrayList;
9 import java.util.Arrays;
10 import java.util.Dictionary;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Set;
14 import java.util.SortedMap;
15 import java.util.TreeMap;
16
17 import javax.naming.InvalidNameException;
18 import javax.naming.directory.Attribute;
19 import javax.naming.directory.Attributes;
20 import javax.naming.directory.BasicAttributes;
21 import javax.naming.ldap.LdapName;
22 import javax.naming.ldap.Rdn;
23
24 import org.argeo.api.cms.CmsConstants;
25 import org.argeo.api.cms.CmsLog;
26 import org.argeo.util.directory.DirectoryConf;
27 import org.argeo.util.directory.ldap.AttributesDictionary;
28 import org.argeo.util.directory.ldap.LdifParser;
29 import org.argeo.util.directory.ldap.LdifWriter;
30 import org.osgi.framework.InvalidSyntaxException;
31 import org.osgi.service.cm.Configuration;
32 import org.osgi.service.cm.ConfigurationAdmin;
33 import org.osgi.service.cm.ConfigurationEvent;
34
35 /** Manages the LDIF-based deployment configuration. */
36 @Deprecated
37 public class DeployConfig {
38
39 private final CmsLog log = CmsLog.getLog(getClass());
40 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
41
42 private static Path deployConfigPath;// = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
43 private SortedMap<LdapName, Attributes> deployConfigs = new TreeMap<>();
44 // private final DataModels dataModels;
45
46 private boolean isFirstInit = false;
47
48 private final static String ROLES = "roles";
49
50 private ConfigurationAdmin configurationAdmin;
51
52 private void firstInit() throws IOException {
53 log.info("## FIRST INIT ##");
54 Files.createDirectories(deployConfigPath.getParent());
55
56 // FirstInit firstInit = new FirstInit();
57 //InitUtils.prepareFirstInitInstanceArea();
58
59 if (!Files.exists(deployConfigPath))
60 deployConfigs = new TreeMap<>();
61 else// config file could have juste been copied by preparation
62 try (InputStream in = Files.newInputStream(deployConfigPath)) {
63 deployConfigs = new LdifParser().read(in);
64 }
65 save();
66 }
67
68 private void setFromFrameworkProperties(boolean isFirstInit) {
69
70 // user admin
71 // List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
72 // if (userDirectoryConfigs.size() != 0) {
73 // List<String> activeCns = new ArrayList<>();
74 // for (int i = 0; i < userDirectoryConfigs.size(); i++) {
75 // Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
76 // String baseDn = (String) userDirectoryConfig.get(DirectoryConf.baseDn.name());
77 // String cn;
78 // if (CmsConstants.ROLES_BASEDN.equals(baseDn))
79 // cn = ROLES;
80 // else
81 // cn = DirectoryConf.baseDnHash(userDirectoryConfig);
82 // activeCns.add(cn);
83 // userDirectoryConfig.put(CmsConstants.CN, cn);
84 // putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
85 // }
86 // // disable others
87 // LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID);
88 // for (LdapName name : deployConfigs.keySet()) {
89 // if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) {
90 //// try {
91 // Attributes attrs = deployConfigs.get(name);
92 // String cn = name.getRdn(name.size() - 1).getValue().toString();
93 // if (!activeCns.contains(cn)) {
94 // attrs.put(DirectoryConf.disabled.name(), "true");
95 // }
96 //// } catch (Exception e) {
97 //// throw new CmsException("Cannot disable user directory " + name, e);
98 //// }
99 // }
100 // }
101 // }
102
103 // http server
104 // Dictionary<String, Object> webServerConfig = InitUtils
105 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
106 // if (!webServerConfig.isEmpty()) {
107 // // TODO check for other customizers
108 // putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
109 // }
110
111 // SAVE
112 save();
113 //
114
115 }
116
117 public void start() {
118 try {
119 if (!isInitialized()) { // first init
120 isFirstInit = true;
121 firstInit();
122 }
123
124 boolean isClean = true;
125 if (configurationAdmin != null)
126 try {
127 Configuration[] confs = configurationAdmin
128 .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
129 isClean = confs == null || confs.length == 0;
130 } catch (Exception e) {
131 throw new IllegalStateException("Cannot analyse clean state", e);
132 }
133
134 try (InputStream in = Files.newInputStream(deployConfigPath)) {
135 deployConfigs = new LdifParser().read(in);
136 }
137 if (isClean) {
138 if (log.isDebugEnabled())
139 log.debug("Clean state, loading from framework properties...");
140 setFromFrameworkProperties(isFirstInit);
141 if (configurationAdmin != null)
142 loadConfigs();
143 }
144 // TODO check consistency if not clean
145 } catch (IOException e) {
146 throw new RuntimeException("Cannot load deploy configuration", e);
147 }
148 }
149
150 public void stop() {
151
152 }
153
154 protected void logAllConfigurations() {
155 if (!log.isDebugEnabled())
156 return;
157 try {
158 Configuration[] configurations = configurationAdmin.listConfigurations(null);
159 if (configurations == null) {
160 log.debug("No configuration available");
161 return;
162 }
163 Arrays.sort(configurations, (o1, o2) -> o1.getPid().compareTo(o2.getPid()));
164 for (Configuration configuration : configurations) {
165 log.debug(configuration.getFactoryPid() + " - " + configuration.getPid() + " - "
166 + configuration.getProperties());
167 }
168 } catch (IOException | InvalidSyntaxException e) {
169 throw new IllegalStateException("Cannot log configurations", e);
170 }
171 }
172
173 public void loadConfigs() throws IOException {
174 // FIXME make it more robust
175 Configuration systemRolesConf = null;
176 LdapName systemRolesDn;
177 try {
178 // FIXME make it more robust
179 systemRolesDn = new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
180 } catch (InvalidNameException e) {
181 throw new IllegalArgumentException(e);
182 }
183 deployConfigs: for (LdapName dn : deployConfigs.keySet()) {
184 Attributes deployConfig = deployConfigs.get(dn);
185 Rdn lastRdn = dn.getRdn(dn.size() - 1);
186 LdapName prefix = (LdapName) dn.getPrefix(dn.size() - 1);
187 if (prefix.toString().equals(CmsConstants.DEPLOY_BASEDN)) {
188 if (lastRdn.getType().equals(CmsConstants.CN)) {
189 // service
190 String pid = lastRdn.getValue().toString();
191 Configuration conf = configurationAdmin.getConfiguration(pid);
192 AttributesDictionary dico = new AttributesDictionary(deployConfig);
193 conf.update(dico);
194 } else {
195 // service factory definition
196 }
197 } else {
198 Attribute disabled = deployConfig.get(DirectoryConf.disabled.name());
199 if (disabled != null)
200 continue deployConfigs;
201 // service factory service
202 if (!lastRdn.getType().equals(CmsConstants.CN))
203 throw new IllegalStateException("Only " + CmsConstants.CN + "= is supported: " + dn);
204 Rdn beforeLastRdn = dn.getRdn(dn.size() - 2);
205 assert beforeLastRdn.getType().equals(CmsConstants.OU);
206 String factoryPid = beforeLastRdn.getValue().toString();
207
208 String cn = lastRdn.getValue().toString();
209 Configuration conf = getSingleServiceConfiguration(factoryPid, cn);
210 if (conf != null) {
211 if (systemRolesDn.equals(dn))
212 systemRolesConf = conf;
213 // TODO deal with modifications
214 // boolean modified = false;
215 // Dictionary<String, Object> currentProperties = conf.getProperties();
216 //
217 // attrs: for (NamingEnumeration<? extends Attribute> it = deployConfig.getAll(); it
218 // .hasMoreElements();) {
219 // Attribute attr = (Attribute) it.next();
220 // String key = attr.getID();
221 // Object currentValue = currentProperties.get(key);
222 // if (currentValue == null) {
223 // modified = true;
224 // break attrs;
225 // }
226 // }
227
228 // AttributesDictionary dico = new AttributesDictionary(deployConfig);
229 // conf.update(dico);
230 } else {
231
232 conf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
233 if (systemRolesDn.equals(dn)) {
234 systemRolesConf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
235 } else {
236 AttributesDictionary dico = new AttributesDictionary(deployConfig);
237 conf.update(dico);
238 }
239 }
240 }
241 }
242
243 // system roles must be last since it triggers node user admin publication
244 if (systemRolesConf == null)
245 throw new IllegalStateException("System roles are not configured.");
246 systemRolesConf.update(new AttributesDictionary(deployConfigs.get(systemRolesDn)));
247
248 // logAllConfigurations();
249 }
250
251 public Set<Dictionary<String, Object>> getUserDirectoryConfigs() {
252 // not static because class is not supported by Android
253 final LdapName USER_ADMIN_BASE_DN;
254 try {
255 USER_ADMIN_BASE_DN = new LdapName(
256 CmsConstants.OU + "=" + CmsConstants.NODE_USER_ADMIN_PID + "," + CmsConstants.DEPLOY_BASEDN);
257 } catch (InvalidNameException e) {
258 throw new IllegalArgumentException(e);
259 }
260 Set<Dictionary<String, Object>> res = new HashSet<>();
261 for (LdapName dn : deployConfigs.keySet()) {
262 if (dn.endsWith(USER_ADMIN_BASE_DN)) {
263 Attributes attributes = deployConfigs.get(dn);
264 res.add(new AttributesDictionary(attributes));
265 }
266 }
267 return res;
268 }
269
270 // @Override
271 public void configurationEvent(ConfigurationEvent event) {
272 try {
273 if (ConfigurationEvent.CM_UPDATED == event.getType()) {
274 Configuration conf = configurationAdmin.getConfiguration(event.getPid(), null);
275 LdapName serviceDn = null;
276 String factoryPid = conf.getFactoryPid();
277 if (factoryPid != null) {
278 LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
279 if (deployConfigs.containsKey(serviceFactoryDn)) {
280 for (LdapName dn : deployConfigs.keySet()) {
281 if (dn.startsWith(serviceFactoryDn)) {
282 Rdn lastRdn = dn.getRdn(dn.size() - 1);
283 assert lastRdn.getType().equals(CmsConstants.CN);
284 Object value = conf.getProperties().get(lastRdn.getType());
285 assert value != null;
286 if (value.equals(lastRdn.getValue())) {
287 serviceDn = dn;
288 break;
289 }
290 }
291 }
292
293 Object cn = conf.getProperties().get(CmsConstants.CN);
294 if (cn == null)
295 throw new IllegalArgumentException("Properties must contain cn");
296 if (serviceDn == null) {
297 putFactoryDeployConfig(factoryPid, conf.getProperties());
298 } else {
299 Attributes attrs = deployConfigs.get(serviceDn);
300 assert attrs != null;
301 AttributesDictionary.copy(conf.getProperties(), attrs);
302 }
303 save();
304 if (log.isDebugEnabled())
305 log.debug("Updated deploy config " + serviceDn(factoryPid, cn.toString()));
306 } else {
307 // ignore non config-registered service factories
308 }
309 } else {
310 serviceDn = serviceDn(event.getPid());
311 if (deployConfigs.containsKey(serviceDn)) {
312 Attributes attrs = deployConfigs.get(serviceDn);
313 assert attrs != null;
314 AttributesDictionary.copy(conf.getProperties(), attrs);
315 save();
316 if (log.isDebugEnabled())
317 log.debug("Updated deploy config " + serviceDn);
318 } else {
319 // ignore non config-registered services
320 }
321 }
322 }
323 } catch (Exception e) {
324 log.error("Could not handle configuration event", e);
325 }
326 }
327
328 public void putFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
329 Object cn = props.get(CmsConstants.CN);
330 if (cn == null)
331 throw new IllegalArgumentException("cn must be set in properties");
332 LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
333 if (!deployConfigs.containsKey(serviceFactoryDn))
334 deployConfigs.put(serviceFactoryDn, new BasicAttributes(CmsConstants.OU, factoryPid));
335 LdapName serviceDn = serviceDn(factoryPid, cn.toString());
336 Attributes attrs = new BasicAttributes();
337 AttributesDictionary.copy(props, attrs);
338 deployConfigs.put(serviceDn, attrs);
339 }
340
341 void putDeployConfig(String servicePid, Dictionary<String, Object> props) {
342 LdapName serviceDn = serviceDn(servicePid);
343 Attributes attrs = new BasicAttributes(CmsConstants.CN, servicePid);
344 AttributesDictionary.copy(props, attrs);
345 deployConfigs.put(serviceDn, attrs);
346 }
347
348 public void save() {
349 try (Writer writer = Files.newBufferedWriter(deployConfigPath)) {
350 new LdifWriter(writer).write(deployConfigs);
351 } catch (IOException e) {
352 // throw new CmsException("Cannot save deploy configs", e);
353 log.error("Cannot save deploy configs", e);
354 }
355 }
356
357 public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
358 this.configurationAdmin = configurationAdmin;
359 }
360
361 public boolean hasDomain() {
362 // FIXME lookup deploy configs directly
363 if (configurationAdmin == null)
364 return false;
365
366 Configuration[] configs = listConfigurationsByFactory(CmsConstants.NODE_USER_ADMIN_PID);
367
368 boolean hasDomain = false;
369 for (Configuration config : configs) {
370 Object realm = config.getProperties().get(DirectoryConf.realm.name());
371 if (realm != null) {
372 log.debug("Found realm: " + realm);
373 hasDomain = true;
374 }
375 }
376 return hasDomain;
377 }
378
379 private Configuration[] listConfigurationsByFactory(String factoryPid) {
380 try {
381 Configuration[] configs = configurationAdmin.listConfigurations("(service.factoryPid=" + factoryPid + ")");
382 if (configs == null)
383 configs = new Configuration[0];
384 return configs;
385 } catch (IOException | InvalidSyntaxException e) {
386 throw new IllegalStateException("Cannot list configurations with factoryPid " + factoryPid, e);
387 }
388
389 }
390
391 private Configuration getSingleServiceConfiguration(String factoryPid, String cn) {
392 Configuration[] configs = listConfigurationsByFactory(factoryPid);
393 List<Configuration> res = new ArrayList<>();
394 for (Configuration config : configs) {
395 Object currentCn = config.getProperties().get(CmsConstants.CN);
396 if (currentCn != null && cn.equals(currentCn.toString()))
397 res.add(config);
398 }
399 if (res.size() == 0)
400 return null;
401 if (res.size() > 1)
402 throw new IllegalStateException(
403 "More than one " + factoryPid + " configuration returned for " + CmsConstants.CN + "=" + cn);
404 return res.get(0);
405 }
406
407 /*
408 * UTILITIES
409 */
410 private LdapName serviceFactoryDn(String factoryPid) {
411 try {
412 return new LdapName(CmsConstants.OU + "=" + factoryPid + "," + CmsConstants.DEPLOY_BASEDN);
413 } catch (InvalidNameException e) {
414 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid, e);
415 }
416 }
417
418 private LdapName serviceDn(String servicePid) {
419 try {
420 return new LdapName(CmsConstants.CN + "=" + servicePid + "," + CmsConstants.DEPLOY_BASEDN);
421 } catch (InvalidNameException e) {
422 throw new IllegalArgumentException("Cannot generate DN from " + servicePid, e);
423 }
424 }
425
426 private LdapName serviceDn(String factoryPid, String cn) {
427 try {
428 return (LdapName) serviceFactoryDn(factoryPid).add(new Rdn(CmsConstants.CN, cn));
429 } catch (InvalidNameException e) {
430 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid + " and " + cn, e);
431 }
432 }
433
434 public Dictionary<String, Object> getProps(String factoryPid, String cn) {
435 Attributes attrs = deployConfigs.get(serviceDn(factoryPid, cn));
436 if (attrs != null)
437 return new AttributesDictionary(attrs);
438 else
439 return null;
440 }
441
442 private static boolean isInitialized() {
443 return Files.exists(deployConfigPath);
444 }
445
446 public boolean isFirstInit() {
447 return isFirstInit;
448 }
449
450 }