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