Improve JCR repository lifecycle
authorMathieu Baudier <mbaudier@argeo.org>
Tue, 11 Jan 2022 09:56:38 +0000 (10:56 +0100)
committerMathieu Baudier <mbaudier@argeo.org>
Tue, 11 Jan 2022 09:56:38 +0000 (10:56 +0100)
org.argeo.cms.jcr/src/org/argeo/cms/jcr/internal/RepositoryContextsFactory.java
org.argeo.cms/src/org/argeo/cms/internal/osgi/CmsActivator.java
org.argeo.cms/src/org/argeo/cms/internal/osgi/GogoShellKiller.java [new file with mode: 0644]
org.argeo.cms/src/org/argeo/cms/internal/runtime/CmsStateImpl.java
org.argeo.cms/src/org/argeo/cms/internal/runtime/GogoShellKiller.java [deleted file]

index 1a1fda1f08756c47aba039545be7abaeadfc8576..2b9ad0043f1b5731c42d11b498a4859167971564 100644 (file)
@@ -1,11 +1,14 @@
 package org.argeo.cms.jcr.internal;
 
+import java.io.IOException;
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Dictionary;
 import java.util.HashMap;
 import java.util.Map;
 
 import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
 import javax.jcr.RepositoryFactory;
 
 import org.apache.jackrabbit.core.RepositoryContext;
@@ -26,15 +29,16 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
 
        private Map<String, RepositoryContext> repositories = new HashMap<String, RepositoryContext>();
        private Map<String, Object> pidToCn = new HashMap<String, Object>();
-       
+
        public void init() {
-               
+
        }
 
        public void destroy() {
                for (String pid : repositories.keySet()) {
                        try {
-                               repositories.get(pid).getRepository().shutdown();
+                               RepositoryContext repositoryContext = repositories.get(pid);
+                               repositoryContext.getRepository().shutdown();
                                if (log.isDebugEnabled())
                                        log.debug("Shut down repository " + pid
                                                        + (pidToCn.containsKey(pid) ? " (" + pidToCn.get(pid) + ")" : ""));
@@ -44,7 +48,6 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
                }
        }
 
-
        @Override
        public String getName() {
                return "Jackrabbit repository service factory";
@@ -58,10 +61,19 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
                if (properties == null)
                        return;
 
-               if (repositories.containsKey(pid)) {
-                       log.warn("Ignore update of Jackrabbit repository " + pid);
-                       return;
-               }
+               Object cn = properties.get(CmsConstants.CN);
+               if (cn != null)
+                       for (String otherPid : pidToCn.keySet()) {
+                               Object o = pidToCn.get(otherPid);
+                               if (cn.equals(o)) {
+                                       RepositoryContext repositoryContext = repositories.remove(otherPid);
+                                       repositories.put(pid, repositoryContext);
+                                       if (log.isDebugEnabled())
+                                               log.debug("Ignore update of Jackrabbit repository " + cn);
+                                       // FIXME perform a proper update (also of the OSGi service)
+                                       return;
+                               }
+                       }
 
                try {
                        Object labeledUri = properties.get(RepoConf.labeledUri.name());
@@ -72,7 +84,6 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
                                Dictionary<String, Object> props = LangUtils.dict(Constants.SERVICE_PID, pid);
                                // props.put(ArgeoJcrConstants.JCR_REPOSITORY_URI,
                                // properties.get(RepoConf.labeledUri.name()));
-                               Object cn = properties.get(CmsConstants.CN);
                                if (cn != null) {
                                        props.put(CmsConstants.CN, cn);
                                        // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, cn);
@@ -80,45 +91,38 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
                                }
                                CmsJcrActivator.registerService(RepositoryContext.class, repositoryContext, props);
                        } else {
-                               try {
-                                       Object cn = properties.get(CmsConstants.CN);
-                                       Object defaultWorkspace = properties.get(RepoConf.defaultWorkspace.name());
-                                       if (defaultWorkspace == null)
-                                               defaultWorkspace = RepoConf.defaultWorkspace.getDefault();
-                                       URI uri = new URI(labeledUri.toString());
+                               Object defaultWorkspace = properties.get(RepoConf.defaultWorkspace.name());
+                               if (defaultWorkspace == null)
+                                       defaultWorkspace = RepoConf.defaultWorkspace.getDefault();
+                               URI uri = new URI(labeledUri.toString());
 //                                     RepositoryFactory repositoryFactory = bc
 //                                                     .getService(bc.getServiceReference(RepositoryFactory.class));
-                                       RepositoryFactory repositoryFactory = CmsJcrActivator.getService(RepositoryFactory.class);
-                                       Map<String, String> parameters = new HashMap<String, String>();
-                                       parameters.put(RepoConf.labeledUri.name(), uri.toString());
-                                       parameters.put(RepoConf.defaultWorkspace.name(), defaultWorkspace.toString());
-                                       Repository repository = repositoryFactory.getRepository(parameters);
-                                       // Repository repository = NodeUtils.getRepositoryByUri(repositoryFactory,
-                                       // uri.toString());
-                                       Dictionary<String, Object> props = LangUtils.dict(Constants.SERVICE_PID, pid);
-                                       props.put(RepoConf.labeledUri.name(),
-                                                       new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
-                                                                       .toString());
-                                       if (cn != null) {
-                                               props.put(CmsConstants.CN, cn);
-                                               // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, cn);
-                                               pidToCn.put(pid, cn);
-                                       }
-                                       CmsJcrActivator.registerService(Repository.class, repository, props);
-
-                                       // home
-                                       if (cn.equals(CmsConstants.NODE_REPOSITORY)) {
-                                               Dictionary<String, Object> homeProps = LangUtils.dict(CmsConstants.CN,
-                                                               CmsConstants.EGO_REPOSITORY);
-                                               EgoRepository homeRepository = new EgoRepository(repository, true);
-                                               CmsJcrActivator.registerService(Repository.class, homeRepository, homeProps);
-                                       }
-                               } catch (Exception e) {
-                                       // TODO Auto-generated catch block
-                                       e.printStackTrace();
+                               RepositoryFactory repositoryFactory = CmsJcrActivator.getService(RepositoryFactory.class);
+                               Map<String, String> parameters = new HashMap<String, String>();
+                               parameters.put(RepoConf.labeledUri.name(), uri.toString());
+                               parameters.put(RepoConf.defaultWorkspace.name(), defaultWorkspace.toString());
+                               Repository repository = repositoryFactory.getRepository(parameters);
+                               // Repository repository = NodeUtils.getRepositoryByUri(repositoryFactory,
+                               // uri.toString());
+                               Dictionary<String, Object> props = LangUtils.dict(Constants.SERVICE_PID, pid);
+                               props.put(RepoConf.labeledUri.name(),
+                                               new URI(uri.getScheme(), null, uri.getHost(), uri.getPort(), uri.getPath(), null, null)
+                                                               .toString());
+                               if (cn != null) {
+                                       props.put(CmsConstants.CN, cn);
+                                       // props.put(NodeConstants.JCR_REPOSITORY_ALIAS, cn);
+                                       pidToCn.put(pid, cn);
+                               }
+                               CmsJcrActivator.registerService(Repository.class, repository, props);
+
+                               // home
+                               if (cn.equals(CmsConstants.NODE_REPOSITORY)) {
+                                       Dictionary<String, Object> homeProps = LangUtils.dict(CmsConstants.CN, CmsConstants.EGO_REPOSITORY);
+                                       EgoRepository homeRepository = new EgoRepository(repository, true);
+                                       CmsJcrActivator.registerService(Repository.class, homeRepository, homeProps);
                                }
                        }
-               } catch (Exception e) {
+               } catch (RepositoryException | URISyntaxException | IOException e) {
                        throw new IllegalStateException("Cannot create Jackrabbit repository " + pid, e);
                }
 
@@ -132,5 +136,4 @@ public class RepositoryContextsFactory implements ManagedServiceFactory {
                        log.debug("Deleted repository " + pid);
        }
 
-
 }
index d28ffdbb225e937e26b904e2bbdc5da788d7017d..264f0c1b957729cb172b7bebae8b5fab1fae9c94 100644 (file)
@@ -89,6 +89,8 @@ public class CmsActivator implements BundleActivator {
                } catch (Exception e) {
                        log.error("CMS activator shutdown failed", e);
                }
+               
+               new GogoShellKiller().start();
        }
 
        private void initSecurity() {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/osgi/GogoShellKiller.java b/org.argeo.cms/src/org/argeo/cms/internal/osgi/GogoShellKiller.java
new file mode 100644 (file)
index 0000000..85c686a
--- /dev/null
@@ -0,0 +1,67 @@
+package org.argeo.cms.internal.osgi;
+
+import java.io.IOException;
+
+/**
+ * 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) // no need to bother if it is not here
+                       return;
+               while (getNonDaemonCount(rootTg) > 2) {
+                       try {
+                               Thread.sleep(100);
+                       } catch (InterruptedException e) {
+                               // silent
+                       }
+               }
+               gogoShellThread = findGogoShellThread(rootTg);
+               if (gogoShellThread == null)
+                       return;
+               System.exit(0);
+               // 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("pipe-gosh --login --noshutdown"))
+                               return thread;
+               }
+               return null;
+       }
+
+}
\ No newline at end of file
index cf203e5e61bacf4de33bd5c1c4f182682d06611e..c071283949b3d688d7738fb4ee79cc9482348e10 100644 (file)
@@ -3,8 +3,6 @@ package org.argeo.cms.internal.runtime;
 import java.net.InetAddress;
 import java.net.URL;
 import java.net.UnknownHostException;
-import java.util.ArrayList;
-import java.util.List;
 
 import javax.security.auth.login.Configuration;
 
@@ -29,7 +27,7 @@ public class CmsStateImpl implements CmsState {
        private Long availableSince;
 
 //     private ThreadGroup threadGroup = new ThreadGroup("CMS");
-       private List<Runnable> stopHooks = new ArrayList<>();
+//     private List<Runnable> stopHooks = new ArrayList<>();
 
        private String stateUuid;
 //     private final boolean cleanState;
@@ -180,15 +178,16 @@ public class CmsStateImpl implements CmsState {
        public void destroy() {
                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
-               }
+//             Thread stopHookThread = new Thread(() -> applyStopHooks(), "Apply Argeo Stop Hooks");
+//             stopHookThread.start();
+//             try {
+//                     stopHookThread.join(10 * 60 * 1000);
+//             } catch (InterruptedException e) {
+//                     // silent
+//             }
 
 //             internalExecutorService.shutdown();
 
@@ -197,19 +196,19 @@ public class CmsStateImpl implements CmsState {
        }
 
        /** 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;
-       }
+//     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() {
diff --git a/org.argeo.cms/src/org/argeo/cms/internal/runtime/GogoShellKiller.java b/org.argeo.cms/src/org/argeo/cms/internal/runtime/GogoShellKiller.java
deleted file mode 100644 (file)
index 56df43a..0000000
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.argeo.cms.internal.runtime;
-
-/**
- * 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("pipe-gosh --login --noshutdown"))
-                               return thread;
-               }
-               return null;
-       }
-
-}
\ No newline at end of file