You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by js...@apache.org on 2016/11/18 15:08:20 UTC

svn commit: r1770390 - in /sling/trunk/bundles/jcr/base/src: main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java

Author: jsedding
Date: Fri Nov 18 15:08:20 2016
New Revision: 1770390

URL: http://svn.apache.org/viewvc?rev=1770390&view=rev
Log:
SLING-6285 - Implement LoginAdminWhitelist in JCR Base

- switch to async registration of SlingRepository to ensure whitelist is available

Modified:
    sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
    sling/trunk/bundles/jcr/base/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java

Modified: sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java?rev=1770390&r1=1770389&r2=1770390&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java (original)
+++ sling/trunk/bundles/jcr/base/src/main/java/org/apache/sling/jcr/base/AbstractSlingRepositoryManager.java Fri Nov 18 15:08:20 2016
@@ -21,6 +21,7 @@ package org.apache.sling.jcr.base;
 import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.Dictionary;
+import java.util.concurrent.CountDownLatch;
 
 import javax.jcr.Repository;
 
@@ -103,7 +104,7 @@ public abstract class AbstractSlingRepos
 
     private volatile Loader loader;
 
-    private volatile ServiceReference<LoginAdminWhitelist> whitelistRef;
+    private volatile ServiceTracker<LoginAdminWhitelist, LoginAdminWhitelist> whitelistTracker;
 
     private volatile LoginAdminWhitelist whitelist;
 
@@ -300,11 +301,12 @@ public abstract class AbstractSlingRepos
     // --------- SCR integration -----------------------------------------------
 
     /**
-     * This method actually starts the backing repository instannce and
-     * registeres the repository service.
+     * This method was deprecated with the introduction of asynchronous repository registration. With
+     * asynchronous registration a boolean return value can no longer be guaranteed, as registration
+     * may happen after the method returns.
      * <p>
-     * Multiple subsequent calls to this method without calling {@link #stop()}
-     * first have no effect.
+     * Instead a {@link org.osgi.framework.ServiceListener} for {@link SlingRepository} may be
+     * registered to get informed about its successful registration.
      *
      * @param bundleContext The {@code BundleContext} to register the repository
      *            service (and optionally more services required to operate the
@@ -315,29 +317,66 @@ public abstract class AbstractSlingRepos
      * @param disableLoginAdministrative Whether to disable the
      *            {@code SlingRepository.loginAdministrative} method or not.
      * @return {@code true} if the repository has been started and the service
-     *         is registered.
+     *         is registered; {@code false} if the service has not been registered,
+     *         which may indicate that startup was unsuccessful OR that it is happening
+     *         asynchronously. A more reliable way to determin availability of the
+     *         {@link SlingRepository} as a service is using a
+     *         {@link org.osgi.framework.ServiceListener}.
+     * @deprecated use {@link #start(BundleContext, AbstractSlingRepositoryManager.Config)} instead.
      */
+    @Deprecated
     protected final boolean start(final BundleContext bundleContext, final String defaultWorkspace,
-            final boolean disableLoginAdministrative) {
+                                  final boolean disableLoginAdministrative) {
+        start(bundleContext, new Config(defaultWorkspace, disableLoginAdministrative));
+        return isRepositoryServiceRegistered();
+    }
+
+    /**
+     * Configuration pojo to be passed to the {@link #start(BundleContext, Config)} method.
+     */
+    protected static final class Config {
+
+        protected final String defaultWorkspace;
+
+        protected final boolean disableLoginAdministrative;
+
+        /**
+         * @param defaultWorkspace The name of the default workspace to use to
+         *            login. This may be {@code null} to have the actual repository
+         *            instance define its own default
+         *
+         * @param disableLoginAdministrative Whether to disable the
+         *            {@code SlingRepository.loginAdministrative} method or not.
+         */
+        protected Config(String defaultWorkspace, boolean disableLoginAdministrative) {
+            this.defaultWorkspace = defaultWorkspace;
+            this.disableLoginAdministrative = disableLoginAdministrative;
+        }
+    }
+
+    /**
+     * This method actually starts the backing repository instannce and
+     * registeres the repository service.
+     * <p>
+     * Multiple subsequent calls to this method without calling {@link #stop()}
+     * first have no effect.
+     *
+     * @param bundleContext The {@code BundleContext} to register the repository
+     *            service (and optionally more services required to operate the
+     *            repository)
+     * @param config The configuration to apply to this instance.
+     */
+    protected final void start(final BundleContext bundleContext, final Config config) {
 
         // already setup ?
         if (this.bundleContext != null) {
             log.debug("start: Repository already started and registered");
-            return true;
+            return;
         }
 
         this.bundleContext = bundleContext;
-        this.defaultWorkspace = defaultWorkspace;
-        this.disableLoginAdministrative = disableLoginAdministrative;
-
-        boolean enableWhitelist = !isAllowLoginAdministrativeForBundleOverridden();
-        if (enableWhitelist) {
-            this.whitelistRef = bundleContext.getServiceReference(LoginAdminWhitelist.class);
-            if (whitelistRef == null) {
-                throw new IllegalStateException("Whitelist must not be null");
-            }
-            this.whitelist = bundleContext.getService(whitelistRef);
-        }
+        this.defaultWorkspace = config.defaultWorkspace;
+        this.disableLoginAdministrative = config.disableLoginAdministrative;
 
         this.repoInitializerTracker = new ServiceTracker<SlingRepositoryInitializer, SlingRepositoryInitializerInfo>(bundleContext, SlingRepositoryInitializer.class,
                 new ServiceTrackerCustomizer<SlingRepositoryInitializer, SlingRepositoryInitializerInfo>() {
@@ -377,6 +416,46 @@ public abstract class AbstractSlingRepos
         });
         this.repoInitializerTracker.open();
 
+        boolean enableWhitelist = !isAllowLoginAdministrativeForBundleOverridden();
+        final CountDownLatch waitForWhitelist = new CountDownLatch(enableWhitelist ? 1 : 0);
+        if (enableWhitelist) {
+            whitelistTracker = new ServiceTracker<LoginAdminWhitelist, LoginAdminWhitelist>(bundleContext, LoginAdminWhitelist.class, null) {
+                @Override
+                public LoginAdminWhitelist addingService(final ServiceReference<LoginAdminWhitelist> reference) {
+                    whitelist = bundleContext.getService(reference);
+                    waitForWhitelist.countDown();
+                    return whitelist;
+                }
+            };
+            whitelistTracker.open();
+        }
+
+        if (waitForWhitelist.getCount() > 0) {
+            // start repository asynchronously to allow LoginAdminWhitelist to become available
+            // NOTE: making this conditional allows tests to register a mock whitelist before
+            // activating the RepositoryManager, so they don't need to deal with async startup
+            new Thread("Apache Sling Repository Startup Thread") {
+                @Override
+                public void run() {
+                    try {
+                        waitForWhitelist.await();
+                        initializeAndRegisterRepositoryService();
+                    } catch (InterruptedException e) {
+                        throw new RuntimeException("Interrupted while waiting for LoginAdminWhitelist", e);
+                    }
+                }
+            }.start();
+        } else {
+            initializeAndRegisterRepositoryService();
+        }
+    }
+
+    private boolean isRepositoryServiceRegistered() {
+        return repositoryService != null;
+    }
+
+    private void initializeAndRegisterRepositoryService() {
+        Throwable t = null;
         try {
             log.debug("start: calling acquireRepository()");
             Repository newRepo = this.acquireRepository();
@@ -392,17 +471,11 @@ public abstract class AbstractSlingRepos
                     this.loader = new Loader(this.masterSlingRepository, this.bundleContext);
 
                     log.debug("start: calling SlingRepositoryInitializer");
-                    Throwable t = null;
                     try {
                         executeRepositoryInitializers(this.masterSlingRepository);
-                    } catch(Exception e) {
-                        t = e;
-                    } catch(Error e) {
+                    } catch(Throwable e) {
                         t = e;
-                    }
-                    if(t != null) {
                         log.error("Exception in a SlingRepositoryInitializer, SlingRepository service registration aborted", t);
-                        return false;
                     }
 
                     log.debug("start: calling registerService()");
@@ -410,18 +483,17 @@ public abstract class AbstractSlingRepos
 
                     log.debug("start: registerService() successful, registration=" + repositoryService);
                 }
-                return true;
             }
-        } catch (Throwable t) {
+        } catch (Throwable e) {
             // consider an uncaught problem an error
-            log.error("start: Uncaught Throwable trying to access Repository, calling stopRepository()", t);
-
-            // repository might be partially started, stop anything left
-            stop();
+            log.error("start: Uncaught Throwable trying to access Repository, calling stopRepository()", e);
+            t = e;
+        } finally {
+            if (t != null) {
+                // repository might be partially started, stop anything left
+                stop();
+            }
         }
-
-        // fallback to failure to start the repository
-        return false;
     }
 
     // find out whether allowLoginAdministrativeForBundle is overridden
@@ -458,55 +530,59 @@ public abstract class AbstractSlingRepos
      * This method must be called if overwritten by implementations !!
      */
     protected final void stop() {
-        if (whitelistRef != null) {
-            whitelist = null;
-            bundleContext.ungetService(whitelistRef);
-            whitelistRef = null;
-        }
-
-        if(repoInitializerTracker != null) {
-            repoInitializerTracker.close();
-            repoInitializerTracker = null;
-        }
 
         // ensure the repository is really disposed off
-        if (repository != null || repositoryService != null) {
+        if (repository != null || isRepositoryServiceRegistered()) {
             log.info("stop: Repository still running, forcing shutdown");
 
-            try {
-                if (repositoryService != null) {
-                    try {
-                        log.debug("stop: Unregistering SlingRepository service, registration=" + repositoryService);
-                        unregisterService(repositoryService);
-                    } catch (Throwable t) {
-                        log.info("stop: Uncaught problem unregistering the repository service", t);
+            // make sure we are not concurrently unregistering the repository
+            synchronized (repoInitLock) {
+                try {
+                    if (isRepositoryServiceRegistered()) {
+                        try {
+                            log.debug("stop: Unregistering SlingRepository service, registration=" + repositoryService);
+                            unregisterService(repositoryService);
+                        } catch (Throwable t) {
+                            log.info("stop: Uncaught problem unregistering the repository service", t);
+                        }
+                        repositoryService = null;
                     }
-                    repositoryService = null;
-                }
 
-                if (repository != null) {
-                    Repository oldRepo = repository;
-                    repository = null;
-
-                    // stop loader
-                    if ( this.loader != null ) {
-                        this.loader.dispose();
-                        this.loader = null;
-                    }
-                    // destroy repository
-                    this.destroy(this.masterSlingRepository);
+                    if (repository != null) {
+                        Repository oldRepo = repository;
+                        repository = null;
+
+                        // stop loader
+                        if (this.loader != null) {
+                            this.loader.dispose();
+                            this.loader = null;
+                        }
+                        // destroy repository
+                        this.destroy(this.masterSlingRepository);
 
-                    try {
-                        disposeRepository(oldRepo);
-                    } catch (Throwable t) {
-                        log.info("stop: Uncaught problem disposing the repository", t);
+                        try {
+                            disposeRepository(oldRepo);
+                        } catch (Throwable t) {
+                            log.info("stop: Uncaught problem disposing the repository", t);
+                        }
                     }
+                } catch (Throwable t) {
+                    log.warn("stop: Unexpected problem stopping repository", t);
                 }
-            } catch (Throwable t) {
-                log.warn("stop: Unexpected problem stopping repository", t);
             }
         }
 
+        if(repoInitializerTracker != null) {
+            repoInitializerTracker.close();
+            repoInitializerTracker = null;
+        }
+
+        if (whitelistTracker != null) {
+            whitelist = null;
+            whitelistTracker.close();
+            whitelistTracker = null;
+        }
+
         this.repositoryService = null;
         this.repository = null;
         this.defaultWorkspace = null;

Modified: sling/trunk/bundles/jcr/base/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/jcr/base/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java?rev=1770390&r1=1770389&r2=1770390&view=diff
==============================================================================
--- sling/trunk/bundles/jcr/base/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java (original)
+++ sling/trunk/bundles/jcr/base/src/test/java/org/apache/sling/jcr/base/MockSlingRepositoryManager.java Fri Nov 18 15:08:20 2016
@@ -98,6 +98,6 @@ public class MockSlingRepositoryManager
     }
 
     public void activate(BundleContext context) {
-        start(context, null, loginAdminDisabled);
+        start(context, new Config(null, loginAdminDisabled));
     }
 }
\ No newline at end of file