You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by cz...@apache.org on 2015/03/10 17:30:23 UTC

svn commit: r1665607 - in /sling/trunk/installer/providers/jcr/src: main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java

Author: cziegeler
Date: Tue Mar 10 16:30:23 2015
New Revision: 1665607

URL: http://svn.apache.org/r1665607
Log:
SLING-4490 : Refactor JCR installer

Modified:
    sling/trunk/installer/providers/jcr/src/main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java
    sling/trunk/installer/providers/jcr/src/test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java

Modified: sling/trunk/installer/providers/jcr/src/main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/providers/jcr/src/main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java?rev=1665607&r1=1665606&r2=1665607&view=diff
==============================================================================
--- sling/trunk/installer/providers/jcr/src/main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java (original)
+++ sling/trunk/installer/providers/jcr/src/main/java/org/apache/sling/installer/provider/jcr/impl/JcrInstaller.java Tue Mar 10 16:30:23 2015
@@ -29,6 +29,7 @@ import java.util.Hashtable;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import javax.jcr.Node;
@@ -69,10 +70,10 @@ import org.slf4j.LoggerFactory;
  */
 @Component(label="%jcrinstall.name", description="%jcrinstall.description", immediate=true, metatype=true)
 @Properties({
-    @Property(name="service.description", value="Sling JCR Install Service"),
-    @Property(name="service.vendor", value="The Apache Software Foundation"),
+    @Property(name=Constants.SERVICE_DESCRIPTION, value="Sling JCR Install Service"),
+    @Property(name=Constants.SERVICE_VENDOR, value="The Apache Software Foundation"),
     @Property(name=UpdateHandler.PROPERTY_SCHEMES, value=JcrInstaller.URL_SCHEME, unbounded=PropertyUnbounded.ARRAY),
-    @Property(name="service.ranking", intValue=100)
+    @Property(name=Constants.SERVICE_RANKING, intValue=100)
 })
 @Service(value=UpdateHandler.class)
 public class JcrInstaller implements EventListener, UpdateHandler, ManagedService {
@@ -101,26 +102,6 @@ public class JcrInstaller implements Eve
     private static final String MIME_TXT = "text/plain";
     private static final String ENCODING = "UTF-8";
 
-    /**
-     * This class watches the repository for installable resources
-     */
-    @Reference
-    private SlingRepository repository;
-
-    /**
-     * Additional installation folders are activated based
-     * on the current RunMode. For example, /libs/foo/install.dev
-     * if the current run mode is "dev".
-     */
-    @Reference
-    private SlingSettingsService settings;
-
-    /**
-     * The OsgiInstaller installs resources in the OSGi framework.
-     */
-    @Reference
-    private OsgiInstaller installer;
-
     /** Default regexp for watched folders */
     public static final String DEFAULT_FOLDER_NAME_REGEXP = ".*/install$";
 
@@ -147,21 +128,6 @@ public class JcrInstaller implements Eve
     public static final String PROP_SEARCH_PATH = "sling.jcrinstall.search.path";
     public static final String [] DEFAULT_SEARCH_PATH = { "/libs:100", "/apps:200" };
 
-    /** List of watched folders */
-    private volatile List<WatchedFolder> watchedFolders;
-
-    /** Session shared by all WatchedFolder */
-    private volatile Session session;
-
-    /** The component context. */
-    private volatile ComponentContext componentContext;
-
-    /** Service reg for managed service. */
-    private volatile ServiceRegistration managedServiceRef;
-
-    /** Configuration from managed service (old pid) */
-    private volatile Dictionary<?, ?> oldConfiguration;
-
     public static final String DEFAULT_NEW_CONFIG_PATH = "sling/install";
     @Property(value=DEFAULT_NEW_CONFIG_PATH)
     public static final String PROP_NEW_CONFIG_PATH = "sling.jcrinstall.new.config.path";
@@ -176,7 +142,37 @@ public class JcrInstaller implements Eve
     @Property(boolValue=DEFAULT_ENABLE_WRITEBACK)
     public static final String PROP_ENABLE_WRITEBACK = "sling.jcrinstall.enable.writeback";
 
-    private volatile EventListener moveEventListener;
+    /**
+     * This class watches the repository for installable resources
+     */
+    @Reference
+    private SlingRepository repository;
+
+    /**
+     * Additional installation folders are activated based
+     * on the current RunMode. For example, /libs/foo/install.dev
+     * if the current run mode is "dev".
+     */
+    @Reference
+    private SlingSettingsService settings;
+
+    /**
+     * The OsgiInstaller installs resources in the OSGi framework.
+     */
+    @Reference
+    private OsgiInstaller installer;
+
+    /** List of watched folders */
+    private volatile List<WatchedFolder> watchedFolders;
+
+    /** The component context. */
+    private volatile ComponentContext componentContext;
+
+    /** Service reg for managed service. */
+    private volatile ServiceRegistration managedServiceRef;
+
+    /** Configuration from managed service (old pid) */
+    private volatile Dictionary<?, ?> oldConfiguration;
 
     /** Convert Nodes to InstallableResources */
     static interface NodeConverter {
@@ -184,9 +180,6 @@ public class JcrInstaller implements Eve
     	throws RepositoryException;
     }
 
-    /** Detect newly created folders that we must watch */
-    private final List<RootFolderListener> listeners = new LinkedList<RootFolderListener>();
-
     /** Timer used to call updateFoldersList() */
     private final RescanTimer updateFoldersListTimer = new RescanTimer();
 
@@ -194,120 +187,140 @@ public class JcrInstaller implements Eve
     private final static AtomicInteger bgThreadCounter = new AtomicInteger();
     class StoppableThread extends Thread {
 
-
         /** Used for synchronizing. */
         final Object lock = new Object();
-        volatile boolean active = true;
+
+        final AtomicBoolean active = new AtomicBoolean(true);
+
+        private final AtomicBoolean running = new AtomicBoolean(false);
 
         private final InstallerConfig cfg;
 
+        /** Detect newly created folders that we must watch */
+        private final List<RootFolderListener> listeners = new LinkedList<RootFolderListener>();
+
+        private volatile EventListener moveEventListener;
+
+        /** Session shared by all WatchedFolder */
+        private volatile Session session;
+
         StoppableThread(final InstallerConfig cfg) {
             this.cfg = cfg;
-            synchronized (JcrInstaller.class) {
-                setName("JcrInstaller." + String.valueOf(bgThreadCounter.incrementAndGet()));
-            }
+            setName("JcrInstaller." + String.valueOf(bgThreadCounter.incrementAndGet()));
             setDaemon(true);
-        }
 
-        @Override
-        public final void run() {
-            logger.info("Background thread {} starting", Thread.currentThread().getName());
-            synchronized ( this.lock ) {
-                if ( this.active ) {
-                    try {
-                        // open session
-                        session = repository.loginAdministrative(repository.getDefaultWorkspace());
-
-                        for (String path : cfg.getRoots()) {
-                            listeners.add(new RootFolderListener(session, cfg.getFolderNameFilter(), path, updateFoldersListTimer));
-                            logger.debug("Configured root folder: {}", path);
-                        }
+            try {
+                // open session
+                session = repository.loginAdministrative(repository.getDefaultWorkspace());
 
-                        // Watch for events on the root - that might be one of our root folders
-                        session.getWorkspace().getObservationManager().addEventListener(JcrInstaller.this,
-                                Event.NODE_ADDED | Event.NODE_REMOVED,
-                                "/",
-                                false, // isDeep
-                                null,
-                                null,
-                                true); // noLocal
-                        // add special observation listener for move events
-                        JcrInstaller.this.moveEventListener = new EventListener() {
-
-                            /**
-                             * @see javax.jcr.observation.EventListener#onEvent(javax.jcr.observation.EventIterator)
-                             */
-                            public void onEvent(final EventIterator events) {
-                                try {
-                                    while (events.hasNext()) {
-                                        final Event e = events.nextEvent();
-                                        JcrInstaller.this.checkChanges(cfg, e.getIdentifier());
-                                        JcrInstaller.this.checkChanges(cfg, e.getPath());
-                                    }
-                                } catch (final RepositoryException re) {
-                                    logger.warn("RepositoryException in onEvent", re);
-                                }
+                for (String path : cfg.getRoots()) {
+                    listeners.add(new RootFolderListener(session, cfg.getFolderNameFilter(), path, updateFoldersListTimer));
+                    logger.debug("Configured root folder: {}", path);
+                }
+
+                // Watch for events on the root - that might be one of our root folders
+                session.getWorkspace().getObservationManager().addEventListener(JcrInstaller.this,
+                        Event.NODE_ADDED | Event.NODE_REMOVED,
+                        "/",
+                        false, // isDeep
+                        null,
+                        null,
+                        true); // noLocal
+                // add special observation listener for move events
+                this.moveEventListener = new EventListener() {
+
+                    /**
+                     * @see javax.jcr.observation.EventListener#onEvent(javax.jcr.observation.EventIterator)
+                     */
+                    public void onEvent(final EventIterator events) {
+                        try {
+                            while (events.hasNext()) {
+                                final Event e = events.nextEvent();
+                                JcrInstaller.this.checkChanges(cfg, e.getIdentifier());
+                                JcrInstaller.this.checkChanges(cfg, e.getPath());
                             }
-                        };
-                        session.getWorkspace().getObservationManager().addEventListener(
-                                moveEventListener,
-                                Event.NODE_MOVED,
-                                "/",
-                                true, // isDeep
-                                null,
-                                null,
-                                true); // noLocal
-
-                        logger.debug("Watching for node events on / to detect removal/add of our root folders");
-
-
-                        // Find paths to watch and create WatchedFolders to manage them
-                        watchedFolders = new LinkedList<WatchedFolder>();
-                        for(String root : cfg.getRoots()) {
-                            findPathsToWatch(cfg, root, watchedFolders);
-                        }
-
-                        // Scan watchedFolders and register resources with installer
-                        for(final WatchedFolder f : watchedFolders) {
-                            f.start();
-                        }
-                    } catch (final RepositoryException re) {
-                        logger.error("Repository exception during startup - deactivating installer!", re);
-                        active = false;
-                        final ComponentContext ctx = componentContext;
-                        if ( ctx  != null ) {
-                            final String name = (String) componentContext.getProperties().get(
-                                    ComponentConstants.COMPONENT_NAME);
-                            ctx.disableComponent(name);
+                        } catch (final RepositoryException re) {
+                            logger.warn("RepositoryException in onEvent", re);
                         }
                     }
+                };
+                session.getWorkspace().getObservationManager().addEventListener(
+                        moveEventListener,
+                        Event.NODE_MOVED,
+                        "/",
+                        true, // isDeep
+                        null,
+                        null,
+                        true); // noLocal
+
+                logger.debug("Watching for node events on / to detect removal/add of our root folders");
+
+
+                // Find paths to watch and create WatchedFolders to manage them
+                watchedFolders = new LinkedList<WatchedFolder>();
+                for(String root : cfg.getRoots()) {
+                    findPathsToWatch(cfg, session, root, watchedFolders);
                 }
-            }
 
-            if ( this.active ) {
-                final List<InstallableResource> resources = new LinkedList<InstallableResource>();
+                // Scan watchedFolders and register resources with installer
                 for(final WatchedFolder f : watchedFolders) {
-                    if ( this.active ) {
-                        try {
-                            final WatchedFolder.ScanResult r = f.scan();
-                            logger.debug("Startup: {} provides resources {}", f, r.toAdd);
-                            resources.addAll(r.toAdd);
-                        } catch (final RepositoryException re) {
-                            if ( this.active ) {
-                                logger.error("Repository exception during scanning.", re);
-                            }
-                        }
-                    }
+                    f.start();
                 }
+                final List<InstallableResource> resources = new LinkedList<InstallableResource>();
+                for(final WatchedFolder f : watchedFolders) {
+                    final WatchedFolder.ScanResult r = f.scan();
+                    logger.debug("Startup: {} provides resources {}", f, r.toAdd);
+                    resources.addAll(r.toAdd);
+                }
+                logger.debug("Registering {} resources with OSGi installer: {}", resources.size(), resources);
+                installer.registerResources(URL_SCHEME, resources.toArray(new InstallableResource[resources.size()]));
+            } catch (final RepositoryException re) {
+                logger.error("Repository exception during startup - deactivating installer!", re);
+                active.set(false);
+            }
+        }
 
-                if ( this.active ) {
-                    logger.debug("Registering {} resources with OSGi installer: {}", resources.size(), resources);
-                    installer.registerResources(URL_SCHEME, resources.toArray(new InstallableResource[resources.size()]));
+        public void shutdown() {
+            while ( running.get() ) {
+                try {
+                    Thread.sleep(10);
+                } catch (InterruptedException e) {
+                    Thread.currentThread().interrupt();
                 }
             }
+            try {
+                if (session != null) {
+                    for(RootFolderListener wfc : listeners) {
+                        wfc.cleanup(session);
+                    }
+                    session.getWorkspace().getObservationManager().removeEventListener(JcrInstaller.this);
+                    if ( moveEventListener != null ) {
+                        session.getWorkspace().getObservationManager().removeEventListener(moveEventListener);
+                        moveEventListener = null;
+                    }
+                }
+            } catch (final RepositoryException e) {
+                logger.warn("Exception in stop()", e);
+            }
+            if ( session != null ) {
+                session.logout();
+                session = null;
+            }
+            listeners.clear();
+
+            watchedFolders = null;
+        }
 
-            while (active) {
-                runOneCycle(cfg);
+        @Override
+        public final void run() {
+            logger.info("Background thread {} starting", Thread.currentThread().getName());
+            while (this.active.get()) {
+                running.set(true);
+                try {
+                    runOneCycle(cfg, session);
+                } finally {
+                    running.set(false);
+                }
             }
             logger.info("Background thread {} done", Thread.currentThread().getName());
             counters[RUN_LOOP_COUNTER] = -1;
@@ -337,7 +350,16 @@ public class JcrInstaller implements Eve
         final InstallerConfig cfg = new InstallerConfig(logger, componentContext, oldConfiguration, settings);
 
         backgroundThread = new StoppableThread(cfg);
-        backgroundThread.start();
+        if ( !backgroundThread.active.get() ) {
+            final ComponentContext ctx = componentContext;
+            if ( ctx  != null ) {
+                final String name = (String) componentContext.getProperties().get(
+                        ComponentConstants.COMPONENT_NAME);
+                ctx.disableComponent(name);
+            }
+        } else {
+            backgroundThread.start();
+        }
     }
 
     /**
@@ -357,34 +379,14 @@ public class JcrInstaller implements Eve
 
     	if ( backgroundThread != null ) {
     	    synchronized ( backgroundThread.lock ) {
-    	        backgroundThread.active = false;
+    	        backgroundThread.active.set(false);
     	        backgroundThread.lock.notify();
     	    }
             logger.debug("Waiting for " + backgroundThread.getName() + " Thread to end...");
-            backgroundThread = null;
-    	}
 
-        try {
-            if (session != null) {
-                for(RootFolderListener wfc : listeners) {
-                    wfc.cleanup(session);
-                }
-                session.getWorkspace().getObservationManager().removeEventListener(this);
-                if ( moveEventListener != null ) {
-                    session.getWorkspace().getObservationManager().removeEventListener(moveEventListener);
-                    moveEventListener = null;
-                }
-            }
-        } catch (final RepositoryException e) {
-            logger.warn("Exception in stop()", e);
-        }
-        if ( session != null ) {
-            session.logout();
-            session = null;
-        }
-        listeners.clear();
-
-        watchedFolders = null;
+            this.backgroundThread.shutdown();
+            this.backgroundThread = null;
+    	}
     }
 
 
@@ -412,7 +414,8 @@ public class JcrInstaller implements Eve
 
     /** Find the paths to watch under rootPath, according to our folderNameFilter,
      * 	and add them to result */
-    private void findPathsToWatch(final InstallerConfig cfg, final String rootPath, final List<WatchedFolder> result) throws RepositoryException {
+    private void findPathsToWatch(final InstallerConfig cfg, final Session session,
+            final String rootPath, final List<WatchedFolder> result) throws RepositoryException {
         Session s = null;
 
         try {
@@ -422,7 +425,7 @@ public class JcrInstaller implements Eve
             } else {
                 logger.debug("Bundles root node {} found, looking for bundle folders inside it", rootPath);
                 final Node n = (Node)s.getItem(rootPath);
-                findPathsUnderNode(cfg, n, result);
+                findPathsUnderNode(cfg, session, n, result);
             }
         } finally {
             if (s != null) {
@@ -435,7 +438,8 @@ public class JcrInstaller implements Eve
      * Add n to result if it is a folder that we must watch, and recurse into its children
      * to do the same.
      */
-    void findPathsUnderNode(final InstallerConfig cfg, final Node n, final List<WatchedFolder> result) throws RepositoryException {
+    void findPathsUnderNode(final InstallerConfig cfg, final Session session,
+            final Node n, final List<WatchedFolder> result) throws RepositoryException {
         final String path = n.getPath();
         final int priority = cfg.getFolderNameFilter().getPriority(path);
         if (priority > 0) {
@@ -448,7 +452,7 @@ public class JcrInstaller implements Eve
         }
         final NodeIterator it = n.getNodes();
         while (it.hasNext()) {
-            findPathsUnderNode(cfg, it.nextNode(), result);
+            findPathsUnderNode(cfg, session, it.nextNode(), result);
         }
     }
 
@@ -474,14 +478,14 @@ public class JcrInstaller implements Eve
      *  @return a list of InstallableResource that must be unregistered,
      *  	for folders that have been removed
      */
-    private List<String> updateFoldersList(final InstallerConfig cfg) throws Exception {
+    private List<String> updateFoldersList(final InstallerConfig cfg, final Session session) throws Exception {
         logger.debug("Updating folder list.");
 
         final List<String> result = new LinkedList<String>();
 
         final List<WatchedFolder> newFolders = new ArrayList<WatchedFolder>();
 	    for(String root : cfg.getRoots()) {
-	        findPathsToWatch(cfg, root, newFolders);
+	        findPathsToWatch(cfg, session, root, newFolders);
 	    }
 	    for(WatchedFolder wf : newFolders) {
 	        addWatchedFolder(wf);
@@ -527,6 +531,10 @@ public class JcrInstaller implements Eve
         return cfg;
     }
 
+    Session getSession() {
+        return this.backgroundThread.session;
+    }
+
     /**
      * @see javax.jcr.observation.EventListener#onEvent(javax.jcr.observation.EventIterator)
      */
@@ -552,7 +560,7 @@ public class JcrInstaller implements Eve
     /**
      * Run periodic scans of our watched folders, and watch for folders creations/deletions.
      */
-    public void runOneCycle(final InstallerConfig cfg) {
+    public void runOneCycle(final InstallerConfig cfg, final Session session) {
         logger.debug("Running watch cycle.");
 
         try {
@@ -561,7 +569,7 @@ public class JcrInstaller implements Eve
             if (anyWatchFolderNeedsScan()) {
                 session.refresh(false);
                 didRefresh = true;
-                if (scanningIsPaused(cfg)) {
+                if (scanningIsPaused(cfg, session)) {
                     if (!pauseMessageLogged) {
                         //Avoid flooding the logs every 500 msec so log at info level once
                         logger.info("Detected signal for pausing the JCR Provider i.e. child nodes found under path {}. " +
@@ -611,7 +619,7 @@ public class JcrInstaller implements Eve
                 }
                 updateFoldersListTimer.reset();
                 counters[UPDATE_FOLDERS_LIST_COUNTER]++;
-                final List<String> toRemove = updateFoldersList(cfg);
+                final List<String> toRemove = updateFoldersList(cfg, session);
                 if ( toRemove.size() > 0 ) {
                     logger.info("Removing resource from OSGi installer (folder deleted): {}", toRemove);
                     installer.updateResources(URL_SCHEME, null,
@@ -634,7 +642,7 @@ public class JcrInstaller implements Eve
         counters[RUN_LOOP_COUNTER]++;
     }
 
-    boolean scanningIsPaused(final InstallerConfig cfg) throws RepositoryException {
+    boolean scanningIsPaused(final InstallerConfig cfg, final Session session) throws RepositoryException {
         if (session.nodeExists(cfg.getPauseScanNodePath())) {
             Node node = session.getNode(cfg.getPauseScanNodePath());
             boolean result = node.hasNodes();

Modified: sling/trunk/installer/providers/jcr/src/test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java
URL: http://svn.apache.org/viewvc/sling/trunk/installer/providers/jcr/src/test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java?rev=1665607&r1=1665606&r2=1665607&view=diff
==============================================================================
--- sling/trunk/installer/providers/jcr/src/test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java (original)
+++ sling/trunk/installer/providers/jcr/src/test/java/org/apache/sling/installer/provider/jcr/impl/ScanningLoopTest.java Tue Mar 10 16:30:23 2015
@@ -88,11 +88,11 @@ public class ScanningLoopTest extends Re
     }
 
     public void testDefaultScanPauseFalse() throws Exception{
-        assertFalse(installer.scanningIsPaused(installer.getConfiguration()));
+        assertFalse(installer.scanningIsPaused(installer.getConfiguration(), installer.getSession()));
     }
 
     public void testPauseScan() throws Exception{
-        assertFalse(installer.scanningIsPaused(installer.getConfiguration()));
+        assertFalse(installer.scanningIsPaused(installer.getConfiguration(), installer.getSession()));
 
         Node n = contentHelper.createFolder(JcrInstaller.PAUSE_SCAN_NODE_PATH);
         Node testNode = n.addNode("foo.example.pause");
@@ -100,7 +100,7 @@ public class ScanningLoopTest extends Re
 
         eventHelper.waitForEvents(TIMEOUT);
 
-        assertTrue(installer.scanningIsPaused(installer.getConfiguration()));
+        assertTrue(installer.scanningIsPaused(installer.getConfiguration(), installer.getSession()));
         final long sf = installer.getCounters()[JcrInstaller.SCAN_FOLDERS_COUNTER];
         final long uc = installer.getCounters()[JcrInstaller.UPDATE_FOLDERS_LIST_COUNTER];