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