]> git.argeo.org Git - lgpl/argeo-commons.git/blob - Kernel.java
cb47a11b36368f37361b8624053420e90a0b8e7b
[lgpl/argeo-commons.git] / Kernel.java
1 package org.argeo.cms.internal.kernel;
2
3 import java.lang.management.ManagementFactory;
4 import java.security.PrivilegedAction;
5 import java.util.HashMap;
6 import java.util.Map;
7 import java.util.Properties;
8
9 import javax.jcr.Repository;
10 import javax.jcr.RepositoryFactory;
11 import javax.security.auth.Subject;
12 import javax.transaction.TransactionManager;
13 import javax.transaction.TransactionSynchronizationRegistry;
14 import javax.transaction.UserTransaction;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.apache.jackrabbit.util.TransientFileFactory;
19 import org.argeo.ArgeoException;
20 import org.argeo.cms.CmsException;
21 import org.argeo.cms.internal.transaction.SimpleTransactionManager;
22 import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
23 import org.argeo.jcr.ArgeoJcrConstants;
24 import org.eclipse.equinox.http.servlet.ExtendedHttpService;
25 import org.osgi.framework.BundleContext;
26 import org.osgi.framework.ServiceEvent;
27 import org.osgi.framework.ServiceListener;
28 import org.osgi.framework.ServiceReference;
29 import org.osgi.service.http.HttpService;
30 import org.osgi.util.tracker.ServiceTracker;
31
32 /**
33 * Argeo CMS Kernel. Responsible for :
34 * <ul>
35 * <li>security</li>
36 * <li>provisioning</li>
37 * <li>transaction</li>
38 * <li>logging</li>
39 * <li>local and remote file systems access</li>
40 * <li>OS access</li>
41 * </ul>
42 */
43 final class Kernel implements ServiceListener {
44 private final static Log log = LogFactory.getLog(Kernel.class);
45
46 private final BundleContext bundleContext = Activator.getBundleContext();
47 private final NodeSecurity nodeSecurity;
48
49 ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName());
50 JackrabbitNode node;
51
52 private SimpleTransactionManager transactionManager;
53 private OsgiJackrabbitRepositoryFactory repositoryFactory;
54 private NodeHttp nodeHttp;
55 private KernelThread kernelThread;
56
57 public Kernel() {
58 nodeSecurity = new NodeSecurity(bundleContext);
59 }
60
61 final void init() {
62 Subject.doAs(nodeSecurity.getKernelSubject(),
63 new PrivilegedAction<Void>() {
64
65 @Override
66 public Void run() {
67 doInit();
68 return null;
69 }
70
71 });
72 }
73
74 private void doInit() {
75 ClassLoader currentContextCl = Thread.currentThread()
76 .getContextClassLoader();
77 Thread.currentThread().setContextClassLoader(
78 Kernel.class.getClassLoader());
79 long begin = System.currentTimeMillis();
80
81 try {
82 // Transaction
83 transactionManager = new SimpleTransactionManager();
84
85 // Jackrabbit node
86 node = new JackrabbitNode(bundleContext);
87
88 // JCR repository factory
89 repositoryFactory = new OsgiJackrabbitRepositoryFactory();
90
91 // Authentication
92 nodeSecurity.getUserAdmin().setTransactionManager(
93 transactionManager);
94
95 // Equinox dependency
96 // ExtendedHttpService httpService = waitForHttpService();
97 // nodeHttp = new NodeHttp(httpService, node);
98 ServiceReference<ExtendedHttpService> sr = bundleContext
99 .getServiceReference(ExtendedHttpService.class);
100 if (sr != null)
101 addHttpService(sr);
102
103 // Kernel thread
104 kernelThread = new KernelThread(this);
105 kernelThread.setContextClassLoader(Kernel.class.getClassLoader());
106 kernelThread.start();
107
108 // Publish services to OSGi
109 bundleContext.registerService(TransactionManager.class,
110 transactionManager, null);
111 bundleContext.registerService(UserTransaction.class,
112 transactionManager, null);
113 bundleContext.registerService(
114 TransactionSynchronizationRegistry.class,
115 transactionManager.getTransactionSynchronizationRegistry(),
116 null);
117 nodeSecurity.publish();
118 node.publish(repositoryFactory);
119 bundleContext.registerService(RepositoryFactory.class,
120 repositoryFactory, null);
121
122 bundleContext.addServiceListener(Kernel.this);
123 } catch (Exception e) {
124 log.error("Cannot initialize Argeo CMS", e);
125 throw new ArgeoException("Cannot initialize", e);
126 } finally {
127 Thread.currentThread().setContextClassLoader(currentContextCl);
128 }
129
130 long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
131 log.info("## ARGEO CMS UP in " + (jvmUptime / 1000) + "."
132 + (jvmUptime % 1000) + "s ##");
133 long initDuration = System.currentTimeMillis() - begin;
134 if (log.isTraceEnabled())
135 log.trace("Kernel initialization took " + initDuration + "ms");
136 directorsCut(initDuration);
137 }
138
139 void destroy() {
140 long begin = System.currentTimeMillis();
141
142 kernelThread.destroyAndJoin();
143
144 if (nodeHttp != null)
145 nodeHttp.destroy();
146 // if (nodeSecurity != null)
147 // nodeSecurity.destroy();
148 if (node != null)
149 node.destroy();
150
151 bundleContext.removeServiceListener(this);
152
153 // Clean hanging threads from Jackrabbit
154 TransientFileFactory.shutdown();
155
156 // Clean hanging Gogo shell thread
157 new GogoShellKiller().start();
158
159 nodeSecurity.destroy();
160 long duration = System.currentTimeMillis() - begin;
161 log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "."
162 + (duration % 1000) + "s ##");
163 }
164
165 @Override
166 public void serviceChanged(ServiceEvent event) {
167 ServiceReference<?> sr = event.getServiceReference();
168 Object service = bundleContext.getService(sr);
169 if (service instanceof Repository) {
170 Object jcrRepoAlias = sr
171 .getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS);
172 if (jcrRepoAlias != null) {// JCR repository
173 String alias = jcrRepoAlias.toString();
174 Repository repository = (Repository) bundleContext
175 .getService(sr);
176 Map<String, Object> props = new HashMap<String, Object>();
177 for (String key : sr.getPropertyKeys())
178 props.put(key, sr.getProperty(key));
179 if (ServiceEvent.REGISTERED == event.getType()) {
180 try {
181 repositoryFactory.register(repository, props);
182 nodeHttp.registerRepositoryServlets(alias, repository);
183 } catch (Exception e) {
184 throw new CmsException(
185 "Could not publish JCR repository " + alias, e);
186 }
187 } else if (ServiceEvent.UNREGISTERING == event.getType()) {
188 repositoryFactory.unregister(repository, props);
189 nodeHttp.unregisterRepositoryServlets(alias);
190 }
191 }
192 } else if (service instanceof ExtendedHttpService) {
193 if (ServiceEvent.REGISTERED == event.getType()) {
194 addHttpService(sr);
195 } else if (ServiceEvent.UNREGISTERING == event.getType()) {
196 nodeHttp.destroy();
197 nodeHttp = null;
198 }
199 }
200 }
201
202 private void addHttpService(ServiceReference<?> sr) {
203 // for (String key : sr.getPropertyKeys())
204 // log.debug(key + "=" + sr.getProperty(key));
205 ExtendedHttpService httpService = (ExtendedHttpService) bundleContext
206 .getService(sr);
207 // TODO find constants
208 Object httpPort = sr.getProperty("http.port");
209 Object httpsPort = sr.getProperty("https.port");
210 nodeHttp = new NodeHttp(httpService, node);
211 if (log.isDebugEnabled())
212 log.debug("HTTP " + httpPort
213 + (httpsPort != null ? " - HTTPS " + httpsPort : ""));
214 }
215
216 private ExtendedHttpService waitForHttpService() {
217 final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
218 bundleContext, ExtendedHttpService.class, null);
219 st.open();
220 ExtendedHttpService httpService;
221 try {
222 httpService = st.waitForService(1000);
223 } catch (InterruptedException e) {
224 httpService = null;
225 }
226
227 if (httpService == null)
228 throw new CmsException("Could not find "
229 + ExtendedHttpService.class + " service.");
230 return httpService;
231 }
232
233 final private static void directorsCut(long initDuration) {
234 // final long ms = 128l + (long) (Math.random() * 128d);
235 long ms = initDuration / 100;
236 log.info("Spend " + ms + "ms"
237 + " reflecting on the progress brought to mankind"
238 + " by Free Software...");
239 long beginNano = System.nanoTime();
240 try {
241 Thread.sleep(ms, 0);
242 } catch (InterruptedException e) {
243 // silent
244 }
245 long durationNano = System.nanoTime() - beginNano;
246 final double M = 1000d * 1000d;
247 double sleepAccuracy = ((double) durationNano) / (ms * M);
248 if (log.isDebugEnabled())
249 log.debug("Sleep accuracy: "
250 + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100))
251 + " %");
252 }
253
254 /** Workaround for blocking Gogo shell by system shutdown. */
255 private class GogoShellKiller extends Thread {
256
257 public GogoShellKiller() {
258 super("Gogo shell killer");
259 setDaemon(true);
260 }
261
262 @Override
263 public void run() {
264 ThreadGroup rootTg = getRootThreadGroup(null);
265 Thread gogoShellThread = findGogoShellThread(rootTg);
266 if (gogoShellThread == null)
267 return;
268 while (getNonDaemonCount(rootTg) > 2) {
269 try {
270 Thread.sleep(100);
271 } catch (InterruptedException e) {
272 // silent
273 }
274 }
275 gogoShellThread = findGogoShellThread(rootTg);
276 if (gogoShellThread == null)
277 return;
278 System.exit(0);
279 }
280 }
281
282 private static ThreadGroup getRootThreadGroup(ThreadGroup tg) {
283 if (tg == null)
284 tg = Thread.currentThread().getThreadGroup();
285 if (tg.getParent() == null)
286 return tg;
287 else
288 return getRootThreadGroup(tg.getParent());
289 }
290
291 private static int getNonDaemonCount(ThreadGroup rootThreadGroup) {
292 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
293 rootThreadGroup.enumerate(threads);
294 int nonDameonCount = 0;
295 for (Thread t : threads)
296 if (!t.isDaemon())
297 nonDameonCount++;
298 return nonDameonCount;
299 }
300
301 private static Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
302 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
303 rootThreadGroup.enumerate(threads, true);
304 for (Thread thread : threads) {
305 if (thread.getName().equals("Gogo shell"))
306 return thread;
307 }
308 return null;
309 }
310
311 }