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