1 package org
.argeo
.cms
.internal
.kernel
;
3 import static org
.argeo
.node
.DataModelNamespace
.CMS_DATA_MODEL_NAMESPACE
;
5 import java
.io
.InputStreamReader
;
7 import java
.lang
.management
.ManagementFactory
;
9 import java
.util
.HashSet
;
10 import java
.util
.Hashtable
;
11 import java
.util
.List
;
15 import javax
.jcr
.Repository
;
16 import javax
.jcr
.Session
;
17 import javax
.security
.auth
.callback
.CallbackHandler
;
19 import org
.apache
.commons
.logging
.Log
;
20 import org
.apache
.commons
.logging
.LogFactory
;
21 import org
.apache
.jackrabbit
.commons
.cnd
.CndImporter
;
22 import org
.apache
.jackrabbit
.core
.RepositoryContext
;
23 import org
.argeo
.cms
.CmsException
;
24 import org
.argeo
.cms
.internal
.http
.NodeHttp
;
25 import org
.argeo
.jcr
.JcrUtils
;
26 import org
.argeo
.node
.DataModelNamespace
;
27 import org
.argeo
.node
.NodeConstants
;
28 import org
.argeo
.node
.NodeDeployment
;
29 import org
.argeo
.node
.NodeState
;
30 import org
.argeo
.node
.security
.CryptoKeyring
;
31 import org
.argeo
.osgi
.useradmin
.UserAdminConf
;
32 import org
.argeo
.util
.LangUtils
;
33 import org
.osgi
.framework
.Bundle
;
34 import org
.osgi
.framework
.BundleContext
;
35 import org
.osgi
.framework
.Constants
;
36 import org
.osgi
.framework
.FrameworkUtil
;
37 import org
.osgi
.framework
.ServiceReference
;
38 import org
.osgi
.framework
.wiring
.BundleCapability
;
39 import org
.osgi
.framework
.wiring
.BundleWire
;
40 import org
.osgi
.framework
.wiring
.BundleWiring
;
41 import org
.osgi
.service
.cm
.Configuration
;
42 import org
.osgi
.service
.cm
.ConfigurationAdmin
;
43 import org
.osgi
.service
.cm
.ManagedService
;
44 import org
.osgi
.service
.useradmin
.UserAdmin
;
45 import org
.osgi
.util
.tracker
.ServiceTracker
;
47 public class CmsDeployment
implements NodeDeployment
{
48 private final static String LEGACY_JCR_REPOSITORY_ALIAS
= "argeo.jcr.repository.alias";
50 private final Log log
= LogFactory
.getLog(getClass());
51 private final BundleContext bc
= FrameworkUtil
.getBundle(getClass()).getBundleContext();
53 private DeployConfig deployConfig
;
54 private HomeRepository homeRepository
;
56 private Long availableSince
;
58 private final boolean cleanState
;
60 private NodeHttp nodeHttp
;
63 private boolean nodeAvailable
= false;
64 private boolean userAdminAvailable
= false;
65 private boolean httpExpected
= false;
66 private boolean httpAvailable
= false;
68 public CmsDeployment() {
69 ServiceReference
<NodeState
> nodeStateSr
= bc
.getServiceReference(NodeState
.class);
70 if (nodeStateSr
== null)
71 throw new CmsException("No node state available");
73 NodeState nodeState
= bc
.getService(nodeStateSr
);
74 cleanState
= nodeState
.isClean();
76 nodeHttp
= new NodeHttp();
80 private void initTrackers() {
81 new ServiceTracker
<NodeHttp
, NodeHttp
>(bc
, NodeHttp
.class, null) {
84 public NodeHttp
addingService(ServiceReference
<NodeHttp
> reference
) {
87 return super.addingService(reference
);
90 new RepositoryContextStc().open();
91 new ServiceTracker
<UserAdmin
, UserAdmin
>(bc
, UserAdmin
.class, null) {
93 public UserAdmin
addingService(ServiceReference
<UserAdmin
> reference
) {
94 userAdminAvailable
= true;
96 return super.addingService(reference
);
99 new ServiceTracker
<ConfigurationAdmin
, ConfigurationAdmin
>(bc
, ConfigurationAdmin
.class, null) {
101 public ConfigurationAdmin
addingService(ServiceReference
<ConfigurationAdmin
> reference
) {
102 ConfigurationAdmin configurationAdmin
= bc
.getService(reference
);
103 deployConfig
= new DeployConfig(configurationAdmin
, cleanState
);
104 httpExpected
= deployConfig
.getProps(KernelConstants
.JETTY_FACTORY_PID
, "default") != null;
106 Configuration
[] configs
= configurationAdmin
107 .listConfigurations("(service.factoryPid=" + NodeConstants
.NODE_REPOS_FACTORY_PID
+ ")");
108 for (Configuration config
: configs
) {
109 Object cn
= config
.getProperties().get(NodeConstants
.CN
);
110 log
.debug("Standalone repo cn: " + cn
);
112 configs
= configurationAdmin
113 .listConfigurations("(service.factoryPid=" + NodeConstants
.NODE_USER_ADMIN_PID
+ ")");
115 boolean hasDomain
= false;
116 for (Configuration config
: configs
) {
117 Object realm
= config
.getProperties().get(UserAdminConf
.realm
.name());
119 log
.debug("Realm: " + realm
);
124 loadNoIpaJaasConfiguration();
126 } catch (Exception e
) {
127 throw new CmsException("Cannot initialize config", e
);
129 return super.addingService(reference
);
134 private void loadNoIpaJaasConfiguration() {
135 if (System
.getProperty(KernelConstants
.JAAS_CONFIG_PROP
) == null) {
136 String jaasConfig
= KernelConstants
.JAAS_CONFIG_NOIPA
;
137 URL url
= getClass().getClassLoader().getResource(jaasConfig
);
138 KernelUtils
.setJaasConfiguration(url
);
139 if (log
.isDebugEnabled())
140 log
.debug("Set no-IPA JAAS configuration.");
144 public void shutdown() {
145 if (nodeHttp
!= null)
147 if (deployConfig
!= null)
151 private void checkReadiness() {
152 if (nodeAvailable
&& userAdminAvailable
&& (httpExpected ? httpAvailable
: true)) {
153 availableSince
= System
.currentTimeMillis();
154 long jvmUptime
= ManagementFactory
.getRuntimeMXBean().getUptime();
155 log
.info("## ARGEO CMS AVAILABLE in " + (jvmUptime
/ 1000) + "." + (jvmUptime
% 1000) + "s ##");
156 long begin
= bc
.getService(bc
.getServiceReference(NodeState
.class)).getAvailableSince();
157 long initDuration
= System
.currentTimeMillis() - begin
;
158 if (log
.isTraceEnabled())
159 log
.trace("Kernel initialization took " + initDuration
+ "ms");
160 directorsCut(initDuration
);
164 final private void directorsCut(long initDuration
) {
165 // final long ms = 128l + (long) (Math.random() * 128d);
166 long ms
= initDuration
/ 100;
167 log
.info("Spend " + ms
+ "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
168 long beginNano
= System
.nanoTime();
171 } catch (InterruptedException e
) {
174 long durationNano
= System
.nanoTime() - beginNano
;
175 final double M
= 1000d
* 1000d
;
176 double sleepAccuracy
= ((double) durationNano
) / (ms
* M
);
177 if (log
.isDebugEnabled())
178 log
.debug("Sleep accuracy: " + String
.format("%.2f", 100 - (sleepAccuracy
* 100 - 100)) + " %");
181 private void prepareNodeRepository(Repository deployedNodeRepository
) {
182 if (availableSince
!= null) {
183 throw new CmsException("Deployment is already available");
187 prepareDataModel(KernelUtils
.openAdminSession(deployedNodeRepository
));
190 private void prepareHomeRepository(Repository deployedRepository
) {
191 Hashtable
<String
, String
> regProps
= new Hashtable
<String
, String
>();
192 regProps
.put(NodeConstants
.CN
, NodeConstants
.HOME
);
193 regProps
.put(LEGACY_JCR_REPOSITORY_ALIAS
, NodeConstants
.HOME
);
194 homeRepository
= new HomeRepository(deployedRepository
);
196 bc
.registerService(Repository
.class, homeRepository
, regProps
);
198 new ServiceTracker
<CallbackHandler
, CallbackHandler
>(bc
, CallbackHandler
.class, null) {
201 public CallbackHandler
addingService(ServiceReference
<CallbackHandler
> reference
) {
202 NodeKeyRing nodeKeyring
= new NodeKeyRing(homeRepository
);
203 CallbackHandler callbackHandler
= bc
.getService(reference
);
204 nodeKeyring
.setDefaultCallbackHandler(callbackHandler
);
205 bc
.registerService(LangUtils
.names(CryptoKeyring
.class, ManagedService
.class), nodeKeyring
,
206 LangUtils
.dico(Constants
.SERVICE_PID
, NodeConstants
.NODE_KEYRING_PID
));
207 return callbackHandler
;
213 /** Session is logged out. */
214 private void prepareDataModel(Session adminSession
) {
216 Set
<String
> processed
= new HashSet
<String
>();
217 bundles
: for (Bundle bundle
: bc
.getBundles()) {
218 BundleWiring wiring
= bundle
.adapt(BundleWiring
.class);
221 processWiring(adminSession
, wiring
, processed
);
224 JcrUtils
.logoutQuietly(adminSession
);
228 private void processWiring(Session adminSession
, BundleWiring wiring
, Set
<String
> processed
) {
229 // recursively process requirements first
230 List
<BundleWire
> requiredWires
= wiring
.getRequiredWires(CMS_DATA_MODEL_NAMESPACE
);
231 for (BundleWire wire
: requiredWires
) {
232 processWiring(adminSession
, wire
.getProviderWiring(), processed
);
233 // registerCnd(adminSession, wire.getCapability(), processed);
235 List
<BundleCapability
> capabilities
= wiring
.getCapabilities(CMS_DATA_MODEL_NAMESPACE
);
236 for (BundleCapability capability
: capabilities
) {
237 registerCnd(adminSession
, capability
, processed
);
241 private void registerCnd(Session adminSession
, BundleCapability capability
, Set
<String
> processed
) {
242 Map
<String
, Object
> attrs
= capability
.getAttributes();
243 String name
= (String
) attrs
.get(DataModelNamespace
.CAPABILITY_NAME_ATTRIBUTE
);
244 if (processed
.contains(name
)) {
245 if (log
.isTraceEnabled())
246 log
.trace("Data model " + name
+ " has already been processed");
249 String path
= (String
) attrs
.get(DataModelNamespace
.CAPABILITY_CND_ATTRIBUTE
);
250 URL url
= capability
.getRevision().getBundle().getResource(path
);
251 try (Reader reader
= new InputStreamReader(url
.openStream())) {
252 CndImporter
.registerNodeTypes(reader
, adminSession
, true);
254 if (log
.isDebugEnabled())
255 log
.debug("Registered CND " + url
);
256 } catch (Exception e
) {
257 throw new CmsException("Cannot import CND " + url
, e
);
260 if (!asBoolean((String
) attrs
.get(DataModelNamespace
.CAPABILITY_ABSTRACT_ATTRIBUTE
))) {
261 Hashtable
<String
, Object
> properties
= new Hashtable
<>();
262 properties
.put(LEGACY_JCR_REPOSITORY_ALIAS
, name
);
263 properties
.put(NodeConstants
.CN
, name
);
264 if (name
.equals(NodeConstants
.NODE
))
265 properties
.put(Constants
.SERVICE_RANKING
, Integer
.MAX_VALUE
);
266 LocalRepository localRepository
= new LocalRepository(adminSession
.getRepository(), capability
);
267 bc
.registerService(Repository
.class, localRepository
, properties
);
268 if (log
.isDebugEnabled())
269 log
.debug("Published data model " + name
);
273 private boolean asBoolean(String value
) {
282 throw new CmsException("Unsupported value for attribute " + DataModelNamespace
.CAPABILITY_ABSTRACT_ATTRIBUTE
288 public Long
getAvailableSince() {
289 return availableSince
;
292 private class RepositoryContextStc
extends ServiceTracker
<RepositoryContext
, RepositoryContext
> {
294 public RepositoryContextStc() {
295 super(bc
, RepositoryContext
.class, null);
299 public RepositoryContext
addingService(ServiceReference
<RepositoryContext
> reference
) {
300 RepositoryContext nodeRepo
= bc
.getService(reference
);
301 Object cn
= reference
.getProperty(NodeConstants
.CN
);
303 if (cn
.equals(NodeConstants
.NODE
)) {
304 prepareNodeRepository(nodeRepo
.getRepository());
305 prepareHomeRepository(nodeRepo
.getRepository());
306 nodeAvailable
= true;
316 public void modifiedService(ServiceReference
<RepositoryContext
> reference
, RepositoryContext service
) {
320 public void removedService(ServiceReference
<RepositoryContext
> reference
, RepositoryContext service
) {