]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/osgi/DeployConfig.java
Working UUID factory
[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.Dictionary;
10 import java.util.List;
11 import java.util.SortedMap;
12 import java.util.TreeMap;
13
14 import javax.naming.InvalidNameException;
15 import javax.naming.directory.Attribute;
16 import javax.naming.directory.Attributes;
17 import javax.naming.directory.BasicAttributes;
18 import javax.naming.ldap.LdapName;
19 import javax.naming.ldap.Rdn;
20
21 import org.argeo.api.cms.CmsConstants;
22 import org.argeo.api.cms.CmsLog;
23 import org.argeo.cms.internal.runtime.InitUtils;
24 import org.argeo.cms.internal.runtime.KernelConstants;
25 import org.argeo.cms.internal.runtime.KernelUtils;
26 import org.argeo.osgi.useradmin.UserAdminConf;
27 import org.argeo.util.naming.AttributesDictionary;
28 import org.argeo.util.naming.LdifParser;
29 import org.argeo.util.naming.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 import org.osgi.service.cm.ConfigurationListener;
35
36 /** Manages the LDIF-based deployment configuration. */
37 public class DeployConfig implements ConfigurationListener {
38 private final CmsLog log = CmsLog.getLog(getClass());
39 // private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
40
41 private static Path deployConfigPath = KernelUtils.getOsgiInstancePath(KernelConstants.DEPLOY_CONFIG_PATH);
42 private SortedMap<LdapName, Attributes> deployConfigs = new TreeMap<>();
43 // private final DataModels dataModels;
44
45 private boolean isFirstInit = false;
46
47 private final static String ROLES = "roles";
48
49 private ConfigurationAdmin configurationAdmin;
50
51 public DeployConfig() {
52 // this.dataModels = dataModels;
53 // ConfigurationAdmin configurationAdmin =
54 // // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
55 // try {
56 // if (!isInitialized()) { // first init
57 // isFirstInit = true;
58 // firstInit();
59 // }
60 // this.configurationAdmin = configurationAdmin;
61 //// init(configurationAdmin, isClean, isFirstInit);
62 // } catch (IOException e) {
63 // throw new RuntimeException("Could not init deploy configs", e);
64 // }
65 // FIXME check race conditions during initialization
66 // bc.registerService(ConfigurationListener.class, this, null);
67 }
68
69 private void firstInit() throws IOException {
70 log.info("## FIRST INIT ##");
71 Files.createDirectories(deployConfigPath.getParent());
72
73 // FirstInit firstInit = new FirstInit();
74 InitUtils.prepareFirstInitInstanceArea();
75
76 if (!Files.exists(deployConfigPath))
77 deployConfigs = new TreeMap<>();
78 else// config file could have juste been copied by preparation
79 try (InputStream in = Files.newInputStream(deployConfigPath)) {
80 deployConfigs = new LdifParser().read(in);
81 }
82 save();
83 }
84
85 private void setFromFrameworkProperties(boolean isFirstInit) {
86
87 // user admin
88 List<Dictionary<String, Object>> userDirectoryConfigs = InitUtils.getUserDirectoryConfigs();
89 if (userDirectoryConfigs.size() != 0) {
90 List<String> activeCns = new ArrayList<>();
91 for (int i = 0; i < userDirectoryConfigs.size(); i++) {
92 Dictionary<String, Object> userDirectoryConfig = userDirectoryConfigs.get(i);
93 String baseDn = (String) userDirectoryConfig.get(UserAdminConf.baseDn.name());
94 String cn;
95 if (CmsConstants.ROLES_BASEDN.equals(baseDn))
96 cn = ROLES;
97 else
98 cn = UserAdminConf.baseDnHash(userDirectoryConfig);
99 activeCns.add(cn);
100 userDirectoryConfig.put(CmsConstants.CN, cn);
101 putFactoryDeployConfig(CmsConstants.NODE_USER_ADMIN_PID, userDirectoryConfig);
102 }
103 // disable others
104 LdapName userAdminFactoryName = serviceFactoryDn(CmsConstants.NODE_USER_ADMIN_PID);
105 for (LdapName name : deployConfigs.keySet()) {
106 if (name.startsWith(userAdminFactoryName) && !name.equals(userAdminFactoryName)) {
107 // try {
108 Attributes attrs = deployConfigs.get(name);
109 String cn = name.getRdn(name.size() - 1).getValue().toString();
110 if (!activeCns.contains(cn)) {
111 attrs.put(UserAdminConf.disabled.name(), "true");
112 }
113 // } catch (Exception e) {
114 // throw new CmsException("Cannot disable user directory " + name, e);
115 // }
116 }
117 }
118 }
119
120 // http server
121 Dictionary<String, Object> webServerConfig = InitUtils
122 .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
123 if (!webServerConfig.isEmpty()) {
124 // TODO check for other customizers
125 // webServerConfig.put("customizer.class", "org.argeo.equinox.jetty.CmsJettyCustomizer");
126 putFactoryDeployConfig(KernelConstants.JETTY_FACTORY_PID, webServerConfig);
127 }
128 // LdapName defaultHttpServiceDn = serviceDn(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT);
129 // if (deployConfigs.containsKey(defaultHttpServiceDn)) {
130 // // remove old default configs since we have now to start Jetty servlet bridge
131 // // indirectly
132 // deployConfigs.remove(defaultHttpServiceDn);
133 // }
134
135 // SAVE
136 save();
137 //
138
139 // Dictionary<String, Object> webServerConfig = InitUtils
140 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, CmsConstants.DEFAULT));
141 }
142
143 public void start() throws IOException {
144 if (!isInitialized()) { // first init
145 isFirstInit = true;
146 firstInit();
147 }
148
149 boolean isClean;
150 try {
151 Configuration[] confs = configurationAdmin
152 .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
153 isClean = confs == null || confs.length == 0;
154 } catch (Exception e) {
155 throw new IllegalStateException("Cannot analyse clean state", e);
156 }
157
158 try (InputStream in = Files.newInputStream(deployConfigPath)) {
159 deployConfigs = new LdifParser().read(in);
160 }
161 if (isClean) {
162 if (log.isDebugEnabled())
163 log.debug("Clean state, loading from framework properties...");
164 setFromFrameworkProperties(isFirstInit);
165 loadConfigs();
166 }
167 // TODO check consistency if not clean
168 }
169
170 public void stop() {
171
172 }
173
174 public void loadConfigs() throws IOException {
175 // FIXME make it more robust
176 Configuration systemRolesConf = null;
177 LdapName systemRolesDn;
178 try {
179 // FIXME make it more robust
180 systemRolesDn = new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
181 } catch (InvalidNameException e) {
182 throw new IllegalArgumentException(e);
183 }
184 deployConfigs: for (LdapName dn : deployConfigs.keySet()) {
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(deployConfigs.get(dn));
193 conf.update(dico);
194 } else {
195 // service factory definition
196 }
197 } else {
198 Attributes config = deployConfigs.get(dn);
199 Attribute disabled = config.get(UserAdminConf.disabled.name());
200 if (disabled != null)
201 continue deployConfigs;
202 // service factory service
203 Rdn beforeLastRdn = dn.getRdn(dn.size() - 2);
204 assert beforeLastRdn.getType().equals(CmsConstants.OU);
205 String factoryPid = beforeLastRdn.getValue().toString();
206 Configuration conf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
207 if (systemRolesDn.equals(dn)) {
208 systemRolesConf = configurationAdmin.createFactoryConfiguration(factoryPid.toString(), null);
209 } else {
210 AttributesDictionary dico = new AttributesDictionary(config);
211 conf.update(dico);
212 }
213 }
214 }
215
216 // system roles must be last since it triggers node user admin publication
217 if (systemRolesConf == null)
218 throw new IllegalStateException("System roles are not configured.");
219 systemRolesConf.update(new AttributesDictionary(deployConfigs.get(systemRolesDn)));
220
221 }
222
223 @Override
224 public void configurationEvent(ConfigurationEvent event) {
225 try {
226 if (ConfigurationEvent.CM_UPDATED == event.getType()) {
227 Configuration conf = configurationAdmin.getConfiguration(event.getPid(), null);
228 LdapName serviceDn = null;
229 String factoryPid = conf.getFactoryPid();
230 if (factoryPid != null) {
231 LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
232 if (deployConfigs.containsKey(serviceFactoryDn)) {
233 for (LdapName dn : deployConfigs.keySet()) {
234 if (dn.startsWith(serviceFactoryDn)) {
235 Rdn lastRdn = dn.getRdn(dn.size() - 1);
236 assert lastRdn.getType().equals(CmsConstants.CN);
237 Object value = conf.getProperties().get(lastRdn.getType());
238 assert value != null;
239 if (value.equals(lastRdn.getValue())) {
240 serviceDn = dn;
241 break;
242 }
243 }
244 }
245
246 Object cn = conf.getProperties().get(CmsConstants.CN);
247 if (cn == null)
248 throw new IllegalArgumentException("Properties must contain cn");
249 if (serviceDn == null) {
250 putFactoryDeployConfig(factoryPid, conf.getProperties());
251 } else {
252 Attributes attrs = deployConfigs.get(serviceDn);
253 assert attrs != null;
254 AttributesDictionary.copy(conf.getProperties(), attrs);
255 }
256 save();
257 if (log.isDebugEnabled())
258 log.debug("Updated deploy config " + serviceDn(factoryPid, cn.toString()));
259 } else {
260 // ignore non config-registered service factories
261 }
262 } else {
263 serviceDn = serviceDn(event.getPid());
264 if (deployConfigs.containsKey(serviceDn)) {
265 Attributes attrs = deployConfigs.get(serviceDn);
266 assert attrs != null;
267 AttributesDictionary.copy(conf.getProperties(), attrs);
268 save();
269 if (log.isDebugEnabled())
270 log.debug("Updated deploy config " + serviceDn);
271 } else {
272 // ignore non config-registered services
273 }
274 }
275 }
276 } catch (Exception e) {
277 log.error("Could not handle configuration event", e);
278 }
279 }
280
281 public void putFactoryDeployConfig(String factoryPid, Dictionary<String, Object> props) {
282 Object cn = props.get(CmsConstants.CN);
283 if (cn == null)
284 throw new IllegalArgumentException("cn must be set in properties");
285 LdapName serviceFactoryDn = serviceFactoryDn(factoryPid);
286 if (!deployConfigs.containsKey(serviceFactoryDn))
287 deployConfigs.put(serviceFactoryDn, new BasicAttributes(CmsConstants.OU, factoryPid));
288 LdapName serviceDn = serviceDn(factoryPid, cn.toString());
289 Attributes attrs = new BasicAttributes();
290 AttributesDictionary.copy(props, attrs);
291 deployConfigs.put(serviceDn, attrs);
292 }
293
294 void putDeployConfig(String servicePid, Dictionary<String, Object> props) {
295 LdapName serviceDn = serviceDn(servicePid);
296 Attributes attrs = new BasicAttributes(CmsConstants.CN, servicePid);
297 AttributesDictionary.copy(props, attrs);
298 deployConfigs.put(serviceDn, attrs);
299 }
300
301 public void save() {
302 try (Writer writer = Files.newBufferedWriter(deployConfigPath)) {
303 new LdifWriter(writer).write(deployConfigs);
304 } catch (IOException e) {
305 // throw new CmsException("Cannot save deploy configs", e);
306 log.error("Cannot save deploy configs", e);
307 }
308 }
309
310 public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) {
311 this.configurationAdmin = configurationAdmin;
312 }
313
314 public boolean hasDomain() {
315 Configuration[] configs;
316 try {
317 configs = configurationAdmin
318 .listConfigurations("(service.factoryPid=" + CmsConstants.NODE_USER_ADMIN_PID + ")");
319 } catch (IOException | InvalidSyntaxException e) {
320 throw new IllegalStateException("Cannot list user directories", e);
321 }
322
323 boolean hasDomain = false;
324 for (Configuration config : configs) {
325 Object realm = config.getProperties().get(UserAdminConf.realm.name());
326 if (realm != null) {
327 log.debug("Found realm: " + realm);
328 hasDomain = true;
329 }
330 }
331 return hasDomain;
332 }
333
334 /*
335 * UTILITIES
336 */
337 private LdapName serviceFactoryDn(String factoryPid) {
338 try {
339 return new LdapName(CmsConstants.OU + "=" + factoryPid + "," + CmsConstants.DEPLOY_BASEDN);
340 } catch (InvalidNameException e) {
341 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid, e);
342 }
343 }
344
345 private LdapName serviceDn(String servicePid) {
346 try {
347 return new LdapName(CmsConstants.CN + "=" + servicePid + "," + CmsConstants.DEPLOY_BASEDN);
348 } catch (InvalidNameException e) {
349 throw new IllegalArgumentException("Cannot generate DN from " + servicePid, e);
350 }
351 }
352
353 private LdapName serviceDn(String factoryPid, String cn) {
354 try {
355 return (LdapName) serviceFactoryDn(factoryPid).add(new Rdn(CmsConstants.CN, cn));
356 } catch (InvalidNameException e) {
357 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid + " and " + cn, e);
358 }
359 }
360
361 public Dictionary<String, Object> getProps(String factoryPid, String cn) {
362 Attributes attrs = deployConfigs.get(serviceDn(factoryPid, cn));
363 if (attrs != null)
364 return new AttributesDictionary(attrs);
365 else
366 return null;
367 }
368
369 private static boolean isInitialized() {
370 return Files.exists(deployConfigPath);
371 }
372
373 public boolean isFirstInit() {
374 return isFirstInit;
375 }
376
377 }