From 5b5c2b97eee9edd037e198acadf8b416a973863f Mon Sep 17 00:00:00 2001 From: mbaudier Date: Thu, 2 Nov 2017 15:48:31 +0100 Subject: [PATCH] Improve node deployment --- dist/argeo-node/pom.xml | 2 +- dist/argeo-node/rpm/etc/node/config.ini | 30 ++++--- dist/argeo-node/rpm/etc/node/log4j.properties | 14 +-- .../rpm/usr/lib/systemd/system/node.service | 5 +- .../rpm/usr/sbin/{node-service => nodectl} | 20 +++-- .../org/argeo/cms/util/SimpleUxContext.java | 3 +- .../argeo/cms/internal/kernel/Activator.java | 1 + .../cms/internal/kernel/CmsDeployment.java | 30 +++---- .../cms/internal/kernel/CmsShutdown.java | 70 +++++++++++++++ .../argeo/cms/internal/kernel/CmsState.java | 85 ++++--------------- .../argeo/cms/internal/kernel/FirstInit.java | 36 ++++---- .../cms/internal/kernel/GogoShellKiller.java | 64 ++++++++++++++ .../argeo/cms/internal/kernel/NodeLogger.java | 31 +++++-- 13 files changed, 249 insertions(+), 142 deletions(-) rename dist/argeo-node/rpm/usr/sbin/{node-service => nodectl} (90%) create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsShutdown.java create mode 100644 org.argeo.cms/src/org/argeo/cms/internal/kernel/GogoShellKiller.java diff --git a/dist/argeo-node/pom.xml b/dist/argeo-node/pom.xml index e53a115ce..da4e22949 100644 --- a/dist/argeo-node/pom.xml +++ b/dist/argeo-node/pom.xml @@ -65,7 +65,7 @@ rpm/usr/sbin - node-service + nodectl diff --git a/dist/argeo-node/rpm/etc/node/config.ini b/dist/argeo-node/rpm/etc/node/config.ini index 44a2170e5..ec75a4f7a 100644 --- a/dist/argeo-node/rpm/etc/node/config.ini +++ b/dist/argeo-node/rpm/etc/node/config.ini @@ -1,11 +1,20 @@ -#argeo.osgi.baseUrl=http://forge.argeo.org/data/java/argeo-2.1/ -#argeo.osgi.distributionUrl=org/argeo/commons/org.argeo.dep.cms.sdk/2.1.65/org.argeo.dep.cms.sdk-2.1.69.jar +argeo.node.repo.type=h2 +org.osgi.service.http.port=8080 +osgi.console=2323 + +# Backend +#argeo.osgi.start.5.apps=org.argeo.suite.apps + +# UI +#argeo.osgi.start.6.apps=org.argeo.suite.apps.web,org.argeo.suite.workbench.rap + +# DO NOT CHANGE BELOW UNLESS YOU KNOW WHAT YOU ARE DOING +osgi.clean=true +osgi.bundles=org.argeo.osgi.boot.jar@start argeo.osgi.bundles=\ /usr/local/share/osgi;in=**/*.jar,\ /usr/share/osgi;in=**/*.jar -argeo.osgi.boot.debug=true - argeo.osgi.start.2.node=\ org.eclipse.equinox.http.servlet,\ org.eclipse.equinox.http.jetty,\ @@ -16,17 +25,10 @@ org.eclipse.rap.rwt.osgi argeo.osgi.start.3.node=\ org.argeo.cms -argeo.osgi.start.4.apps=\ -org.eclipse.gemini.blueprint.extender - -argeo.osgi.start.4.workbench=\ -org.eclipse.equinox.http.registry,\ +argeo.osgi.start.4.node=\ +org.eclipse.gemini.blueprint.extender,\ +org.eclipse.equinox.http.registry -argeo.node.repo.type=h2 -org.osgi.service.http.port=8080 - -# DO NOT CHANGE BELOW UNLESS YOU KNOW WHAT YOU ARE DOING -osgi.bundles=org.argeo.osgi.boot.jar@start org.osgi.framework.bootdelegation=com.sun.jndi.ldap,\ com.sun.jndi.ldap.sasl,\ com.sun.security.jgss,\ diff --git a/dist/argeo-node/rpm/etc/node/log4j.properties b/dist/argeo-node/rpm/etc/node/log4j.properties index f79dc2c6b..d53b851de 100644 --- a/dist/argeo-node/rpm/etc/node/log4j.properties +++ b/dist/argeo-node/rpm/etc/node/log4j.properties @@ -1,13 +1,15 @@ -log4j.rootLogger=WARN, console +log4j.rootLogger=WARN, console, file -log4j.logger.org.argeo=DEBUG +log4j.logger.org.argeo=INFO ## Appenders log4j.appender.console=org.apache.log4j.ConsoleAppender log4j.appender.console.layout=org.apache.log4j.PatternLayout -log4j.appender.console.layout.ConversionPattern= %-5p %d{ISO8601} %m - %c - [%t]%n +log4j.appender.console.layout.ConversionPattern=%-5p %m%n -log4j.appender.file=org.apache.log4j.RollingFileAppender -log4j.appender.file.File=/var/log/node/node.log +log4j.appender.file=org.apache.log4j.DailyRollingFileAppender +log4j.appender.file.File=/var/log/node/node.csv log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern= %-5p %d{ISO8601} [%15.15t] %m - %c%n +log4j.appender.file.layout.ConversionPattern=%d{ISO8601};"%m";%c;%p%n +log4j.appender.file.bufferedIO=true +log4j.appender.file.immediateFlush=false diff --git a/dist/argeo-node/rpm/usr/lib/systemd/system/node.service b/dist/argeo-node/rpm/usr/lib/systemd/system/node.service index 45fab9ada..80cd827a1 100644 --- a/dist/argeo-node/rpm/usr/lib/systemd/system/node.service +++ b/dist/argeo-node/rpm/usr/lib/systemd/system/node.service @@ -6,9 +6,8 @@ Wants=postgresql.service [Service] Type=simple PIDFile=/var/run/node/node.pid -ExecStart=/usr/sbin/node-service start -#ExecReload=/usr/sbin/node-service reload -#ExecStop=/usr/sbin/node-service stop +ExecStart=/usr/sbin/nodectl start +ExecReload=/usr/sbin/nodectl reload SuccessExitStatus=143 [Install] diff --git a/dist/argeo-node/rpm/usr/sbin/node-service b/dist/argeo-node/rpm/usr/sbin/nodectl similarity index 90% rename from dist/argeo-node/rpm/usr/sbin/node-service rename to dist/argeo-node/rpm/usr/sbin/nodectl index 7accca912..26fe3eeaf 100755 --- a/dist/argeo-node/rpm/usr/sbin/node-service +++ b/dist/argeo-node/rpm/usr/sbin/nodectl @@ -14,7 +14,7 @@ LOG_FILE=$LOG_DIR/$APP.log RUN_DIR=/var/run/$APP PID_FILE=$RUN_DIR/$APP.pid -SHUTDOWN_FILE=$RUN_DIR/$APP.shutdown +#SHUTDOWN_FILE=$RUN_DIR/$APP.shutdown OSGI_INSTALL_AREA=/usr/share/osgi/boot OSGI_FRAMEWORK=$OSGI_INSTALL_AREA/org.eclipse.osgi.jar @@ -41,13 +41,12 @@ start() { fi fi - if [ ! -f $CONF_RW/config.ini ]; then +# if [ ! -f $CONF_RW/config.ini ]; then cp --preserve $CONF_DIR/config.ini $CONF_RW/config.ini - fi - touch $SHUTDOWN_FILE +# fi +# touch $SHUTDOWN_FILE cd $EXEC_DIR $JVM \ - -Dargeo.osgi.shutdownFile="$SHUTDOWN_FILE" \ -Dlog4j.configuration="file:$CONF_DIR/log4j.properties" \ -Djava.security.manager= \ -Djava.security.policy="file:$CONF_DIR/node.policy" \ @@ -56,6 +55,10 @@ start() { -data "$DATA_DIR" } +reload() { + echo Not yet implemented +} + stop() { if [ -f $PID_FILE ];then PID=`cat $PID_FILE` @@ -76,8 +79,8 @@ stop() { # notifies application by removing the shutdown file rm -f $SHUTDOWN_FILE - # wait 5 min for application to shutdown, then kill it - TIMEOUT=$((5*60)) + # wait 10 min for application to shutdown, then kill it + TIMEOUT=$((10*60)) BEGIN=$(date +%s) while kill -0 $PID &> /dev/null do @@ -134,6 +137,9 @@ case "$1" in start) start ;; + reload) + reload + ;; stop) stop ;; diff --git a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java index a38444658..bde67e404 100644 --- a/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java +++ b/org.argeo.cms.ui/src/org/argeo/cms/util/SimpleUxContext.java @@ -43,7 +43,8 @@ public class SimpleUxContext implements UxContext { @Override public boolean isMasterData() { - return false; + // TODO make it configurable + return true; } } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java index a70235976..1efc9dc66 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/Activator.java @@ -49,6 +49,7 @@ public class Activator implements BundleActivator { @Override public void start(BundleContext bundleContext) throws Exception { + Runtime.getRuntime().addShutdownHook(new CmsShutdown()); instance = this; this.bc = bundleContext; this.logReaderService = getService(LogReaderService.class); diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java index 126c591e6..07c10f486 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsDeployment.java @@ -157,25 +157,25 @@ public class CmsDeployment implements NodeDeployment { long initDuration = System.currentTimeMillis() - begin; if (log.isTraceEnabled()) log.trace("Kernel initialization took " + initDuration + "ms"); - directorsCut(initDuration); + tributeToFreeSoftware(initDuration); } } - final private void directorsCut(long initDuration) { - // final long ms = 128l + (long) (Math.random() * 128d); - long ms = initDuration / 100; - log.info("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software..."); - long beginNano = System.nanoTime(); - try { - Thread.sleep(ms, 0); - } catch (InterruptedException e) { - // silent + final private void tributeToFreeSoftware(long initDuration) { + if (log.isTraceEnabled()) { + long ms = initDuration / 100; + log.trace("Spend " + ms + "ms" + " reflecting on the progress brought to mankind" + " by Free Software..."); + long beginNano = System.nanoTime(); + try { + Thread.sleep(ms, 0); + } catch (InterruptedException e) { + // silent + } + long durationNano = System.nanoTime() - beginNano; + final double M = 1000d * 1000d; + double sleepAccuracy = ((double) durationNano) / (ms * M); + log.trace("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %"); } - long durationNano = System.nanoTime() - beginNano; - final double M = 1000d * 1000d; - double sleepAccuracy = ((double) durationNano) / (ms * M); - if (log.isDebugEnabled()) - log.debug("Sleep accuracy: " + String.format("%.2f", 100 - (sleepAccuracy * 100 - 100)) + " %"); } private void prepareNodeRepository(Repository deployedNodeRepository) { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsShutdown.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsShutdown.java new file mode 100644 index 000000000..a62ee7f10 --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsShutdown.java @@ -0,0 +1,70 @@ +package org.argeo.cms.internal.kernel; + +import java.io.IOException; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleContext; +import org.osgi.framework.FrameworkEvent; +import org.osgi.framework.FrameworkUtil; +import org.osgi.framework.launch.Framework; + +/** Shutdowns the OSGi framework */ +class CmsShutdown extends Thread { + public final int EXIT_OK = 0; + public final int EXIT_ERROR = 1; + public final int EXIT_TIMEOUT = 2; + public final int EXIT_UNKNOWN = 3; + + private final Log log = LogFactory.getLog(CmsShutdown.class); + // private final BundleContext bc = + // FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext(); + private final Framework framework; + + /** Shutdown timeout in ms */ + private long timeout = 10 * 60 * 1000; + + public CmsShutdown() { + super("CMS Shutdown Hook"); + framework = (Framework) FrameworkUtil.getBundle(CmsShutdown.class).getBundleContext().getBundle(0); + } + + @Override + public void run() { + if (framework.getState() != Bundle.ACTIVE) { + return; + } + + if (log.isDebugEnabled()) + log.debug("Shutting down OSGi framework..."); + try { + // shutdown framework + framework.stop(); + // wait for shutdown + FrameworkEvent shutdownEvent = framework.waitForStop(timeout); + int stoppedType = shutdownEvent.getType(); + Runtime runtime = Runtime.getRuntime(); + if (stoppedType == FrameworkEvent.STOPPED) { + // close VM + //System.exit(EXIT_OK); + } else if (stoppedType == FrameworkEvent.ERROR) { + log.error("The OSGi framework stopped with an error"); + runtime.halt(EXIT_ERROR); + } else if (stoppedType == FrameworkEvent.WAIT_TIMEDOUT) { + log.error("The OSGi framework hasn't stopped after " + timeout + "ms." + + " Forcibly terminating the JVM..."); + runtime.halt(EXIT_TIMEOUT); + } else { + log.error("Unknown state of OSGi framework after " + timeout + "ms." + + " Forcibly terminating the JVM... (" + shutdownEvent + ")"); + runtime.halt(EXIT_UNKNOWN); + } + } catch (Exception e) { + e.printStackTrace(); + log.error("Unexpected exception " + e + " in shutdown hook. " + " Forcibly terminating the JVM..."); + Runtime.getRuntime().halt(EXIT_UNKNOWN); + } + } + +} diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java index eebf71ccb..0b5bd0d48 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/CmsState.java @@ -5,6 +5,7 @@ import static bitronix.tm.TransactionManagerServices.getTransactionSynchronizati import static java.util.Locale.ENGLISH; import java.io.File; +import java.lang.management.ManagementFactory; import java.net.InetAddress; import java.net.UnknownHostException; import java.nio.file.spi.FileSystemProvider; @@ -48,7 +49,7 @@ public class CmsState implements NodeState { private ThreadGroup threadGroup = new ThreadGroup("CMS"); private KernelThread kernelThread; - private List shutdownHooks = new ArrayList<>(); + private List stopHooks = new ArrayList<>(); private final String stateUuid; private final boolean cleanState; @@ -66,7 +67,7 @@ public class CmsState implements NodeState { availableSince = System.currentTimeMillis(); if (log.isDebugEnabled()) - log.debug("## CMS STARTED " + this.stateUuid + (cleanState ? " (clean state) " : " ")); + log.debug("## CMS starting... stateUuid=" + this.stateUuid + (cleanState ? " (clean state) " : " ")); initI18n(); initServices(); @@ -90,7 +91,7 @@ public class CmsState implements NodeState { // JCR RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory(); - shutdownHooks.add(() -> repositoryServiceFactory.shutdown()); + stopHooks.add(() -> repositoryServiceFactory.shutdown()); bc.registerService(ManagedServiceFactory.class, repositoryServiceFactory, LangUtils.dico(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID)); @@ -99,7 +100,7 @@ public class CmsState implements NodeState { // Security NodeUserAdmin userAdmin = new NodeUserAdmin(NodeConstants.ROLES_BASEDN); - shutdownHooks.add(() -> userAdmin.destroy()); + stopHooks.add(() -> userAdmin.destroy()); bc.registerService(ManagedServiceFactory.class, userAdmin, LangUtils.dico(Constants.SERVICE_PID, NodeConstants.NODE_USER_ADMIN_PID)); @@ -132,7 +133,7 @@ public class CmsState implements NodeState { tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath()); } BitronixTransactionManager transactionManager = getTransactionManager(); - shutdownHooks.add(() -> transactionManager.shutdown()); + stopHooks.add(() -> transactionManager.shutdown()); BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); // register bc.registerService(TransactionManager.class, transactionManager, null); @@ -143,20 +144,22 @@ public class CmsState implements NodeState { } void shutdown() { + if (log.isDebugEnabled()) + log.debug("CMS stopping... stateUuid=" + this.stateUuid + (cleanState ? " (clean state) " : " ")); + if (kernelThread != null) kernelThread.destroyAndJoin(); + applyStopHooks(); - applyShutdownHooks(); - - if (log.isDebugEnabled()) - log.debug("## CMS STOPPED"); + long duration = ((System.currentTimeMillis() - availableSince) / 1000) / 60; + log.info("## ARGEO CMS STOPPED after " + (duration / 60) + "h " + (duration % 60) + "min uptime ##"); } /** Apply shutdown hoos in reverse order. */ - private void applyShutdownHooks() { - for (int i = shutdownHooks.size() - 1; i >= 0; i--) { + private void applyStopHooks() { + for (int i = stopHooks.size() - 1; i >= 0; i--) { try { - shutdownHooks.get(i).run(); + stopHooks.get(i).run(); } catch (Exception e) { log.error("Could not run shutdown hook #" + i); } @@ -189,62 +192,4 @@ public class CmsState implements NodeState { public String getHostname() { return hostname; } - - /** 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 != null && !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; - } - } diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java index 78eb68289..1c7cb1497 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/FirstInit.java @@ -75,7 +75,7 @@ class FirstInit { if (keyStorePassword == null) keyStorePassword = "changeit"; if (!Files.exists(keyStorePath)) - createSelfSignedKeyStore(keyStorePath); + createSelfSignedKeyStore(keyStorePath, keyStorePassword); props.put(JettyConstants.SSL_KEYSTORETYPE, "PKCS12"); props.put(JettyConstants.SSL_KEYSTORE, keyStorePath.toString()); props.put(JettyConstants.SSL_PASSWORD, keyStorePassword); @@ -115,18 +115,18 @@ class FirstInit { // Business roles String userAdminUris = getFrameworkProp(NodeConstants.USERADMIN_URIS); if (userAdminUris == null) { - String demoBaseDn = "dc=example,dc=com"; - File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif"); - if (!businessRolesFile.exists()) - try { - FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(demoBaseDn + ".ldif"), - businessRolesFile); - } catch (IOException e) { - throw new CmsException("Cannot copy demo resource", e); - } - userAdminUris = businessRolesFile.toURI().toString(); - log.warn("## DEV Using dummy base DN " + demoBaseDn); - // TODO downgrade security level + String demoBaseDn = "dc=example,dc=com"; + File businessRolesFile = new File(nodeBaseDir, demoBaseDn + ".ldif"); + if (!businessRolesFile.exists()) + try { + FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(demoBaseDn + ".ldif"), + businessRolesFile); + } catch (IOException e) { + throw new CmsException("Cannot copy demo resource", e); + } + userAdminUris = businessRolesFile.toURI().toString(); + log.warn("## DEV Using dummy base DN " + demoBaseDn); + // TODO downgrade security level } for (String userAdminUri : userAdminUris.split(" ")) uris.add(userAdminUri); @@ -158,10 +158,10 @@ class FirstInit { return res; } - + /** - * Called before node initialisation, in order populate OSGi instance are - * with some files (typically LDIF, etc). + * Called before node initialisation, in order populate OSGi instance are with + * some files (typically LDIF, etc). */ static void prepareInstanceArea() { String nodeInit = getFrameworkProp(NodeConstants.NODE_INIT); @@ -196,11 +196,11 @@ class FirstInit { } } - private void createSelfSignedKeyStore(Path keyStorePath) { + private void createSelfSignedKeyStore(Path keyStorePath, String keyStorePassword) { // for (Provider provider : Security.getProviders()) // System.out.println(provider.getName()); File keyStoreFile = keyStorePath.toFile(); - char[] ksPwd = "changeit".toCharArray(); + char[] ksPwd = keyStorePassword.toCharArray(); char[] keyPwd = Arrays.copyOf(ksPwd, ksPwd.length); if (!keyStoreFile.exists()) { try { diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/GogoShellKiller.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/GogoShellKiller.java new file mode 100644 index 000000000..940fc8d9f --- /dev/null +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/GogoShellKiller.java @@ -0,0 +1,64 @@ +package org.argeo.cms.internal.kernel; + +/** + * Workaround for killing Gogo shell by system shutdown. + * + * @see https://issues.apache.org/jira/browse/FELIX-4208 + */ +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; + // No non-deamon threads left, forcibly halting the VM + Runtime.getRuntime().halt(0); + } + + private ThreadGroup getRootThreadGroup(ThreadGroup tg) { + if (tg == null) + tg = Thread.currentThread().getThreadGroup(); + if (tg.getParent() == null) + return tg; + else + return getRootThreadGroup(tg.getParent()); + } + + private int getNonDaemonCount(ThreadGroup rootThreadGroup) { + Thread[] threads = new Thread[rootThreadGroup.activeCount()]; + rootThreadGroup.enumerate(threads); + int nonDameonCount = 0; + for (Thread t : threads) + if (t != null && !t.isDaemon()) + nonDameonCount++; + return nonDameonCount; + } + + private 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 diff --git a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java index 14d88144d..e83d6c10e 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/kernel/NodeLogger.java @@ -156,18 +156,25 @@ class NodeLogger implements ArgeoLogger, LogListener { if (sr != null) { sb.append(' '); String[] objectClasses = (String[]) sr.getProperty(Constants.OBJECTCLASS); - sb.append(arrayToString(objectClasses)); + if (isSpringApplicationContext(objectClasses)) { + sb.append("{org.springframework.context.ApplicationContext}"); + Object symbolicName = sr.getProperty(Constants.BUNDLE_SYMBOLICNAME); + if (symbolicName != null) + sb.append(" " + Constants.BUNDLE_SYMBOLICNAME + ": " + symbolicName); + } else { + sb.append(arrayToString(objectClasses)); + } Object cn = sr.getProperty(NodeConstants.CN); if (cn != null) sb.append(" " + NodeConstants.CN + ": " + cn); Object factoryPid = sr.getProperty(ConfigurationAdmin.SERVICE_FACTORYPID); if (factoryPid != null) sb.append(" " + ConfigurationAdmin.SERVICE_FACTORYPID + ": " + factoryPid); -// else { -// Object servicePid = sr.getProperty(Constants.SERVICE_PID); -// if (servicePid != null) -// sb.append(" " + Constants.SERVICE_PID + ": " + servicePid); -// } + // else { + // Object servicePid = sr.getProperty(Constants.SERVICE_PID); + // if (servicePid != null) + // sb.append(" " + Constants.SERVICE_PID + ": " + servicePid); + // } // servlets Object whiteBoardPattern = sr.getProperty(KernelConstants.WHITEBOARD_PATTERN_PROP); if (whiteBoardPattern != null) @@ -177,11 +184,12 @@ class NodeLogger implements ArgeoLogger, LogListener { Object contextName = sr.getProperty(KernelConstants.CONTEXT_NAME_PROP); if (contextName != null) sb.append(" " + KernelConstants.CONTEXT_NAME_PROP + ": " + contextName); - + // user directories Object baseDn = sr.getProperty(UserAdminConf.baseDn.name()); if (baseDn != null) sb.append(" " + UserAdminConf.baseDn.name() + ": " + baseDn); + } return sb.toString(); } @@ -198,6 +206,15 @@ class NodeLogger implements ArgeoLogger, LogListener { return sb.toString(); } + private boolean isSpringApplicationContext(String[] objectClasses) { + for (String clss : objectClasses) { + if (clss.equals("org.eclipse.gemini.blueprint.context.DelegatedExecutionOsgiBundleApplicationContext")) { + return true; + } + } + return false; + } + // // ARGEO LOGGER // -- 2.30.2