You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by ro...@apache.org on 2017/11/07 09:35:42 UTC

[sling-org-apache-sling-fsresource] 22/25: SLING-1387 : File system provider should send resource events

This is an automated email from the ASF dual-hosted git repository.

rombert pushed a commit to annotated tag org.apache.sling.fsresource-1.0.0
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-fsresource.git

commit 93cda71b4356d387c52bb9a1947117b76cf551e3
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Fri Feb 19 10:37:26 2010 +0000

    SLING-1387 : File system provider should send resource events
    
    git-svn-id: https://svn.apache.org/repos/asf/sling/trunk/bundles/extensions/fsresource@911777 13f79535-47bb-0310-9956-ffa450edef68
---
 NOTICE                                             |   2 +-
 pom.xml                                            |   4 +
 .../sling/fsprovider/internal/FileMonitor.java     | 256 +++++++++++++++++++++
 .../sling/fsprovider/internal/FsResource.java      |   4 +-
 .../fsprovider/internal/FsResourceProvider.java    |  43 +++-
 src/main/resources/META-INF/NOTICE                 |   2 +-
 6 files changed, 306 insertions(+), 5 deletions(-)

diff --git a/NOTICE b/NOTICE
index 7a727bf..9d9af52 100644
--- a/NOTICE
+++ b/NOTICE
@@ -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/).
diff --git a/pom.xml b/pom.xml
index ed57ad1..89aaae9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -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>
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
new file mode 100644
index 0000000..5e03ea9
--- /dev/null
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FileMonitor.java
@@ -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
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java b/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
index 372d499..7ecd8bc 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsResource.java
@@ -42,13 +42,13 @@ public class FsResource extends SlingAdaptable implements Resource {
      * 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;
diff --git a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
index 1cc0e18..dd7b740 100644
--- a/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
+++ b/src/main/java/org/apache/sling/fsprovider/internal/FsResourceProvider.java
@@ -32,6 +32,7 @@ import org.apache.sling.api.resource.Resource;
 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 @@ import org.osgi.framework.BundleContext;
  *                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 class FsResourceProvider implements ResourceProvider {
      */
     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 @@ public class FsResourceProvider implements ResourceProvider {
     // 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 @@ public class FsResourceProvider implements ResourceProvider {
         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,
diff --git a/src/main/resources/META-INF/NOTICE b/src/main/resources/META-INF/NOTICE
index 7a727bf..9d9af52 100644
--- a/src/main/resources/META-INF/NOTICE
+++ b/src/main/resources/META-INF/NOTICE
@@ -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/).

-- 
To stop receiving notification emails like this one, please contact
"commits@sling.apache.org" <co...@sling.apache.org>.