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 2010/02/19 11:37:27 UTC

svn commit: r911777 - in /sling/trunk/bundles/extensions/fsresource: ./ src/main/java/org/apache/sling/fsprovider/internal/ src/main/resources/META-INF/

Author: cziegeler
Date: Fri Feb 19 10:37:26 2010
New Revision: 911777

URL: http://svn.apache.org/viewvc?rev=911777&view=rev
Log:
SLING-1387 : File system provider should send resource events

Added:
    sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java   (with props)
Modified:
    sling/trunk/bundles/extensions/fsresource/NOTICE
    sling/trunk/bundles/extensions/fsresource/pom.xml
    sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
    sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
    sling/trunk/bundles/extensions/fsresource/src/main/resources/META-INF/NOTICE

Modified: sling/trunk/bundles/extensions/fsresource/NOTICE
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/NOTICE?rev=911777&r1=911776&r2=911777&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/NOTICE (original)
+++ sling/trunk/bundles/extensions/fsresource/NOTICE Fri Feb 19 10:37:26 2010
@@ -1,5 +1,5 @@
 Apache Sling File System Resource Provider
-Copyright 2008-2009 The Apache Software Foundation
+Copyright 2008-2010 The Apache Software Foundation
 
 Apache Sling is based on source code originally developed 
 by Day Software (http://www.day.com/).

Modified: sling/trunk/bundles/extensions/fsresource/pom.xml
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/pom.xml?rev=911777&r1=911776&r2=911777&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/pom.xml (original)
+++ sling/trunk/bundles/extensions/fsresource/pom.xml Fri Feb 19 10:37:26 2010
@@ -89,6 +89,10 @@
             <artifactId>org.osgi.core</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
         </dependency>

Added: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java?rev=911777&view=auto
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java (added)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java Fri Feb 19 10:37:26 2010
@@ -0,0 +1,256 @@
+/*
+ * 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.fsprovider.internal;
+
+import java.io.File;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.Timer;
+import java.util.TimerTask;
+
+import org.apache.sling.api.SlingConstants;
+import org.osgi.service.event.EventAdmin;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class is a monitor for the file system
+ * that periodically checks for changes.
+ */
+public class FileMonitor extends TimerTask {
+
+    /** The logger. */
+    private final Logger logger = LoggerFactory.getLogger(this.getClass());
+
+    private final Timer timer = new Timer();
+    private boolean stop = false;
+    private boolean stopped = true;
+
+    private final Monitorable root;
+
+    private final FsResourceProvider provider;
+
+    /**
+     * Creates a new instance of this class.
+     * @param provider The resource provider.
+     * @param interval The interval between executions of the task, in milliseconds.
+     */
+    public FileMonitor(final FsResourceProvider provider, final long interval) {
+        this.provider = provider;
+        this.root = new Monitorable(this.provider.getProviderRoot(), this.provider.getRootFile());
+        createStatus(this.root);
+        logger.debug("Starting file monitor for {} with an interval of {}ms", this.root.file, interval);
+        timer.schedule(this, 0, interval);
+    }
+
+    /**
+     * Stop periodically executing this task. If the task is currently executing it
+     * will never be run again after the current execution, otherwise it will simply
+     * never run (again).
+     */
+    void stop() {
+        synchronized (timer) {
+            if (!stop) {
+                stop = true;
+                cancel();
+                timer.cancel();
+            }
+
+            boolean interrupted = false;
+            while (!stopped) {
+                try {
+                    timer.wait();
+                }
+                catch (InterruptedException e) {
+                    interrupted = true;
+                }
+            }
+            if (interrupted) {
+                Thread.currentThread().interrupt();
+            }
+        }
+        logger.debug("Stopped file monitor for {}", this.root.file);
+    }
+
+    /**
+     * @see java.util.TimerTask#run()
+     */
+    public void run() {
+        synchronized (timer) {
+            stopped = false;
+            if (stop) {
+                stopped = true;
+                timer.notifyAll();
+                return;
+            }
+        }
+        synchronized ( this ) {
+            try {
+                // if we don't have an event admin, we just skip the check
+                final EventAdmin localEA = this.provider.getEventAdmin();
+                if ( localEA != null ) {
+                    this.check(this.root, localEA);
+                }
+            } catch (Exception e) {
+                // ignore this
+            }
+        }
+        synchronized (timer) {
+            stopped = true;
+            timer.notifyAll();
+        }
+    }
+
+    /**
+     * Check the monitorable
+     * @param monitorable The monitorable to check
+     * @param localEA The event admin
+     */
+    private void check(final Monitorable monitorable, final EventAdmin localEA) {
+        logger.debug("Checking {}", monitorable.file);
+        // if the file is non existing, check if it has been readded
+        if ( monitorable.status instanceof NonExistingStatus ) {
+            if ( monitorable.file.exists() ) {
+                // new file and reset status
+                createStatus(monitorable);
+                sendEvents(monitorable,
+                           SlingConstants.TOPIC_RESOURCE_ADDED,
+                           localEA);
+            }
+        } else {
+            // check if the file has been removed
+            if ( !monitorable.file.exists() ) {
+                // removed file and update status
+                sendEvents(monitorable,
+                           SlingConstants.TOPIC_RESOURCE_REMOVED,
+                           localEA);
+                monitorable.status = NonExistingStatus.SINGLETON;
+            } else {
+                // check for changes
+                final FileStatus fs = (FileStatus)monitorable.status;
+                boolean changed = false;
+                if ( fs.lastModified < monitorable.file.lastModified() ) {
+                    fs.lastModified = monitorable.file.lastModified();
+                    // changed
+                    sendEvents(monitorable,
+                               SlingConstants.TOPIC_RESOURCE_CHANGED,
+                               localEA);
+                    changed = true;
+                }
+                if ( fs instanceof DirStatus ) {
+                    // directory
+                    final DirStatus ds = (DirStatus)fs;
+                    for(int i=0; i<ds.children.length; i++) {
+                        check(ds.children[i], localEA);
+                    }
+                    // if the dir changed we have to update
+                    if ( changed ) {
+                        // and now update
+                        final File[] files = monitorable.file.listFiles();
+                        final Monitorable[] children = new Monitorable[files.length];
+                        for(int i=0; i<files.length; i++) {
+                            // search in old list
+                            for(int m=0;m<ds.children.length;m++) {
+                                if ( ds.children[m].file.equals(files[i]) ) {
+                                    children[i] = ds.children[m];
+                                    break;
+                                }
+                            }
+                            if ( children[i] == null ) {
+                                children[i] = new Monitorable(monitorable.path + '/' + files[i].getName(), files[i]);
+                                children[i].status = NonExistingStatus.SINGLETON;
+                                check(children[i], localEA);
+                            }
+                        }
+                        ds.children = children;
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Send the event async via the event admin.
+     */
+    private void sendEvents(final Monitorable monitorable, final String topic, final EventAdmin localEA) {
+        if ( logger.isDebugEnabled() ) {
+            logger.debug("Detected change for resource {} : {}", monitorable.path, topic);
+        }
+
+        final Dictionary<String, String> properties = new Hashtable<String, String>();
+        properties.put(SlingConstants.PROPERTY_PATH, monitorable.path);
+        final String type = monitorable.status instanceof FileStatus ?
+                FsResource.RESOURCE_TYPE_FILE : FsResource.RESOURCE_TYPE_FOLDER;
+        properties.put(SlingConstants.PROPERTY_RESOURCE_TYPE, type);
+        localEA.postEvent(new org.osgi.service.event.Event(topic, properties));
+    }
+
+    /**
+     * Create a status object for the monitorable
+     */
+    private static void createStatus(final Monitorable monitorable) {
+        if ( !monitorable.file.exists() ) {
+            monitorable.status = NonExistingStatus.SINGLETON;
+        } else if ( monitorable.file.isFile() ) {
+            monitorable.status = new FileStatus(monitorable.file);
+        } else {
+            monitorable.status = new DirStatus(monitorable.file, monitorable.path);
+        }
+    }
+
+    /** The monitorable to hold the resource path, the file and the status. */
+    private static final class Monitorable {
+        public final String path;
+        public final File   file;
+        public Object status;
+
+        public Monitorable(final String path, final File file) {
+            this.path = path;
+            this.file = file;
+        }
+    }
+
+    /** Status for files. */
+    private static class FileStatus {
+        public long lastModified;
+        public FileStatus(final File file) {
+            this.lastModified = file.lastModified();
+        }
+    }
+
+    /** Status for directories. */
+    private static final class DirStatus extends FileStatus {
+        public Monitorable[] children;
+
+        public DirStatus(final File dir, final String path) {
+            super(dir);
+            final File[] files = dir.listFiles();
+            this.children = new Monitorable[files.length];
+            for(int i=0; i<files.length; i++) {
+                this.children[i] = new Monitorable(path + '/' + files[i].getName(), files[i]);
+                FileMonitor.createStatus(this.children[i]);
+            }
+        }
+    }
+
+    /** Status for non existing files. */
+    private static final class NonExistingStatus {
+        public static NonExistingStatus SINGLETON = new NonExistingStatus();
+    }
+}
\ No newline at end of file

Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Propchange: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java?rev=911777&r1=911776&r2=911777&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java Fri Feb 19 10:37:26 2010
@@ -42,13 +42,13 @@
      * The resource type for file system files mapped into the resource tree by
      * the {@link FsResourceProvider} (value is "nt:file").
      */
-    private static final String RESOURCE_TYPE_FILE = "nt:file";
+    static final String RESOURCE_TYPE_FILE = "nt:file";
 
     /**
      * The resource type for file system folders mapped into the resource tree
      * by the {@link FsResourceProvider} (value is "nt:folder").
      */
-    private static final String RESOURCE_TYPE_FOLDER = "nt:folder";
+    static final String RESOURCE_TYPE_FOLDER = "nt:folder";
 
     // default log, assigned on demand
     private Logger log;

Modified: sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java?rev=911777&r1=911776&r2=911777&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java Fri Feb 19 10:37:26 2010
@@ -32,6 +32,7 @@
 import org.apache.sling.api.resource.ResourceProvider;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.osgi.framework.BundleContext;
+import org.osgi.service.event.EventAdmin;
 
 /**
  * The <code>FsResourceProvider</code> is a resource provider which maps
@@ -48,12 +49,13 @@
  *                label="%resource.resolver.name"
  *                description="%resource.resolver.description"
  *                configurationFactory="true"
- * @scr.service
+ * @scr.service interface="ResourceProvider"
  * @scr.property name="service.description" value="Sling Filesystem Resource
  *               Provider"
  * @scr.property name="service.vendor" value="The Apache Software Foundation"
  * @scr.property nameRef="ResourceProvider.ROOTS"
  * @scr.property nameRef="PROP_PROVIDER_FILE"
+ * @scr.property nameRef="PROP_PROVIDER_CHECKINTERVAL" valueRef="DEFAULT_CHECKINTERVAL"
  */
 public class FsResourceProvider implements ResourceProvider {
 
@@ -64,6 +66,14 @@
      */
     public static final String PROP_PROVIDER_FILE = "provider.file";
 
+    /**
+     * The name of the configuration property providing the check interval
+     * for file changes (value is "provider.checkinterval").
+     */
+    public static final String PROP_PROVIDER_CHECKINTERVAL = "provider.checkinterval";
+
+    public static long DEFAULT_CHECKINTERVAL = 1000;
+
     // The location in the resource tree where the resources are mapped
     private String providerRoot;
 
@@ -73,6 +83,12 @@
     // The "root" file or folder in the file system
     private File providerFile;
 
+    /** The monitor to detect file changes. */
+    private FileMonitor monitor;
+
+    /** @scr.reference cardinality="0..1" policy="dynamic" */
+    private EventAdmin eventAdmin;
+
     /**
      * Same as {@link #getResource(ResourceResolver, String)}, i.e. the
      * <code>request</code> parameter is ignored.
@@ -199,14 +215,39 @@
         this.providerRoot = providerRoot;
         this.providerRootPrefix = providerRoot.concat("/");
         this.providerFile = getProviderFile(providerFileName, bundleContext);
+        // start background monitor if check interval is higher than 100
+        long checkInterval = DEFAULT_CHECKINTERVAL;
+        final Object interval = props.get(PROP_PROVIDER_CHECKINTERVAL);
+        if ( interval != null && interval instanceof Long ) {
+            checkInterval = (Long)interval;
+        }
+        if ( checkInterval > 100 ) {
+            this.monitor = new FileMonitor(this, checkInterval);
+        }
     }
 
     protected void deactivate() {
+        if ( this.monitor != null ) {
+            this.monitor.stop();
+            this.monitor = null;
+        }
         this.providerRoot = null;
         this.providerRootPrefix = null;
         this.providerFile = null;
     }
 
+    EventAdmin getEventAdmin() {
+        return this.eventAdmin;
+    }
+
+    File getRootFile() {
+        return this.providerFile;
+    }
+
+    String getProviderRoot() {
+        return this.providerRoot;
+    }
+
     // ---------- internal
 
     private File getProviderFile(String providerFileName,

Modified: sling/trunk/bundles/extensions/fsresource/src/main/resources/META-INF/NOTICE
URL: http://svn.apache.org/viewvc/sling/trunk/bundles/extensions/fsresource/src/main/resources/META-INF/NOTICE?rev=911777&r1=911776&r2=911777&view=diff
==============================================================================
--- sling/trunk/bundles/extensions/fsresource/src/main/resources/META-INF/NOTICE (original)
+++ sling/trunk/bundles/extensions/fsresource/src/main/resources/META-INF/NOTICE Fri Feb 19 10:37:26 2010
@@ -1,5 +1,5 @@
 Apache Sling File System Resource Provider
-Copyright 2008-2009 The Apache Software Foundation
+Copyright 2008-2010 The Apache Software Foundation
 
 Apache Sling is based on source code originally developed 
 by Day Software (http://www.day.com/).