From def1b847ee067994c32f49107e22d3c04eac2b0e Mon Sep 17 00:00:00 2001 From: Mathieu Baudier Date: Fri, 4 Mar 2022 10:23:04 +0100 Subject: [PATCH] Improve Argeo Init shutdown sequence. --- .../cms/internal/runtime/CmsStateImpl.java | 140 +----------------- .../src/org/argeo/init/RuntimeContext.java | 4 +- .../src/org/argeo/init/Service.java | 19 ++- .../org/argeo/init/logging/ThinLogging.java | 12 ++ .../argeo/init/osgi/OsgiRuntimeContext.java | 14 +- 5 files changed, 41 insertions(+), 148 deletions(-) diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java index b493c08ef..a0c4b0c0b 100644 --- a/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java +++ b/org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java @@ -9,7 +9,6 @@ import javax.security.auth.login.Configuration; import org.argeo.api.cms.CmsLog; import org.argeo.api.cms.CmsState; import org.argeo.cms.auth.ident.IdentClient; -import org.argeo.cms.internal.osgi.CmsShutdown; import org.osgi.framework.Constants; /** @@ -17,32 +16,20 @@ import org.osgi.framework.Constants; */ public class CmsStateImpl implements CmsState { private final static CmsLog log = CmsLog.getLog(CmsStateImpl.class); -// private final BundleContext bc = FrameworkUtil.getBundle(CmsState.class).getBundleContext(); - -// private static CmsStateImpl instance; - -// private ExecutorService internalExecutorService; // REFERENCES private Long availableSince; -// private ThreadGroup threadGroup = new ThreadGroup("CMS"); -// private List stopHooks = new ArrayList<>(); - private String stateUuid; // private final boolean cleanState; private String hostname; public void start() { -// instance = this; - - Runtime.getRuntime().addShutdownHook(new CmsShutdown()); -// this.internalExecutorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); +// Runtime.getRuntime().addShutdownHook(new CmsShutdown()); try { initSecurity(); // initArgeoLogger(); -// initNode(); if (log.isTraceEnabled()) log.trace("CMS State started"); @@ -81,139 +68,14 @@ public class CmsStateImpl implements CmsState { Configuration.getConfiguration(); } -// private void initI18n() { -// Object defaultLocaleValue = KernelUtils.getFrameworkProp(CmsConstants.I18N_DEFAULT_LOCALE); -// defaultLocale = defaultLocaleValue != null ? new Locale(defaultLocaleValue.toString()) -// : new Locale(ENGLISH.getLanguage()); -// locales = LocaleUtils.asLocaleList(KernelUtils.getFrameworkProp(CmsConstants.I18N_LOCALES)); -// } - - private void initServices() { - // JTA -// String tmType = KernelUtils.getFrameworkProp(CmsConstants.TRANSACTION_MANAGER, -// CmsConstants.TRANSACTION_MANAGER_SIMPLE); -// if (CmsConstants.TRANSACTION_MANAGER_SIMPLE.equals(tmType)) { -// initSimpleTransactionManager(); -// } else if (CmsConstants.TRANSACTION_MANAGER_BITRONIX.equals(tmType)) { -//// initBitronixTransactionManager(); -// throw new UnsupportedOperationException( -// "Bitronix is not supported anymore, but could be again if there is enough interest."); -// } else { -// throw new IllegalArgumentException("Usupported transaction manager type " + tmType); -// } - - // POI -// POIXMLTypeLoader.setClassLoader(CTConnection.class.getClassLoader()); - - // Tika -// OpenDocumentParser odfParser = new OpenDocumentParser(); -// bc.registerService(Parser.class, odfParser, new Hashtable()); -// PDFParser pdfParser = new PDFParser(); -// bc.registerService(Parser.class, pdfParser, new Hashtable()); -// OOXMLParser ooxmlParser = new OOXMLParser(); -// bc.registerService(Parser.class, ooxmlParser, new Hashtable()); -// TesseractOCRParser ocrParser = new TesseractOCRParser(); -// ocrParser.setLanguage("ara"); -// bc.registerService(Parser.class, ocrParser, new Hashtable()); - -// // JCR -// RepositoryServiceFactory repositoryServiceFactory = new RepositoryServiceFactory(); -// stopHooks.add(() -> repositoryServiceFactory.shutdown()); -// Activator.registerService(ManagedServiceFactory.class, repositoryServiceFactory, -// LangUtils.dict(Constants.SERVICE_PID, NodeConstants.NODE_REPOS_FACTORY_PID)); -// -// NodeRepositoryFactory repositoryFactory = new NodeRepositoryFactory(); -// Activator.registerService(RepositoryFactory.class, repositoryFactory, null); - - // Security -// NodeUserAdmin userAdmin = new NodeUserAdmin(CmsConstants.ROLES_BASEDN, CmsConstants.TOKENS_BASEDN); -// stopHooks.add(() -> userAdmin.destroy()); -// Activator.registerService(ManagedServiceFactory.class, userAdmin, -// LangUtils.dict(Constants.SERVICE_PID, CmsConstants.NODE_USER_ADMIN_PID)); - - } - -// private void initSimpleTransactionManager() { -// SimpleTransactionManager transactionManager = new SimpleTransactionManager(); -// Activator.registerService(WorkControl.class, transactionManager, null); -// Activator.registerService(WorkTransaction.class, transactionManager, null); -//// Activator.registerService(TransactionManager.class, transactionManager, null); -//// Activator.registerService(UserTransaction.class, transactionManager, null); -// // TODO TransactionSynchronizationRegistry -// } - -// private void initBitronixTransactionManager() { -// // TODO manage it in a managed service, as startup could be long -// ServiceReference existingTm = bc.getServiceReference(TransactionManager.class); -// if (existingTm != null) { -// if (log.isDebugEnabled()) -// log.debug("Using provided transaction manager " + existingTm); -// return; -// } -// -// if (!TransactionManagerServices.isTransactionManagerRunning()) { -// bitronix.tm.Configuration tmConf = TransactionManagerServices.getConfiguration(); -// tmConf.setServerId(UUID.randomUUID().toString()); -// -// Bundle bitronixBundle = FrameworkUtil.getBundle(bitronix.tm.Configuration.class); -// File tmBaseDir = bitronixBundle.getDataFile(KernelConstants.DIR_TRANSACTIONS); -// File tmDir1 = new File(tmBaseDir, "btm1"); -// tmDir1.mkdirs(); -// tmConf.setLogPart1Filename(new File(tmDir1, tmDir1.getName() + ".tlog").getAbsolutePath()); -// File tmDir2 = new File(tmBaseDir, "btm2"); -// tmDir2.mkdirs(); -// tmConf.setLogPart2Filename(new File(tmDir2, tmDir2.getName() + ".tlog").getAbsolutePath()); -// } -// BitronixTransactionManager transactionManager = getTransactionManager(); -// stopHooks.add(() -> transactionManager.shutdown()); -// BitronixTransactionSynchronizationRegistry transactionSynchronizationRegistry = getTransactionSynchronizationRegistry(); -// // register -// bc.registerService(TransactionManager.class, transactionManager, null); -// bc.registerService(UserTransaction.class, transactionManager, null); -// bc.registerService(TransactionSynchronizationRegistry.class, transactionSynchronizationRegistry, null); -// if (log.isDebugEnabled()) -// log.debug("Initialised default Bitronix transaction manager"); -// } - public void stop() { if (log.isDebugEnabled()) log.debug("CMS stopping... (" + this.stateUuid + ")"); -// new GogoShellKiller().start(); - - // In a different thread in order to avoid interruptions -// Thread stopHookThread = new Thread(() -> applyStopHooks(), "Apply Argeo Stop Hooks"); -// stopHookThread.start(); -// try { -// stopHookThread.join(10 * 60 * 1000); -// } catch (InterruptedException e) { -// // silent -// } - -// internalExecutorService.shutdown(); 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 applyStopHooks() { -//// for (int i = stopHooks.size() - 1; i >= 0; i--) { -//// try { -//// stopHooks.get(i).run(); -//// } catch (Exception e) { -//// log.error("Could not run shutdown hook #" + i); -//// } -//// } -// // Clean hanging Gogo shell thread -// new GogoShellKiller().start(); -// -//// instance = null; -// } - -// @Override -// public boolean isClean() { -// return cleanState; -// } @Override public Long getAvailableSince() { diff --git a/org.argeo.init/src/org/argeo/init/RuntimeContext.java b/org.argeo.init/src/org/argeo/init/RuntimeContext.java index 7dd8e6c0c..1d3c743ca 100644 --- a/org.argeo.init/src/org/argeo/init/RuntimeContext.java +++ b/org.argeo.init/src/org/argeo/init/RuntimeContext.java @@ -1,5 +1,7 @@ package org.argeo.init; -public interface RuntimeContext extends Runnable, AutoCloseable { +public interface RuntimeContext extends Runnable { void waitForStop(long timeout) throws InterruptedException; + + void close() throws Exception; } diff --git a/org.argeo.init/src/org/argeo/init/Service.java b/org.argeo.init/src/org/argeo/init/Service.java index 03e2f1273..9dcea49d2 100644 --- a/org.argeo.init/src/org/argeo/init/Service.java +++ b/org.argeo.init/src/org/argeo/init/Service.java @@ -31,6 +31,7 @@ public class Service implements Runnable, AutoCloseable { Runtime.getRuntime().addShutdownHook(new Thread(() -> { try { if (Service.runtimeContext != null) { +// System.out.println("Argeo Init stopping with PID " + pid); Service.runtimeContext.close(); Service.runtimeContext.waitForStop(0); } @@ -47,22 +48,26 @@ public class Service implements Runnable, AutoCloseable { // log.log(Logger.Level.DEBUG, key + "=" + System.getProperty(key.toString())); // } try { - try (OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext((Map) config)) { + try { + OsgiRuntimeContext osgiRuntimeContext = new OsgiRuntimeContext((Map) config); osgiRuntimeContext.run(); Service.runtimeContext = osgiRuntimeContext; Service.runtimeContext.waitForStop(0); } catch (NoClassDefFoundError e) { - try (StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map) config)) { - staticRuntimeContext.run(); - Service.runtimeContext = staticRuntimeContext; - Service.runtimeContext.waitForStop(0); - } + StaticRuntimeContext staticRuntimeContext = new StaticRuntimeContext((Map) config); + staticRuntimeContext.run(); + Service.runtimeContext = staticRuntimeContext; + Service.runtimeContext.waitForStop(0); } } catch (Exception e) { e.printStackTrace(); System.exit(1); } - log.log(Logger.Level.DEBUG, "Argeo Init stopping with PID " + pid); + log.log(Logger.Level.DEBUG, "Argeo Init stopped with PID " + pid); } + + public static RuntimeContext getRuntimeContext() { + return runtimeContext; + } } diff --git a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java index e02dae0b4..e21899394 100644 --- a/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java +++ b/org.argeo.init/src/org/argeo/init/logging/ThinLogging.java @@ -26,6 +26,9 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.function.Consumer; +import org.argeo.init.RuntimeContext; +import org.argeo.init.Service; + /** * A thin logging system based on the {@link Logger} framework. It is a * {@link Consumer} of configuration, and can be registered as such. @@ -115,6 +118,15 @@ class ThinLogging implements Consumer> { } private void close() { + RuntimeContext runtimeContext = Service.getRuntimeContext(); + if (runtimeContext != null) { + try { + runtimeContext.waitForStop(0); + } catch (InterruptedException e) { + // silent + } + } + publisher.close(); try { // we ait a bit in order to make sure all messages are flushed diff --git a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java index 6c3e40ce5..373e3d671 100644 --- a/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java +++ b/org.argeo.init/src/org/argeo/init/osgi/OsgiRuntimeContext.java @@ -11,6 +11,7 @@ import java.util.function.Consumer; import org.argeo.init.RuntimeContext; import org.argeo.init.logging.ThinLoggerFinder; +import org.osgi.framework.Bundle; import org.osgi.framework.BundleContext; import org.osgi.framework.BundleException; import org.osgi.framework.Constants; @@ -91,14 +92,25 @@ public class OsgiRuntimeContext implements RuntimeContext { public void waitForStop(long timeout) throws InterruptedException { if (framework == null) throw new IllegalStateException("Framework is not initialised"); + framework.waitForStop(timeout); } - @Override public void close() throws Exception { + // TODO make shutdown of dynamic service more robust + Bundle scrBundle = osgiBoot.getBundlesBySymbolicName().get("org.apache.felix.scr"); + if (scrBundle != null) { + scrBundle.stop(); + while (!(scrBundle.getState() <= Bundle.RESOLVED)) { + Thread.sleep(500); + } + Thread.sleep(1000); + } + stop(framework.getBundleContext()); if (framework != null) framework.stop(); + } } -- 2.30.2