You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2008/09/10 11:58:26 UTC

svn commit: r693761 - in /incubator/sling/trunk/extensions/jcrinstall/src: main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/

Author: bdelacretaz
Date: Wed Sep 10 02:58:25 2008
New Revision: 693761

URL: http://svn.apache.org/viewvc?rev=693761&view=rev
Log:
SLING-646 - work in progress, ResourceDetectionTest implemented

Added:
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java   (with props)
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java   (with props)
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java   (with props)
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java   (with props)
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java   (with props)
Modified:
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java
    incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/WatchedFolder.java
    incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java

Added: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java?rev=693761&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java Wed Sep 10 02:58:25 2008
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import java.io.InputStream;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+/** Provides data (InputStream, last modified data) about
+ * 	JCR nodes which are files.
+ * 	TODO: Yes, this should be a generic JCR utility.
+ */
+class FileDataProvider {
+    /**
+     * The relative path of the data and last modified date of an nt:file node
+     */
+    public static final String JCR_CONTENT = "jcr:content";
+    public static final String JCR_CONTENT_DATA = JCR_CONTENT + "/jcr:data";
+    public static final String JCR_LAST_MODIFIED = "jcr:lastModified";
+    public static final String JCR_CONTENT_LAST_MODIFIED = JCR_CONTENT + "/" + JCR_LAST_MODIFIED;
+    
+    private final InputStream inputStream;
+    private final long lastModified;
+    
+	FileDataProvider(Node n) throws RepositoryException {
+        if (n.hasProperty(JCR_CONTENT_LAST_MODIFIED)) {
+        	lastModified = n.getProperty(JCR_CONTENT_LAST_MODIFIED).getDate().getTimeInMillis();
+        } else {
+        	lastModified = -1;
+	    }
+	    
+        if(n.hasProperty(JCR_CONTENT_DATA)) {
+        	inputStream = n.getProperty(JCR_CONTENT_DATA).getStream();
+        } else {
+        	inputStream = null;
+        }
+	}
+	
+	boolean isFile() {
+		return inputStream != null && lastModified != -1;
+	}
+
+	InputStream getInputStream() {
+		return inputStream;
+	}
+
+	long getLastModified() {
+		return lastModified;
+	}
+}

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FileDataProvider.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java?rev=693761&r1=693760&r2=693761&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/RepositoryObserver.java Wed Sep 10 02:58:25 2008
@@ -63,6 +63,9 @@
     /** Default regexp for watched folders */
     public static final String DEFAULT_FOLDER_NAME_REGEXP = ".*/install$";
     
+    /** Scan delay for watched folders */
+    private final long scanDelayMsec = 1000L;
+    
     protected static final Logger log = LoggerFactory.getLogger(WatchedFolder.class);
     
     /** Upon activation, find folders to watch under our roots, and observe those
@@ -118,12 +121,12 @@
     }
     
     /** Add WatchedFolders that have been discovered by our WatchedFolderCreationListeners, if any */
-    void addNewWatchedFolders() {
+    void addNewWatchedFolders() throws RepositoryException {
     	for(WatchedFolderCreationListener w : listeners) {
     		final Set<String> paths = w.getAndClearPaths();
     		if(paths != null) {
     			for(String path : paths) {
-    				folders.add(new WatchedFolder(path, osgiController));
+    				folders.add(new WatchedFolder(repository, path, osgiController, scanDelayMsec));
     			}
     		}
     	}
@@ -160,7 +163,7 @@
     void findWatchedFolders(Node n, Set<WatchedFolder> setToUpdate) throws RepositoryException 
     {
         if (folderNameFilter.accept(n.getPath())) {
-            setToUpdate.add(new WatchedFolder(n.getPath(), osgiController));
+            setToUpdate.add(new WatchedFolder(repository, n.getPath(), osgiController, scanDelayMsec));
         }
         final NodeIterator it = n.getNodes();
         while (it.hasNext()) {
@@ -177,4 +180,11 @@
         }
         return path;
     }
+    
+    /** Let our WatchedFolders run their scanning cycles */ 
+    void runOneCycle() throws Exception {
+    	for(WatchedFolder wf : folders) {
+    		wf.scanIfNeeded();
+    	}
+    }
 }

Modified: incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/WatchedFolder.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/WatchedFolder.java?rev=693761&r1=693760&r2=693761&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/WatchedFolder.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/main/java/org/apache/sling/jcr/jcrinstall/jcr/impl/WatchedFolder.java Wed Sep 10 02:58:25 2008
@@ -18,6 +18,20 @@
  */
 package org.apache.sling.jcr.jcrinstall.jcr.impl;
 
+import java.io.IOException;
+import java.io.InputStream;
+
+import javax.jcr.Item;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jcrinstall.osgi.JcrInstallException;
 import org.apache.sling.jcr.jcrinstall.osgi.OsgiController;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -26,20 +40,43 @@
  *  to it (non-recursively) and sending the appropriate messages
  *  to the OsgiController service.
  */
-class WatchedFolder {
+class WatchedFolder implements EventListener {
     private final String path;
     private final OsgiController controller;
+    private long nextScan;
+    private final Session session;
     protected static final Logger log = LoggerFactory.getLogger(WatchedFolder.class);
     
-    WatchedFolder(String path, OsgiController ctrl) {
+    /**
+     * After receiving JCR events, we wait for this many msec before
+     * re-scanning the folder, as events often come in bursts.
+     */
+    private final long scanDelayMsec;
+
+    WatchedFolder(SlingRepository repository, String path, OsgiController ctrl, long scanDelayMsec) throws RepositoryException {
         this.path = path;
         this.controller = ctrl;
+        this.scanDelayMsec = scanDelayMsec;
+        session = repository.loginAdministrative(repository.getDefaultWorkspace());
+        
+        // observe any changes in our folder, but not recursively
+        final int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED
+                | Event.PROPERTY_ADDED | Event.PROPERTY_CHANGED | Event.PROPERTY_REMOVED;
+        final boolean isDeep = false;
+        final boolean noLocal = true;
+        session.getWorkspace().getObservationManager().addEventListener(this, eventTypes, path,
+                isDeep, null, null, noLocal);
         
         log.info("Watching folder " + path);
     }
     
     void cleanup() {
-    	// TODO stop listening to events
+    	try {
+	    	session.getWorkspace().getObservationManager().removeEventListener(this);
+	    	session.logout();
+    	} catch(RepositoryException re) {
+    		log.warn("RepositoryException in cleanup()", re);
+    	}
     }
     
     @Override
@@ -65,4 +102,68 @@
     String getPath() {
         return path;
     }
+    
+    /** Set our timer whenever an event occurs, to wait
+     * 	a bit before processing event bursts.
+     */
+    public void onEvent(EventIterator it) {
+        nextScan = System.currentTimeMillis() + scanDelayMsec;
+    }
+    
+    /**
+     * 	If our timer allows it, scan our folder Node for updates
+     * 	and deletes.
+     */
+    void scanIfNeeded() throws Exception {
+        if (nextScan != -1 && System.currentTimeMillis() > nextScan) {
+            nextScan = -1;
+        }
+        scan();
+    }
+    
+    /** Scan our folder and inform OsgiController of any changes */
+    protected void scan() throws Exception {
+        log.debug("Scanning {}", path);
+        
+        Node folder = null;
+        if(session.itemExists(path)) {
+        	Item i = session.getItem(path);
+        	if(i.isNode()) {
+        		folder = (Node)i;
+        	}
+        }
+        
+        if(folder == null) {
+        	log.info("Folder {} does not exist (or not anymore), cannot scan", path);
+        	return;
+        }
+        	
+        
+        // TODO: check deletions
+        
+        // Check adds and updates, for all child nodes that are files
+        final NodeIterator it = folder.getNodes();
+        while(it.hasNext()) {
+        	final Node n = it.nextNode();
+        	final FileDataProvider dp = new FileDataProvider(n);
+        	if(!dp.isFile()) {
+        		log.debug("Node {} does not seem to be a file, ignored", n.getPath());
+        	}
+        	installOrUpdate(n.getPath(), dp.getInputStream(), dp.getLastModified());
+        }
+    }
+    
+    /** Install or update the given resource, as needed */ 
+    protected void installOrUpdate(String path, InputStream data, Long lastModified) throws IOException, JcrInstallException {
+    	final long currentLastModified = controller.getLastModified(path);
+    	if(currentLastModified == -1) {
+    		log.info("Resource {} was not installed yet, installing in OsgiController", path);
+    		controller.installOrUpdate(path, lastModified, data);
+    	} else if(currentLastModified < lastModified) {
+    		log.info("Resource {} has been updated, updating in OsgiController", path);
+    		controller.installOrUpdate(path, lastModified, data);
+    	} else {
+    		log.info("Resource {} not modified, ignoring", path);
+    	}
+    }
 }

Added: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java?rev=693761&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java Wed Sep 10 02:58:25 2008
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import java.io.InputStream;
+import java.util.Calendar;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+class ContentHelper {
+    public static final String NT_FOLDER = "nt:folder";
+    public static final String NT_FILE = "nt:file";
+    public static final String NT_RESOURCE = "nt:resource";
+    public static final String JCR_CONTENT = "jcr:content";
+    public static final String JCR_LASTMODIFIED = "jcr:lastModified";
+    public static final String JCR_MIMETYPE = "jcr:mimeType";
+    public static final String JCR_ENCODING = "jcr:encoding";
+    public static final String JCR_DATA = "jcr:data";
+    
+    final String [] WATCHED_FOLDERS = {
+        "libs/foo/bar/install",
+        "libs/foo/wii/install",
+        "apps/install"
+    };
+
+    final String [] IGNORED_FOLDERS = {
+        "libs/foo/bar/installed",
+        "apps/noninstall"
+    };
+    
+    private final Session session;
+    
+    ContentHelper(Session s) {
+    	session = s;
+    }
+
+    void cleanupContent() throws Exception {
+    	final String [] paths = { "libs", "apps" }; 
+    	for(String path : paths) {
+            if(session.getRootNode().hasNode(path)) {
+                session.getRootNode().getNode(path).remove();
+            }
+    	}
+    }
+    
+    void setupContent() throws Exception {
+    	cleanupContent();
+        for(String folder : WATCHED_FOLDERS) {
+            createFolder(folder);
+        }
+        for(String folder : IGNORED_FOLDERS) {
+            createFolder(folder);
+        }
+    }
+    
+    void createFolder(String path) throws Exception {
+        final String [] parts = path.split("/");
+        Node n = session.getRootNode();
+        for(String part : parts) {
+            if(n.hasNode(part)) {
+                n = n.getNode(part);
+            } else {
+                n = n.addNode(part);
+            }
+        }
+        session.save();
+    }
+    
+    void createOrUpdateFile(String path, InputStream data, long lastModified) throws RepositoryException {
+        final String relPath = path.substring(1);
+        Node f = null;
+        Node res = null;
+        if(session.getRootNode().hasNode(relPath)) {
+            f = session.getRootNode().getNode(relPath);
+            res = f.getNode(JCR_CONTENT);
+        } else {
+            f = session.getRootNode().addNode(relPath);
+            res = f.addNode(JCR_CONTENT);
+        }
+        
+        final Calendar c = Calendar.getInstance();
+        c.setTimeInMillis(lastModified);
+        res.setProperty(JCR_LASTMODIFIED, c);
+        res.setProperty(JCR_DATA, data);
+        f.getParent().save();
+    }
+}
\ No newline at end of file

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ContentHelper.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java?rev=693761&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java Wed Sep 10 02:58:25 2008
@@ -0,0 +1,85 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.jcr.observation.Event;
+import javax.jcr.observation.EventIterator;
+import javax.jcr.observation.EventListener;
+
+/** Used by tests to wait until JCR notification events
+ * 	have been delivered.
+ */
+class EventHelper implements EventListener {
+	private final Session session;
+	private int eventCount;
+	public static final String WAIT_NODE_FOLDER = "WAIT_NODE";
+	public static final String WAIT_NODE_NODE = EventHelper.class.getSimpleName();
+	private final Node waitNodeFolder;
+	
+	EventHelper(Session s) throws RepositoryException {
+		session = s;
+		
+        final int eventTypes = Event.NODE_ADDED | Event.NODE_REMOVED;
+        final boolean isDeep = true;
+        final boolean noLocal = false;
+        session.getWorkspace().getObservationManager().addEventListener(
+        		this, eventTypes, "/" + WAIT_NODE_FOLDER, isDeep, null, null, noLocal);
+        
+        if(session.getRootNode().hasNode(WAIT_NODE_FOLDER)) {
+        	waitNodeFolder = session.getRootNode().getNode(WAIT_NODE_FOLDER);
+        } else {
+        	waitNodeFolder = session.getRootNode().addNode(WAIT_NODE_FOLDER, "nt:unstructured");
+        }
+        session.save();
+	}
+	
+    public void onEvent(EventIterator it) {
+    	eventCount++;
+    }
+    
+    /** To make sure observation events have been delivered,
+     * 	create or delete a a node and wait for the corresponding
+     * 	events to be received.
+     */
+	void waitForEvents(long timeoutMsec) throws RepositoryException {
+		final int targetEventCount = eventCount + 1;
+		
+		if(waitNodeFolder.hasNode(WAIT_NODE_NODE)) {
+			waitNodeFolder.getNode(WAIT_NODE_NODE).remove();
+		} else {
+			waitNodeFolder.addNode(WAIT_NODE_NODE);
+		}
+		session.save();
+		
+    	final long end = System.currentTimeMillis() + timeoutMsec;
+    	while(eventCount < targetEventCount && System.currentTimeMillis() < end) {
+    		try {
+    			Thread.sleep(100);
+    		} catch(InterruptedException ignored) {
+    		}
+    	}
+    	
+    	if(eventCount < targetEventCount) {
+    		throw new IllegalStateException("Event counter did not reach " + targetEventCount + ", waited " + timeoutMsec + " msec");
+    	}
+    }
+}

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/EventHelper.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Modified: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java?rev=693761&r1=693760&r2=693761&view=diff
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java (original)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/FindWatchedFoldersTest.java Wed Sep 10 02:58:25 2008
@@ -18,44 +18,28 @@
  */
 package org.apache.sling.jcr.jcrinstall.jcr.impl;
 
-import java.lang.reflect.Field;
 import java.util.Set;
 
-import javax.jcr.Node;
-import javax.jcr.RepositoryException;
 import javax.jcr.Session;
-import javax.jcr.observation.Event;
-import javax.jcr.observation.EventIterator;
-import javax.jcr.observation.EventListener;
 
 import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
 import org.apache.sling.jcr.api.SlingRepository;
 
 /** Test the "find watched folders" feature of the RepositoryObserver */
-public class FindWatchedFoldersTest extends RepositoryTestBase implements EventListener {
+public class FindWatchedFoldersTest extends RepositoryTestBase {
     
     SlingRepository repo;
     Session session;
-    String eventPathToWatch;
-    int eventCount;
-    
-    private static final String [] WATCHED_FOLDERS = {
-            "libs/foo/bar/install",
-            "libs/foo/wii/install",
-            "apps/install"
-    };
-    
-    private static final String [] IGNORED_FOLDERS = {
-            "libs/foo/bar/installed",
-            "apps/noninstall"
-    };
+    private EventHelper eventHelper; 
+    private ContentHelper contentHelper;
     
     @Override
     protected void tearDown() throws Exception {
         super.tearDown();
-        session.getWorkspace().getObservationManager().removeEventListener(this);
-        cleanupContent();
+        contentHelper.cleanupContent();
         session.logout();
+        eventHelper = null;
+        contentHelper = null;
     }
 
     @Override
@@ -63,140 +47,42 @@
         super.setUp();
         repo = getRepository();
         session = repo.loginAdministrative(repo.getDefaultWorkspace());
-        cleanupContent();
-        
-        final int eventTypes = Event.NODE_ADDED;
-        final boolean isDeep = true;
-        final boolean noLocal = false;
-        session.getWorkspace().getObservationManager().addEventListener(
-        		this, eventTypes, "/", isDeep, null, null, noLocal);
-    }
-    
-    void cleanupContent() throws Exception {
-    	final String [] paths = { "libs", "apps" }; 
-    	for(String path : paths) {
-            if(session.getRootNode().hasNode(path)) {
-                session.getRootNode().getNode(path).remove();
-            }
-    	}
-    }
-    
-    void setupContent() throws Exception {
-    	cleanupContent();
-        for(String folder : WATCHED_FOLDERS) {
-            createFolder(folder);
-        }
-        for(String folder : IGNORED_FOLDERS) {
-            createFolder(folder);
-        }
+        eventHelper = new EventHelper(session);
+        contentHelper = new ContentHelper(session);
+        contentHelper.cleanupContent();
     }
     
-    public void onEvent(EventIterator it) {
-        try {
-            while(it.hasNext()) {
-                final Event e = it.nextEvent();
-                if(e.getPath().equals(eventPathToWatch)) {
-                	eventCount++;
-                }
-            }
-        } catch(RepositoryException ignored) {
-            // ignore
-        }
-    }
-
-    @SuppressWarnings("unchecked")
     public void testInitialFind() throws Exception {
     	
-    	setupContent();
-        final RepositoryObserver ro = createRepositoryObserver();
+    	contentHelper.setupContent();
+        final RepositoryObserver ro = MiscHelper.createRepositoryObserver(repo, null);
         ro.activate(null);
         
-        final Set<WatchedFolder> wfSet = getWatchedFolders(ro);
-        assertEquals("activate() must find all watched folders", WATCHED_FOLDERS.length, wfSet.size());
+        final Set<WatchedFolder> wfSet = MiscHelper.getWatchedFolders(ro);
+        assertEquals("activate() must find all watched folders", contentHelper.WATCHED_FOLDERS.length, wfSet.size());
         
-        for(String folder : WATCHED_FOLDERS) {
+        for(String folder : contentHelper.WATCHED_FOLDERS) {
             assertTrue("Folder " + folder + " must be watched (watched=" + wfSet + ")", 
-                    folderIsWatched(ro, folder)); 
+                    MiscHelper.folderIsWatched(ro, folder)); 
         }
     }
     
     public void testNewWatchedFolderDetection() throws Exception {
-    	setupContent();
-        final RepositoryObserver ro = createRepositoryObserver();
+    	contentHelper.setupContent();
+        final RepositoryObserver ro = MiscHelper.createRepositoryObserver(repo, null);
         ro.activate(null);
 
         final String newPaths [] = { "libs/tnwf/install", "apps/tnwf/install" };
         for(String newPath : newPaths) {
-            assertFalse(newPath + " must not be watched before test", folderIsWatched(ro, newPath));
+            assertFalse(newPath + " must not be watched before test", MiscHelper.folderIsWatched(ro, newPath));
             
             // Create folder, wait for observation event and check that
             // it is detected
-            final int ec = eventCount;
-            eventPathToWatch = "/" + newPath;
-            createFolder(newPath);
-            waitForEventCount(ec + 1, 5000L);
-            assertFalse(newPath + " must not be watched before calling addNewWatchedFolders()", folderIsWatched(ro, newPath));
+            contentHelper.createFolder(newPath);
+            eventHelper.waitForEvents(5000L);
+            assertFalse(newPath + " must not be watched before calling addNewWatchedFolders()", MiscHelper.folderIsWatched(ro, newPath));
             ro.addNewWatchedFolders();
-            assertTrue(newPath + " must be watched before calling addNewWatchedFolders()", folderIsWatched(ro, newPath));
-        }
-    }
-    
-    void waitForEventCount(int count, long timeoutMsec) {
-    	final long end = System.currentTimeMillis() + timeoutMsec;
-    	while(eventCount < count && System.currentTimeMillis() < end) {
-    		try {
-    			Thread.sleep(100);
-    		} catch(InterruptedException ignored) {
-    		}
-    	}
-    	
-    	if(eventCount < count) {
-    		throw new IllegalStateException("Event counter did not reach " + count + ", waited " + timeoutMsec + " msec");
-    	}
-    }
-    
-    @SuppressWarnings("unchecked")
-	Set<WatchedFolder> getWatchedFolders(RepositoryObserver ro) throws Exception {
-        final Field f = ro.getClass().getDeclaredField("folders");
-        f.setAccessible(true);
-        return (Set<WatchedFolder>)f.get(ro);
-    }
-    
-    boolean folderIsWatched(RepositoryObserver ro, String path) throws Exception {
-    	boolean result = false;
-    	final Set<WatchedFolder> wfSet = getWatchedFolders(ro);
-    	for(WatchedFolder wf : wfSet) {
-    		if(wf.getPath().equals("/" + path)) {
-    			result = true;
-    			break;
-    		}
-    	}
-    	return result;
-    }
-    
-    void createFolder(String path) throws Exception {
-        final String [] parts = path.split("/");
-        Node n = session.getRootNode();
-        for(String part : parts) {
-            if(n.hasNode(part)) {
-                n = n.getNode(part);
-            } else {
-                n = n.addNode(part);
-            }
+            assertTrue(newPath + " must be watched before calling addNewWatchedFolders()", MiscHelper.folderIsWatched(ro, newPath));
         }
-        session.save();
-    }
-    
-    RepositoryObserver createRepositoryObserver() throws Exception {
-        final RepositoryObserver result = new RepositoryObserver();
-        setField(result, "repository", repo);
-        return result;
-    }
-    
-    static void setField(Object target, String name, Object value) throws Exception, IllegalAccessException {
-        final Field f = target.getClass().getDeclaredField(name);
-        f.setAccessible(true);
-        f.set(target, value);
-
-    }
-}
+    }   
+}
\ No newline at end of file

Added: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java?rev=693761&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java Wed Sep 10 02:58:25 2008
@@ -0,0 +1,62 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import java.lang.reflect.Field;
+import java.util.Set;
+
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jcrinstall.osgi.OsgiController;
+
+/** Miscellaneous test helper functions */
+class MiscHelper {
+    static boolean folderIsWatched(RepositoryObserver ro, String path) throws Exception {
+        boolean result = false;
+        final Set<WatchedFolder> wfSet = getWatchedFolders(ro);
+        for(WatchedFolder wf : wfSet) {
+            if(wf.getPath().equals("/" + path)) {
+                result = true;
+                break;
+            }
+        }
+        return result;
+    }
+    
+    @SuppressWarnings("unchecked")
+    static Set<WatchedFolder> getWatchedFolders(RepositoryObserver ro) throws Exception {
+        final Field f = ro.getClass().getDeclaredField("folders");
+        f.setAccessible(true);
+        return (Set<WatchedFolder>)f.get(ro);
+    }
+    
+    static RepositoryObserver createRepositoryObserver(SlingRepository repo, OsgiController c) throws Exception {
+        final RepositoryObserver result = new RepositoryObserver();
+        setField(result, "repository", repo);
+        setField(result, "osgiController", c);
+        return result;
+    }
+    
+    static void setField(Object target, String name, Object value) throws Exception, IllegalAccessException {
+        final Field f = target.getClass().getDeclaredField(name);
+        f.setAccessible(true);
+        f.set(target, value);
+
+    }
+
+}

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/MiscHelper.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL

Added: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java
URL: http://svn.apache.org/viewvc/incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java?rev=693761&view=auto
==============================================================================
--- incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java (added)
+++ incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java Wed Sep 10 02:58:25 2008
@@ -0,0 +1,163 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.sling.jcr.jcrinstall.jcr.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
+import javax.jcr.Session;
+
+import org.apache.sling.commons.testing.jcr.RepositoryTestBase;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.jcrinstall.osgi.OsgiController;
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.Sequence;
+
+/** Test that added/updated/removed resources are
+ * 	correctly translated to OsgiController calls.
+ */
+public class ResourceDetectionTest extends RepositoryTestBase {
+    SlingRepository repo;
+    Session session;
+    private EventHelper eventHelper; 
+    private ContentHelper contentHelper;
+    private Mockery mockery;
+    private Sequence sequence;
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        contentHelper.cleanupContent();
+        session.logout();
+        eventHelper = null;
+        contentHelper = null;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        repo = getRepository();
+        session = repo.loginAdministrative(repo.getDefaultWorkspace());
+        eventHelper = new EventHelper(session);
+        contentHelper = new ContentHelper(session);
+        contentHelper.cleanupContent();
+        
+        mockery = new Mockery();
+        sequence = mockery.sequence(getClass().getSimpleName());
+    }
+    
+    protected String getOsgiResourceLocation(String uri) {
+        return "jcrinstall://" + uri;
+    }
+    
+    /** Add, update and remove resources from the repository
+     *  and verify that the OsgiController receives the
+     *  correct messages
+     */
+    public void testSingleResourceDetection() throws Exception {
+        contentHelper.setupContent();
+        
+        final String dummyJar = "/libs/foo/bar/install/dummy.jar";
+        final InputStream data = new ByteArrayInputStream(dummyJar.getBytes());
+        final long lastModifiedA = System.currentTimeMillis();
+        final long lastModifiedB = lastModifiedA + 1;
+        
+        final OsgiController c = mockery.mock(OsgiController.class);
+        final RepositoryObserver ro = MiscHelper.createRepositoryObserver(repo, c);
+        ro.activate(null);
+        
+        // Define the whole sequence of calls to OsgiController,
+        // Using getLastModified calls to mark the test phases
+        mockery.checking(new Expectations() {{
+            one(c).getLastModified("phase1"); 
+            inSequence(sequence);
+            one(c).getLastModified(dummyJar); will(returnValue(-1L));
+            inSequence(sequence);
+            one(c).installOrUpdate(with(equal(dummyJar)), with(equal(lastModifiedA)), with(any(InputStream.class)));
+            inSequence(sequence);
+            
+            one(c).getLastModified("phase2"); 
+            inSequence(sequence);
+            one(c).getLastModified(dummyJar); will(returnValue(lastModifiedA));
+            inSequence(sequence);
+            
+            one(c).getLastModified("phase3"); 
+            inSequence(sequence);
+            one(c).getLastModified(dummyJar); will(returnValue(lastModifiedA));
+            inSequence(sequence);
+            one(c).installOrUpdate(with(equal(dummyJar)), with(equal(lastModifiedB)), with(any(InputStream.class)));
+            inSequence(sequence);
+        }});
+        
+        // Add two files, run one cycle must install the bundles
+        c.getLastModified("phase1");
+        contentHelper.createOrUpdateFile(dummyJar, data, lastModifiedA);
+        eventHelper.waitForEvents(5000L);
+        ro.runOneCycle();
+        
+        // Updating with the same timestamp must not call install again
+        c.getLastModified("phase2");
+        contentHelper.createOrUpdateFile(dummyJar, data, lastModifiedA);
+        eventHelper.waitForEvents(5000L);
+        ro.runOneCycle();
+        
+        // Updating with a new timestamp must call install again
+        c.getLastModified("phase3");
+        contentHelper.createOrUpdateFile(dummyJar, data, lastModifiedB);
+        eventHelper.waitForEvents(5000L);
+        ro.runOneCycle();
+        
+        mockery.assertIsSatisfied();
+    }
+    
+    public void testMultipleResourceDetection() throws Exception {
+        contentHelper.setupContent();
+        
+        final String [] resources = {
+                "/libs/foo/bar/install/dummy.jar",
+                "/libs/foo/bar/install/dummy.cfg",
+                "/libs/foo/bar/install/dummy.dp"
+        };
+        final InputStream data = new ByteArrayInputStream("hello".getBytes());
+        final long lastModifiedA = System.currentTimeMillis();
+        
+        final OsgiController c = mockery.mock(OsgiController.class);
+        final RepositoryObserver ro = MiscHelper.createRepositoryObserver(repo, c);
+        ro.activate(null);
+        
+        // Define the whole sequence of calls to OsgiController,
+        // Using getLastModified calls to mark the test phases
+        mockery.checking(new Expectations() {{
+            allowing(c).getLastModified(with(any(String.class))); will(returnValue(-1L)); 
+            one(c).installOrUpdate(with(equal(resources[0])), with(equal(lastModifiedA)), with(any(InputStream.class)));
+            one(c).installOrUpdate(with(equal(resources[1])), with(equal(lastModifiedA)), with(any(InputStream.class)));
+            one(c).installOrUpdate(with(equal(resources[2])), with(equal(lastModifiedA)), with(any(InputStream.class)));
+        }});
+        
+        // Add two files, run one cycle must install the bundles
+        for(String file : resources) {
+            contentHelper.createOrUpdateFile(file, data, lastModifiedA);
+        }
+        eventHelper.waitForEvents(5000L);
+        ro.runOneCycle();
+        
+        mockery.assertIsSatisfied();
+    }
+}

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/sling/trunk/extensions/jcrinstall/src/test/java/org/apache/sling/jcr/jcrinstall/jcr/impl/ResourceDetectionTest.java
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision Rev URL