]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java
Rather use DataAdmin login context
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / CmsDeployment.java
1 package org.argeo.cms.internal.kernel;
2
3 import static org.argeo.node.DataModelNamespace.CMS_DATA_MODEL_NAMESPACE;
4
5 import java.io.InputStreamReader;
6 import java.io.Reader;
7 import java.lang.management.ManagementFactory;
8 import java.net.URL;
9 import java.util.HashSet;
10 import java.util.Hashtable;
11 import java.util.List;
12 import java.util.Map;
13 import java.util.Set;
14
15 import javax.jcr.Repository;
16 import javax.jcr.Session;
17 import javax.security.auth.callback.CallbackHandler;
18
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.jcr.JcrUtils;
25 import org.argeo.node.DataModelNamespace;
26 import org.argeo.node.NodeConstants;
27 import org.argeo.node.NodeDeployment;
28 import org.argeo.node.NodeState;
29 import org.argeo.node.security.CryptoKeyring;
30 import org.argeo.util.LangUtils;
31 import org.osgi.framework.Bundle;
32 import org.osgi.framework.BundleContext;
33 import org.osgi.framework.Constants;
34 import org.osgi.framework.FrameworkUtil;
35 import org.osgi.framework.ServiceReference;
36 import org.osgi.framework.wiring.BundleCapability;
37 import org.osgi.framework.wiring.BundleWire;
38 import org.osgi.framework.wiring.BundleWiring;
39 import org.osgi.service.cm.ConfigurationAdmin;
40 import org.osgi.service.cm.ManagedService;
41 import org.osgi.service.http.HttpService;
42 import org.osgi.service.useradmin.UserAdmin;
43 import org.osgi.util.tracker.ServiceTracker;
44
45 public class CmsDeployment implements NodeDeployment {
46 private final static String LEGACY_JCR_REPOSITORY_ALIAS = "argeo.jcr.repository.alias";
47
48 private final Log log = LogFactory.getLog(getClass());
49 private final BundleContext bc = FrameworkUtil.getBundle(getClass()).getBundleContext();
50
51 private DeployConfig deployConfig;
52 private HomeRepository homeRepository;
53
54 private Long availableSince;
55
56 private final boolean cleanState;
57 // Readiness
58 private boolean nodeAvailable = false;
59 private boolean userAdminAvailable = false;
60 private boolean httpExpected = false;
61 private boolean httpAvailable = false;
62
63 public CmsDeployment() {
64 ServiceReference<NodeState> nodeStateSr = bc.getServiceReference(NodeState.class);
65 if (nodeStateSr == null)
66 throw new CmsException("No node state available");
67
68 NodeState nodeState = bc.getService(nodeStateSr);
69 cleanState = nodeState.isClean();
70
71 initTrackers();
72 }
73
74 private void initTrackers() {
75 new PrepareHttpStc().open();
76 new RepositoryContextStc().open();
77 new ServiceTracker<UserAdmin, UserAdmin>(bc, UserAdmin.class, null) {
78 @Override
79 public UserAdmin addingService(ServiceReference<UserAdmin> reference) {
80 userAdminAvailable = true;
81 checkReadiness();
82 return super.addingService(reference);
83 }
84 }.open();
85 new ServiceTracker<ConfigurationAdmin, ConfigurationAdmin>(bc, ConfigurationAdmin.class, null) {
86 @Override
87 public ConfigurationAdmin addingService(ServiceReference<ConfigurationAdmin> reference) {
88 ConfigurationAdmin configurationAdmin = bc.getService(reference);
89 deployConfig = new DeployConfig(configurationAdmin, cleanState);
90 httpExpected = deployConfig.getProps(KernelConstants.JETTY_FACTORY_PID, "default") != null;
91 return super.addingService(reference);
92 }
93 }.open();
94 }
95
96 public void shutdown() {
97 if (deployConfig != null)
98 deployConfig.save();
99 }
100
101 private void checkReadiness() {
102 if (nodeAvailable && userAdminAvailable && (httpExpected ? httpAvailable : true)) {
103 availableSince = System.currentTimeMillis();
104 long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
105 log.info("## ARGEO CMS AVAILABLE in " + (jvmUptime / 1000) + "." + (jvmUptime % 1000) + "s ##");
106 long begin = bc.getService(bc.getServiceReference(NodeState.class)).getAvailableSince();
107 long initDuration = System.currentTimeMillis() - begin;
108 if (log.isTraceEnabled())
109 log.trace("Kernel initialization took " + initDuration + "ms");
110 directorsCut(initDuration);
111 }
112 }
113
114 final private void directorsCut(long initDuration) {
115 // final long ms = 128l + (long) (Math.random() * 128d);
116 long ms = initDuration / 100;
117 log.info("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software...");
118 long beginNano = System.nanoTime();
119 try {
120 Thread.sleep(ms, 0);
121 } catch (InterruptedException e) {
122 // silent
123 }
124 long durationNano = System.nanoTime() - beginNano;
125 final double M = 1000d * 1000d;
126 double sleepAccuracy = ((double) durationNano) / (ms * M);
127 if (log.isDebugEnabled())
128 log.debug("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %");
129 }
130
131 private void prepareNodeRepository(Repository deployedNodeRepository) {
132 if (availableSince != null) {
133 throw new CmsException("Deployment is already available");
134 }
135
136 // home
137 prepareDataModel(KernelUtils.openAdminSession(deployedNodeRepository));
138 }
139
140 private void prepareHomeRepository(Repository deployedRepository) {
141 Hashtable<String, String> regProps = new Hashtable<String, String>();
142 regProps.put(NodeConstants.CN, NodeConstants.HOME);
143 regProps.put(LEGACY_JCR_REPOSITORY_ALIAS, NodeConstants.HOME);
144 homeRepository = new HomeRepository(deployedRepository);
145 // register
146 bc.registerService(Repository.class, homeRepository, regProps);
147
148 new ServiceTracker<CallbackHandler, CallbackHandler>(bc, CallbackHandler.class, null) {
149
150 @Override
151 public CallbackHandler addingService(ServiceReference<CallbackHandler> reference) {
152 NodeKeyRing nodeKeyring = new NodeKeyRing(homeRepository);
153 CallbackHandler callbackHandler = bc.getService(reference);
154 nodeKeyring.setDefaultCallbackHandler(callbackHandler);
155 bc.registerService(LangUtils.names(CryptoKeyring.class, ManagedService.class), nodeKeyring,
156 LangUtils.dico(Constants.SERVICE_PID, NodeConstants.NODE_KEYRING_PID));
157 return callbackHandler;
158 }
159
160 }.open();
161 }
162
163 /** Session is logged out. */
164 private void prepareDataModel(Session adminSession) {
165 try {
166 Set<String> processed = new HashSet<String>();
167 bundles: for (Bundle bundle : bc.getBundles()) {
168 BundleWiring wiring = bundle.adapt(BundleWiring.class);
169 if (wiring == null)
170 continue bundles;
171 processWiring(adminSession, wiring, processed);
172 }
173 } finally {
174 JcrUtils.logoutQuietly(adminSession);
175 }
176 }
177
178 private void processWiring(Session adminSession, BundleWiring wiring, Set<String> processed) {
179 // recursively process requirements first
180 List<BundleWire> requiredWires = wiring.getRequiredWires(CMS_DATA_MODEL_NAMESPACE);
181 for (BundleWire wire : requiredWires) {
182 processWiring(adminSession, wire.getProviderWiring(), processed);
183 // registerCnd(adminSession, wire.getCapability(), processed);
184 }
185 List<BundleCapability> capabilities = wiring.getCapabilities(CMS_DATA_MODEL_NAMESPACE);
186 for (BundleCapability capability : capabilities) {
187 registerCnd(adminSession, capability, processed);
188 }
189 }
190
191 private void registerCnd(Session adminSession, BundleCapability capability, Set<String> processed) {
192 Map<String, Object> attrs = capability.getAttributes();
193 String name = (String) attrs.get(DataModelNamespace.CAPABILITY_NAME_ATTRIBUTE);
194 if (processed.contains(name)) {
195 if (log.isTraceEnabled())
196 log.trace("Data model " + name + " has already been processed");
197 return;
198 }
199 String path = (String) attrs.get(DataModelNamespace.CAPABILITY_CND_ATTRIBUTE);
200 URL url = capability.getRevision().getBundle().getResource(path);
201 try (Reader reader = new InputStreamReader(url.openStream())) {
202 CndImporter.registerNodeTypes(reader, adminSession, true);
203 processed.add(name);
204 if (log.isDebugEnabled())
205 log.debug("Registered CND " + url);
206 } catch (Exception e) {
207 throw new CmsException("Cannot import CND " + url, e);
208 }
209
210 if (!asBoolean((String) attrs.get(DataModelNamespace.CAPABILITY_ABSTRACT_ATTRIBUTE))) {
211 Hashtable<String, Object> properties = new Hashtable<>();
212 properties.put(LEGACY_JCR_REPOSITORY_ALIAS, name);
213 properties.put(NodeConstants.CN, name);
214 if (name.equals(NodeConstants.NODE))
215 properties.put(Constants.SERVICE_RANKING, Integer.MAX_VALUE);
216 LocalRepository localRepository = new LocalRepository(adminSession.getRepository(), capability);
217 bc.registerService(Repository.class, localRepository, properties);
218 if (log.isDebugEnabled())
219 log.debug("Published data model " + name);
220 }
221 }
222
223 private boolean asBoolean(String value) {
224 if (value == null)
225 return false;
226 switch (value) {
227 case "true":
228 return true;
229 case "false":
230 return false;
231 default:
232 throw new CmsException("Unsupported value for attribute " + DataModelNamespace.CAPABILITY_ABSTRACT_ATTRIBUTE
233 + ": " + value);
234 }
235 }
236
237 @Override
238 public Long getAvailableSince() {
239 return availableSince;
240 }
241
242 private class RepositoryContextStc extends ServiceTracker<RepositoryContext, RepositoryContext> {
243
244 public RepositoryContextStc() {
245 super(bc, RepositoryContext.class, null);
246 }
247
248 @Override
249 public RepositoryContext addingService(ServiceReference<RepositoryContext> reference) {
250 RepositoryContext nodeRepo = bc.getService(reference);
251 Object cn = reference.getProperty(NodeConstants.CN);
252 if (cn != null) {
253 if (cn.equals(NodeConstants.NODE)) {
254 prepareNodeRepository(nodeRepo.getRepository());
255 prepareHomeRepository(nodeRepo.getRepository());
256 nodeAvailable = true;
257 checkReadiness();
258 }else{
259 // TODO standalone
260 }
261 }
262 return nodeRepo;
263 }
264
265 @Override
266 public void modifiedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
267 }
268
269 @Override
270 public void removedService(ServiceReference<RepositoryContext> reference, RepositoryContext service) {
271 }
272
273 }
274
275 private class PrepareHttpStc extends ServiceTracker<HttpService, HttpService> {
276 private DataHttp dataHttp;
277 private NodeHttp nodeHttp;
278
279 public PrepareHttpStc() {
280 super(bc, HttpService.class, null);
281 }
282
283 @Override
284 public HttpService addingService(ServiceReference<HttpService> reference) {
285 HttpService httpService = addHttpService(reference);
286 return httpService;
287 }
288
289 @Override
290 public void removedService(ServiceReference<HttpService> reference, HttpService service) {
291 if (dataHttp != null)
292 dataHttp.destroy();
293 dataHttp = null;
294 if (nodeHttp != null)
295 nodeHttp.destroy();
296 nodeHttp = null;
297 }
298
299 private HttpService addHttpService(ServiceReference<HttpService> sr) {
300 HttpService httpService = bc.getService(sr);
301 // TODO find constants
302 Object httpPort = sr.getProperty("http.port");
303 Object httpsPort = sr.getProperty("https.port");
304 dataHttp = new DataHttp(httpService);
305 nodeHttp = new NodeHttp(httpService, bc);
306 log.info(httpPortsMsg(httpPort, httpsPort));
307 httpAvailable = true;
308 checkReadiness();
309 return httpService;
310 }
311
312 private String httpPortsMsg(Object httpPort, Object httpsPort) {
313 return "HTTP " + httpPort + (httpsPort != null ? " - HTTPS " + httpsPort : "");
314 }
315 }
316
317 }