]> git.argeo.org Git - lgpl/argeo-commons.git/blob - org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java
Fix add member to LDIF group
[lgpl/argeo-commons.git] / org.argeo.cms / src / org / argeo / cms / internal / kernel / 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
8 import javax.jcr.Repository;
9 import javax.jcr.RepositoryFactory;
10 import javax.security.auth.Subject;
11 import javax.transaction.TransactionManager;
12 import javax.transaction.TransactionSynchronizationRegistry;
13 import javax.transaction.UserTransaction;
14
15 import org.apache.commons.logging.Log;
16 import org.apache.commons.logging.LogFactory;
17 import org.apache.jackrabbit.util.TransientFileFactory;
18 import org.argeo.ArgeoException;
19 import org.argeo.cms.CmsException;
20 import org.argeo.cms.internal.transaction.SimpleTransactionManager;
21 import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory;
22 import org.argeo.jcr.ArgeoJcrConstants;
23 import org.argeo.security.core.InternalAuthentication;
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.util.tracker.ServiceTracker;
30 import org.springframework.security.core.context.SecurityContextHolder;
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 InternalAuthentication initAuth = new InternalAuthentication(
81 KernelConstants.DEFAULT_SECURITY_KEY);
82 SecurityContextHolder.getContext().setAuthentication(initAuth);
83
84 try {
85 // Transaction
86 transactionManager = new SimpleTransactionManager();
87 bundleContext.registerService(TransactionManager.class,
88 transactionManager, null);
89 bundleContext.registerService(UserTransaction.class,
90 transactionManager, null);
91 bundleContext.registerService(
92 TransactionSynchronizationRegistry.class,
93 transactionManager.getTransactionSynchronizationRegistry(),
94 null);
95
96 // Jackrabbit node
97 node = new JackrabbitNode(bundleContext);
98
99 // JCR repository factory
100 repositoryFactory = new OsgiJackrabbitRepositoryFactory();
101
102 // Authentication
103 nodeSecurity.getUserAdmin().setSyncRegistry(
104 transactionManager.getTransactionSynchronizationRegistry());
105 nodeSecurity.getUserAdmin().setTransactionManager(
106 transactionManager);
107
108 // Equinox dependency
109 ExtendedHttpService httpService = waitForHttpService();
110 nodeHttp = new NodeHttp(httpService, node, nodeSecurity);
111
112 // Kernel thread
113 kernelThread = new KernelThread(this);
114 kernelThread.setContextClassLoader(Kernel.class.getClassLoader());
115 kernelThread.start();
116
117 // Publish services to OSGi
118 nodeSecurity.publish();
119 node.publish(repositoryFactory);
120 bundleContext.registerService(RepositoryFactory.class,
121 repositoryFactory, null);
122
123 bundleContext.addServiceListener(Kernel.this);
124 } catch (Exception e) {
125 log.error("Cannot initialize Argeo CMS", e);
126 throw new ArgeoException("Cannot initialize", e);
127 } finally {
128 Thread.currentThread().setContextClassLoader(currentContextCl);
129 }
130
131 long jvmUptime = ManagementFactory.getRuntimeMXBean().getUptime();
132 log.info("## ARGEO CMS UP in " + (jvmUptime / 1000) + "."
133 + (jvmUptime % 1000) + "s ##");
134 long initDuration = System.currentTimeMillis() - begin;
135 if (log.isTraceEnabled())
136 log.trace("Kernel initialization took " + initDuration + "ms");
137 directorsCut(initDuration);
138 }
139
140 void destroy() {
141 long begin = System.currentTimeMillis();
142
143 kernelThread.destroyAndJoin();
144
145 if (nodeHttp != null)
146 nodeHttp.destroy();
147 // if (nodeSecurity != null)
148 // nodeSecurity.destroy();
149 if (node != null)
150 node.destroy();
151
152 bundleContext.removeServiceListener(this);
153
154 // Clean hanging threads from Jackrabbit
155 TransientFileFactory.shutdown();
156
157 // Clean hanging Gogo shell thread
158 new GogoShellKiller().start();
159
160 nodeSecurity.destroy();
161 long duration = System.currentTimeMillis() - begin;
162 log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "."
163 + (duration % 1000) + "s ##");
164 }
165
166 @Override
167 public void serviceChanged(ServiceEvent event) {
168 ServiceReference<?> sr = event.getServiceReference();
169 Object jcrRepoAlias = sr
170 .getProperty(ArgeoJcrConstants.JCR_REPOSITORY_ALIAS);
171 if (jcrRepoAlias != null) {// JCR repository
172 String alias = jcrRepoAlias.toString();
173 Repository repository = (Repository) bundleContext.getService(sr);
174 Map<String, Object> props = new HashMap<String, Object>();
175 for (String key : sr.getPropertyKeys())
176 props.put(key, sr.getProperty(key));
177 if (ServiceEvent.REGISTERED == event.getType()) {
178 try {
179 repositoryFactory.register(repository, props);
180 nodeHttp.registerRepositoryServlets(alias, repository);
181 } catch (Exception e) {
182 throw new CmsException("Could not publish JCR repository "
183 + alias, e);
184 }
185 } else if (ServiceEvent.UNREGISTERING == event.getType()) {
186 repositoryFactory.unregister(repository, props);
187 nodeHttp.unregisterRepositoryServlets(alias);
188 }
189 }
190
191 }
192
193 private ExtendedHttpService waitForHttpService() {
194 final ServiceTracker<ExtendedHttpService, ExtendedHttpService> st = new ServiceTracker<ExtendedHttpService, ExtendedHttpService>(
195 bundleContext, ExtendedHttpService.class, null);
196 st.open();
197 ExtendedHttpService httpService;
198 try {
199 httpService = st.waitForService(1000);
200 } catch (InterruptedException e) {
201 httpService = null;
202 }
203
204 if (httpService == null)
205 throw new CmsException("Could not find "
206 + ExtendedHttpService.class + " service.");
207 return httpService;
208 }
209
210 final private static void directorsCut(long initDuration) {
211 // final long ms = 128l + (long) (Math.random() * 128d);
212 long ms = initDuration / 100;
213 log.info("Spend " + ms + "ms"
214 + " reflecting on the progress brought to mankind"
215 + " by Free Software...");
216 long beginNano = System.nanoTime();
217 try {
218 Thread.sleep(ms, 0);
219 } catch (InterruptedException e) {
220 // silent
221 }
222 long durationNano = System.nanoTime() - beginNano;
223 final double M = 1000d * 1000d;
224 double sleepAccuracy = ((double) durationNano) / (ms * M);
225 if (log.isDebugEnabled())
226 log.debug("Sleep accuracy: "
227 + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100))
228 + " %");
229 }
230
231 /** Workaround for blocking Gogo shell by system shutdown. */
232 private class GogoShellKiller extends Thread {
233
234 public GogoShellKiller() {
235 super("Gogo shell killer");
236 setDaemon(true);
237 }
238
239 @Override
240 public void run() {
241 ThreadGroup rootTg = getRootThreadGroup(null);
242 Thread gogoShellThread = findGogoShellThread(rootTg);
243 if (gogoShellThread == null)
244 return;
245 while (getNonDaemonCount(rootTg) > 2) {
246 try {
247 Thread.sleep(100);
248 } catch (InterruptedException e) {
249 // silent
250 }
251 }
252 gogoShellThread = findGogoShellThread(rootTg);
253 if (gogoShellThread == null)
254 return;
255 System.exit(0);
256 }
257 }
258
259 private static ThreadGroup getRootThreadGroup(ThreadGroup tg) {
260 if (tg == null)
261 tg = Thread.currentThread().getThreadGroup();
262 if (tg.getParent() == null)
263 return tg;
264 else
265 return getRootThreadGroup(tg.getParent());
266 }
267
268 private static int getNonDaemonCount(ThreadGroup rootThreadGroup) {
269 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
270 rootThreadGroup.enumerate(threads);
271 int nonDameonCount = 0;
272 for (Thread t : threads)
273 if (!t.isDaemon())
274 nonDameonCount++;
275 return nonDameonCount;
276 }
277
278 private static Thread findGogoShellThread(ThreadGroup rootThreadGroup) {
279 Thread[] threads = new Thread[rootThreadGroup.activeCount()];
280 rootThreadGroup.enumerate(threads, true);
281 for (Thread thread : threads) {
282 if (thread.getName().equals("Gogo shell"))
283 return thread;
284 }
285 return null;
286 }
287
288 }