1 package org
.argeo
.cms
.internal
.kernel
;
3 import java
.io
.IOException
;
4 import java
.io
.InputStream
;
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
;
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
;
21 import org
.apache
.commons
.logging
.Log
;
22 import org
.apache
.commons
.logging
.LogFactory
;
23 import org
.argeo
.api
.NodeConstants
;
24 import org
.argeo
.naming
.AttributesDictionary
;
25 import org
.argeo
.naming
.LdifParser
;
26 import org
.argeo
.naming
.LdifWriter
;
27 import org
.argeo
.osgi
.useradmin
.UserAdminConf
;
28 import org
.eclipse
.equinox
.http
.jetty
.JettyConfigurator
;
29 import org
.osgi
.framework
.BundleContext
;
30 import org
.osgi
.framework
.FrameworkUtil
;
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
;
36 /** Manages the LDIF-based deployment configuration. */
37 class DeployConfig
implements ConfigurationListener
{
38 private final Log log
= LogFactory
.getLog(getClass());
39 private final BundleContext bc
= FrameworkUtil
.getBundle(getClass()).getBundleContext();
41 private static Path deployConfigPath
= KernelUtils
.getOsgiInstancePath(KernelConstants
.DEPLOY_CONFIG_PATH
);
42 private SortedMap
<LdapName
, Attributes
> deployConfigs
= new TreeMap
<>();
43 // private final DataModels dataModels;
45 private boolean isFirstInit
= false;
47 private final static String ROLES
= "roles";
49 private ConfigurationAdmin configurationAdmin
;
51 public DeployConfig(ConfigurationAdmin configurationAdmin
, boolean isClean
) {
52 // this.dataModels = dataModels;
53 // ConfigurationAdmin configurationAdmin =
54 // bc.getService(bc.getServiceReference(ConfigurationAdmin.class));
56 if (!isInitialized()) { // first init
60 this.configurationAdmin
= configurationAdmin
;
61 init(configurationAdmin
, isClean
, isFirstInit
);
62 } catch (IOException e
) {
63 throw new RuntimeException("Could not init deploy configs", e
);
65 // FIXME check race conditions during initialization
66 // bc.registerService(ConfigurationListener.class, this, null);
69 private void firstInit() throws IOException
{
70 log
.info("## FIRST INIT ##");
71 Files
.createDirectories(deployConfigPath
.getParent());
73 // FirstInit firstInit = new FirstInit();
74 InitUtils
.prepareFirstInitInstanceArea();
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
);
85 private void setFromFrameworkProperties(boolean isFirstInit
) {
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());
95 if (NodeConstants
.ROLES_BASEDN
.equals(baseDn
))
98 cn
= UserAdminConf
.baseDnHash(userDirectoryConfig
);
100 userDirectoryConfig
.put(NodeConstants
.CN
, cn
);
101 putFactoryDeployConfig(NodeConstants
.NODE_USER_ADMIN_PID
, userDirectoryConfig
);
104 LdapName userAdminFactoryName
= serviceFactoryDn(NodeConstants
.NODE_USER_ADMIN_PID
);
105 for (LdapName name
: deployConfigs
.keySet()) {
106 if (name
.startsWith(userAdminFactoryName
) && !name
.equals(userAdminFactoryName
)) {
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");
113 // } catch (Exception e) {
114 // throw new CmsException("Cannot disable user directory " + name, e);
121 // Dictionary<String, Object> webServerConfig = InitUtils
122 // .getHttpServerConfig(getProps(KernelConstants.JETTY_FACTORY_PID, NodeConstants.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);
128 LdapName defaultHttpServiceDn
= serviceDn(KernelConstants
.JETTY_FACTORY_PID
, NodeConstants
.DEFAULT
);
129 if (deployConfigs
.containsKey(defaultHttpServiceDn
)) {
130 // remove old default configs since we have now to start Jetty servlet bridge
132 deployConfigs
.remove(defaultHttpServiceDn
);
139 // Explicitly configures Jetty so that the default server is not started by the
140 // activator of the Equinox Jetty bundle.
141 Dictionary
<String
, Object
> webServerConfig
= InitUtils
142 .getHttpServerConfig(getProps(KernelConstants
.JETTY_FACTORY_PID
, NodeConstants
.DEFAULT
));
143 // if (!webServerConfig.isEmpty()) {
144 // webServerConfig.put("customizer.class", KernelConstants.CMS_JETTY_CUSTOMIZER_CLASS);
146 // // TODO centralise with Jetty extender
147 // Object webSocketEnabled = webServerConfig.get(InternalHttpConstants.WEBSOCKET_ENABLED);
148 // if (webSocketEnabled != null && webSocketEnabled.toString().equals("true")) {
149 // bc.registerService(ServerEndpointConfig.Configurator.class, new CmsWebSocketConfigurator(), null);
150 // webServerConfig.put(InternalHttpConstants.WEBSOCKET_ENABLED, "true");
156 tryGettyJetty
: while (tryCount
> 0) {
158 JettyConfigurator
.startServer(KernelConstants
.DEFAULT_JETTY_SERVER
, webServerConfig
);
159 // Explicitly starts Jetty OSGi HTTP bundle, so that it gets triggered if OSGi
160 // configuration is not cleaned
161 FrameworkUtil
.getBundle(JettyConfigurator
.class).start();
163 } catch (IllegalStateException e
) {
164 // Jetty may not be ready
167 } catch (Exception e1
) {
173 } catch (Exception e
) {
174 log
.error("Cannot start default Jetty server with config " + webServerConfig
, e
);
179 private void init(ConfigurationAdmin configurationAdmin
, boolean isClean
, boolean isFirstInit
) throws IOException
{
181 try (InputStream in
= Files
.newInputStream(deployConfigPath
)) {
182 deployConfigs
= new LdifParser().read(in
);
185 if (log
.isDebugEnabled())
186 log
.debug("Clean state, loading from framework properties...");
187 setFromFrameworkProperties(isFirstInit
);
190 // TODO check consistency if not clean
193 public void loadConfigs() throws IOException
{
194 // FIXME make it more robust
195 Configuration systemRolesConf
= null;
196 LdapName systemRolesDn
;
198 // FIXME make it more robust
199 systemRolesDn
= new LdapName("cn=roles,ou=org.argeo.api.userAdmin,ou=deploy,ou=node");
200 } catch (InvalidNameException e
) {
201 throw new IllegalArgumentException(e
);
203 deployConfigs
: for (LdapName dn
: deployConfigs
.keySet()) {
204 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
205 LdapName prefix
= (LdapName
) dn
.getPrefix(dn
.size() - 1);
206 if (prefix
.toString().equals(NodeConstants
.DEPLOY_BASEDN
)) {
207 if (lastRdn
.getType().equals(NodeConstants
.CN
)) {
209 String pid
= lastRdn
.getValue().toString();
210 Configuration conf
= configurationAdmin
.getConfiguration(pid
);
211 AttributesDictionary dico
= new AttributesDictionary(deployConfigs
.get(dn
));
214 // service factory definition
217 Attributes config
= deployConfigs
.get(dn
);
218 Attribute disabled
= config
.get(UserAdminConf
.disabled
.name());
219 if (disabled
!= null)
220 continue deployConfigs
;
221 // service factory service
222 Rdn beforeLastRdn
= dn
.getRdn(dn
.size() - 2);
223 assert beforeLastRdn
.getType().equals(NodeConstants
.OU
);
224 String factoryPid
= beforeLastRdn
.getValue().toString();
225 Configuration conf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
226 if (systemRolesDn
.equals(dn
)) {
227 systemRolesConf
= configurationAdmin
.createFactoryConfiguration(factoryPid
.toString(), null);
229 AttributesDictionary dico
= new AttributesDictionary(config
);
235 // system roles must be last since it triggers node user admin publication
236 if (systemRolesConf
== null)
237 throw new IllegalStateException("System roles are not configured.");
238 systemRolesConf
.update(new AttributesDictionary(deployConfigs
.get(systemRolesDn
)));
243 public void configurationEvent(ConfigurationEvent event
) {
245 if (ConfigurationEvent
.CM_UPDATED
== event
.getType()) {
246 ConfigurationAdmin configurationAdmin
= bc
.getService(event
.getReference());
247 Configuration conf
= configurationAdmin
.getConfiguration(event
.getPid(), null);
248 LdapName serviceDn
= null;
249 String factoryPid
= conf
.getFactoryPid();
250 if (factoryPid
!= null) {
251 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
252 if (deployConfigs
.containsKey(serviceFactoryDn
)) {
253 for (LdapName dn
: deployConfigs
.keySet()) {
254 if (dn
.startsWith(serviceFactoryDn
)) {
255 Rdn lastRdn
= dn
.getRdn(dn
.size() - 1);
256 assert lastRdn
.getType().equals(NodeConstants
.CN
);
257 Object value
= conf
.getProperties().get(lastRdn
.getType());
258 assert value
!= null;
259 if (value
.equals(lastRdn
.getValue())) {
266 Object cn
= conf
.getProperties().get(NodeConstants
.CN
);
268 throw new IllegalArgumentException("Properties must contain cn");
269 if (serviceDn
== null) {
270 putFactoryDeployConfig(factoryPid
, conf
.getProperties());
272 Attributes attrs
= deployConfigs
.get(serviceDn
);
273 assert attrs
!= null;
274 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
277 if (log
.isDebugEnabled())
278 log
.debug("Updated deploy config " + serviceDn(factoryPid
, cn
.toString()));
280 // ignore non config-registered service factories
283 serviceDn
= serviceDn(event
.getPid());
284 if (deployConfigs
.containsKey(serviceDn
)) {
285 Attributes attrs
= deployConfigs
.get(serviceDn
);
286 assert attrs
!= null;
287 AttributesDictionary
.copy(conf
.getProperties(), attrs
);
289 if (log
.isDebugEnabled())
290 log
.debug("Updated deploy config " + serviceDn
);
292 // ignore non config-registered services
296 } catch (Exception e
) {
297 log
.error("Could not handle configuration event", e
);
301 void putFactoryDeployConfig(String factoryPid
, Dictionary
<String
, Object
> props
) {
302 Object cn
= props
.get(NodeConstants
.CN
);
304 throw new IllegalArgumentException("cn must be set in properties");
305 LdapName serviceFactoryDn
= serviceFactoryDn(factoryPid
);
306 if (!deployConfigs
.containsKey(serviceFactoryDn
))
307 deployConfigs
.put(serviceFactoryDn
, new BasicAttributes(NodeConstants
.OU
, factoryPid
));
308 LdapName serviceDn
= serviceDn(factoryPid
, cn
.toString());
309 Attributes attrs
= new BasicAttributes();
310 AttributesDictionary
.copy(props
, attrs
);
311 deployConfigs
.put(serviceDn
, attrs
);
314 void putDeployConfig(String servicePid
, Dictionary
<String
, Object
> props
) {
315 LdapName serviceDn
= serviceDn(servicePid
);
316 Attributes attrs
= new BasicAttributes(NodeConstants
.CN
, servicePid
);
317 AttributesDictionary
.copy(props
, attrs
);
318 deployConfigs
.put(serviceDn
, attrs
);
322 try (Writer writer
= Files
.newBufferedWriter(deployConfigPath
)) {
323 new LdifWriter(writer
).write(deployConfigs
);
324 } catch (IOException e
) {
325 // throw new CmsException("Cannot save deploy configs", e);
326 log
.error("Cannot save deploy configs", e
);
333 private LdapName
serviceFactoryDn(String factoryPid
) {
335 return new LdapName(NodeConstants
.OU
+ "=" + factoryPid
+ "," + NodeConstants
.DEPLOY_BASEDN
);
336 } catch (InvalidNameException e
) {
337 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
, e
);
341 private LdapName
serviceDn(String servicePid
) {
343 return new LdapName(NodeConstants
.CN
+ "=" + servicePid
+ "," + NodeConstants
.DEPLOY_BASEDN
);
344 } catch (InvalidNameException e
) {
345 throw new IllegalArgumentException("Cannot generate DN from " + servicePid
, e
);
349 private LdapName
serviceDn(String factoryPid
, String cn
) {
351 return (LdapName
) serviceFactoryDn(factoryPid
).add(new Rdn(NodeConstants
.CN
, cn
));
352 } catch (InvalidNameException e
) {
353 throw new IllegalArgumentException("Cannot generate DN from " + factoryPid
+ " and " + cn
, e
);
357 public Dictionary
<String
, Object
> getProps(String factoryPid
, String cn
) {
358 Attributes attrs
= deployConfigs
.get(serviceDn(factoryPid
, cn
));
360 return new AttributesDictionary(attrs
);
365 private static boolean isInitialized() {
366 return Files
.exists(deployConfigPath
);
369 public boolean isFirstInit() {