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