X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms%2Fsrc%2Forg%2Fargeo%2Fcms%2Finternal%2Fkernel%2FKernel.java;h=08dad56b803c28bedf147e9e055ffa214e247e21;hb=bedec814b105c1cc53f2beadfe36d76e42c9a02e;hp=67f0c3737caab318e3acd0c8a28d0e207ffe0760;hpb=f7944a8accf7b9cfc3cffe6e6f5c611cd48f592c;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java index 67f0c3737..08dad56b8 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Kernel.java @@ -1,7 +1,6 @@ package org.argeo.cms.internal.kernel; import java.lang.management.ManagementFactory; -import java.net.URL; import java.security.PrivilegedAction; import java.util.HashMap; import java.util.Map; @@ -9,15 +8,16 @@ import java.util.Map; import javax.jcr.Repository; import javax.jcr.RepositoryFactory; import javax.security.auth.Subject; -import javax.security.auth.login.LoginContext; -import javax.security.auth.login.LoginException; +import javax.transaction.TransactionManager; +import javax.transaction.TransactionSynchronizationRegistry; +import javax.transaction.UserTransaction; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jackrabbit.util.TransientFileFactory; import org.argeo.ArgeoException; import org.argeo.cms.CmsException; -import org.argeo.cms.KernelHeader; +import org.argeo.cms.internal.transaction.SimpleTransactionManager; import org.argeo.jackrabbit.OsgiJackrabbitRepositoryFactory; import org.argeo.jcr.ArgeoJcrConstants; import org.argeo.security.core.InternalAuthentication; @@ -44,40 +44,31 @@ final class Kernel implements ServiceListener { private final static Log log = LogFactory.getLog(Kernel.class); private final BundleContext bundleContext = Activator.getBundleContext(); + private final NodeSecurity nodeSecurity; ThreadGroup threadGroup = new ThreadGroup(Kernel.class.getSimpleName()); JackrabbitNode node; - OsgiJackrabbitRepositoryFactory repositoryFactory; - NodeSecurity nodeSecurity; - NodeHttp nodeHttp; - private KernelThread kernelThread; - private final Subject kernelSubject = new Subject(); + private SimpleTransactionManager transactionManager; + private OsgiJackrabbitRepositoryFactory repositoryFactory; + private NodeHttp nodeHttp; + private KernelThread kernelThread; public Kernel() { - URL url = getClass().getClassLoader().getResource( - KernelConstants.JAAS_CONFIG); - System.setProperty("java.security.auth.login.config", - url.toExternalForm()); - try { - LoginContext kernelLc = new LoginContext( - KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject); - kernelLc.login(); - } catch (LoginException e) { - throw new CmsException("Cannot log in kernel", e); - } + nodeSecurity = new NodeSecurity(bundleContext); } final void init() { - Subject.doAs(kernelSubject, new PrivilegedAction() { + Subject.doAs(nodeSecurity.getKernelSubject(), + new PrivilegedAction() { - @Override - public Void run() { - doInit(); - return null; - } + @Override + public Void run() { + doInit(); + return null; + } - }); + }); } private void doInit() { @@ -91,6 +82,9 @@ final class Kernel implements ServiceListener { SecurityContextHolder.getContext().setAuthentication(initAuth); try { + // Transaction + transactionManager = new SimpleTransactionManager(); + // Jackrabbit node node = new JackrabbitNode(bundleContext); @@ -98,11 +92,12 @@ final class Kernel implements ServiceListener { repositoryFactory = new OsgiJackrabbitRepositoryFactory(); // Authentication - nodeSecurity = new NodeSecurity(bundleContext, node); + nodeSecurity.getUserAdmin().setTransactionManager( + transactionManager); // Equinox dependency ExtendedHttpService httpService = waitForHttpService(); - nodeHttp = new NodeHttp(httpService, node, nodeSecurity); + nodeHttp = new NodeHttp(httpService, node); // Kernel thread kernelThread = new KernelThread(this); @@ -110,6 +105,14 @@ final class Kernel implements ServiceListener { kernelThread.start(); // Publish services to OSGi + bundleContext.registerService(TransactionManager.class, + transactionManager, null); + bundleContext.registerService(UserTransaction.class, + transactionManager, null); + bundleContext.registerService( + TransactionSynchronizationRegistry.class, + transactionManager.getTransactionSynchronizationRegistry(), + null); nodeSecurity.publish(); node.publish(repositoryFactory); bundleContext.registerService(RepositoryFactory.class, @@ -139,8 +142,8 @@ final class Kernel implements ServiceListener { if (nodeHttp != null) nodeHttp.destroy(); - if (nodeSecurity != null) - nodeSecurity.destroy(); + // if (nodeSecurity != null) + // nodeSecurity.destroy(); if (node != null) node.destroy(); @@ -149,14 +152,10 @@ final class Kernel implements ServiceListener { // Clean hanging threads from Jackrabbit TransientFileFactory.shutdown(); - try { - LoginContext kernelLc = new LoginContext( - KernelHeader.LOGIN_CONTEXT_SYSTEM, kernelSubject); - kernelLc.logout(); - } catch (LoginException e) { - throw new CmsException("Cannot log in kernel", e); - } + // Clean hanging Gogo shell thread + new GogoShellKiller().start(); + nodeSecurity.destroy(); long duration = System.currentTimeMillis() - begin; log.info("## ARGEO CMS DOWN in " + (duration / 1000) + "." + (duration % 1000) + "s ##"); @@ -226,4 +225,62 @@ final class Kernel implements ServiceListener { + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %"); } + + /** Workaround for blocking Gogo shell by system shutdown. */ + private class GogoShellKiller extends Thread { + + public GogoShellKiller() { + super("Gogo shell killer"); + setDaemon(true); + } + + @Override + public void run() { + ThreadGroup rootTg = getRootThreadGroup(null); + Thread gogoShellThread = findGogoShellThread(rootTg); + if (gogoShellThread == null) + return; + while (getNonDaemonCount(rootTg) > 2) { + try { + Thread.sleep(100); + } catch (InterruptedException e) { + // silent + } + } + gogoShellThread = findGogoShellThread(rootTg); + if (gogoShellThread == null) + return; + System.exit(0); + } + } + + private static ThreadGroup getRootThreadGroup(ThreadGroup tg) { + if (tg == null) + tg = Thread.currentThread().getThreadGroup(); + if (tg.getParent() == null) + return tg; + else + return getRootThreadGroup(tg.getParent()); + } + + private static int getNonDaemonCount(ThreadGroup rootThreadGroup) { + Thread[] threads = new Thread[rootThreadGroup.activeCount()]; + rootThreadGroup.enumerate(threads); + int nonDameonCount = 0; + for (Thread t : threads) + if (!t.isDaemon()) + nonDameonCount++; + return nonDameonCount; + } + + private static Thread findGogoShellThread(ThreadGroup rootThreadGroup) { + Thread[] threads = new Thread[rootThreadGroup.activeCount()]; + rootThreadGroup.enumerate(threads, true); + for (Thread thread : threads) { + if (thread.getName().equals("Gogo shell")) + return thread; + } + return null; + } + } \ No newline at end of file