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