SLING-5999 : JcrResourceBundleProvider should move to new ResourceChangeListener API 

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1761721 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/pom.xml b/pom.xml
index c1d17ea..48aab0e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,11 +38,9 @@
     </description>
 
     <properties>
-        <exam.version>3.5.0</exam.version>
-        <url.version>1.5.2</url.version>
+        <exam.version>4.9.1</exam.version>
+        <url.version>2.4.7</url.version>
         <org.ops4j.pax.logging.DefaultServiceLog.level>INFO</org.ops4j.pax.logging.DefaultServiceLog.level>
-        <bundle.file.name>${basedir}/target/${project.build.finalName}.jar</bundle.file.name>
-        <sling.launchpad.version>7</sling.launchpad.version>
     </properties>
 
     <scm>
@@ -119,8 +117,7 @@
                         <org.ops4j.pax.logging.DefaultServiceLog.level>${org.ops4j.pax.logging.DefaultServiceLog.level}</org.ops4j.pax.logging.DefaultServiceLog.level>
                         <pax.exam.log.level>${pax.exam.log.level}</pax.exam.log.level>
                         <java.protocol.handler.pkgs>org.ops4j.pax.url</java.protocol.handler.pkgs>
-                        <bundle.file.name>${bundle.file.name}</bundle.file.name>
-                        <sling.launchpad.version>${sling.launchpad.version}</sling.launchpad.version>
+                        <bundle.filename>${basedir}/target/${project.build.finalName}.jar</bundle.filename>
                     </systemPropertyVariables>
                 </configuration>
             </plugin>
@@ -175,7 +172,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.api</artifactId>
-            <version>2.4.0</version>
+            <version>2.11.0</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -197,10 +194,8 @@
         <!-- Testing -->
         <dependency>
             <groupId>org.apache.sling</groupId>
-            <artifactId>org.apache.sling.launchpad</artifactId>
-            <version>${sling.launchpad.version}</version>
-            <type>xml</type>
-            <classifier>bundlelist</classifier>
+            <artifactId>org.apache.sling.testing.paxexam</artifactId>
+            <version>0.0.2</version>
             <scope>test</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java b/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java
index e546dfe..159bde9 100644
--- a/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java
+++ b/src/main/java/org/apache/sling/i18n/impl/I18NFilter.java
@@ -47,6 +47,7 @@
 import org.apache.felix.scr.annotations.sling.SlingFilterScope;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
+import org.apache.sling.commons.osgi.Order;
 import org.apache.sling.commons.osgi.ServiceUtil;
 import org.apache.sling.i18n.DefaultLocaleResolver;
 import org.apache.sling.i18n.LocaleResolver;
@@ -166,14 +167,14 @@
 
     protected void bindResourceBundleProvider(final ResourceBundleProvider provider, final Map<String, Object> props) {
         synchronized ( this.providers ) {
-            this.providers.put(ServiceUtil.getComparableForServiceRanking(props), provider);
+            this.providers.put(ServiceUtil.getComparableForServiceRanking(props, Order.ASCENDING), provider);
             this.sortedProviders = this.providers.values().toArray(new ResourceBundleProvider[this.providers.size()]);
         }
     }
 
     protected void unbindResourceBundleProvider(final ResourceBundleProvider provider, final Map<String, Object> props) {
         synchronized ( this.providers ) {
-            this.providers.remove(ServiceUtil.getComparableForServiceRanking(props));
+            this.providers.remove(ServiceUtil.getComparableForServiceRanking(props, Order.ASCENDING));
             this.sortedProviders = this.providers.values().toArray(new ResourceBundleProvider[this.providers.size()]);
         }
     }
diff --git a/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java b/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
index cd30d42..0c5b040 100644
--- a/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
+++ b/src/main/java/org/apache/sling/i18n/impl/JcrResourceBundleProvider.java
@@ -30,6 +30,7 @@
 import java.util.HashSet;
 import java.util.Hashtable;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.MissingResourceException;
@@ -42,20 +43,20 @@
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
 import org.apache.felix.scr.annotations.Service;
-import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.resource.LoginException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.api.resource.ResourceResolverFactory;
 import org.apache.sling.api.resource.ValueMap;
+import org.apache.sling.api.resource.observation.ExternalResourceChangeListener;
+import org.apache.sling.api.resource.observation.ResourceChange;
+import org.apache.sling.api.resource.observation.ResourceChangeListener;
 import org.apache.sling.commons.osgi.PropertiesUtil;
 import org.apache.sling.commons.scheduler.ScheduleOptions;
 import org.apache.sling.commons.scheduler.Scheduler;
 import org.apache.sling.i18n.ResourceBundleProvider;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceRegistration;
-import org.osgi.service.event.EventConstants;
-import org.osgi.service.event.EventHandler;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -66,9 +67,9 @@
  * repository.
  */
 @Component(immediate = true, metatype = true, label = "%provider.name", description = "%provider.description")
-@Service({ResourceBundleProvider.class, EventHandler.class})
-@Property(name=EventConstants.EVENT_TOPIC, value="org/apache/sling/api/resource/Resource/*", propertyPrivate=true)
-public class JcrResourceBundleProvider implements ResourceBundleProvider, EventHandler {
+@Service({ResourceBundleProvider.class, ResourceChangeListener.class})
+@Property(name=ResourceChangeListener.PATHS, value="/", propertyPrivate=true)
+public class JcrResourceBundleProvider implements ResourceBundleProvider, ResourceChangeListener, ExternalResourceChangeListener {
 
     private static final boolean DEFAULT_PRELOAD_BUNDLES = false;
 
@@ -185,22 +186,21 @@
     // ---------- EventHandler ------------------------------------------------
 
     @Override
-    public void handleEvent(final org.osgi.service.event.Event event) {
-        final String path = (String) event.getProperty(SlingConstants.PROPERTY_PATH);
-        if (path != null) {
-            log.trace("handleEvent: Detecting event {} for path '{}'", event, path);
+    public void onChange(List<ResourceChange> changes) {
+        for(final ResourceChange change : changes) {
+            log.trace("handleEvent: Detecting event {} for path '{}'", change.getType(), change.getPath());
 
             // if this change was on languageRootPath level this might change basename and locale as well, therefore
             // invalidate everything
-            if (languageRootPaths.contains(path)) {
+            if (languageRootPaths.contains(change.getPath())) {
                 log.debug(
                         "handleEvent: Detected change of cached language root '{}', removing all cached ResourceBundles",
-                        path);
+                        change.getPath());
                 scheduleReloadBundles(true);
             } else {
                 // if it is only a change below a root path, only messages of one resource bundle can be affected!
                 for (final String root : languageRootPaths) {
-                    if (path.startsWith(root)) {
+                    if (change.getPath().startsWith(root)) {
                         // figure out which JcrResourceBundle from the cached ones is affected
                         for (JcrResourceBundle bundle : resourceBundleCache.values()) {
                             if (bundle.getLanguageRootPaths().contains(root)) {
@@ -216,46 +216,41 @@
                     }
                 }
                 // may be a completely new dictionary
-                if (isDictionaryResource(path, event)) {
+                if (isDictionaryResource(change)) {
                     scheduleReloadBundles(true);
                 }
             }
         }
     }
 
-    private boolean isDictionaryResource(final String path, final org.osgi.service.event.Event event) {
+    private boolean isDictionaryResource(final ResourceChange change) {
         // language node changes happen quite frequently (https://issues.apache.org/jira/browse/SLING-2881)
         // therefore only consider changes either for sling:MessageEntry's
         // or for JSON dictionaries
-        String resourceType = (String) event.getProperty(SlingConstants.PROPERTY_RESOURCE_TYPE);
-        if (resourceType == null) {
-            return false;
-        }
-        if (JcrResourceBundle.RT_MESSAGE_ENTRY.equals(resourceType)) {
-            log.debug("Found new dictionary entry: New {} resource in '{}' detected", JcrResourceBundle.RT_MESSAGE_ENTRY, path);
-            return true;
-        }
         // get valuemap
         resourceResolver.refresh();
-        Resource resource = resourceResolver.getResource(path);
+        final Resource resource = resourceResolver.getResource(change.getPath());
         if (resource == null) {
-            log.trace("Could not resource for '{}' for event {}", path, event);
+            log.trace("Could not get resource for '{}' for event {}", change.getPath(), change.getType());
             return false;
         }
-        ValueMap valueMap = resource.adaptTo(ValueMap.class);
-        if (valueMap == null) {
-            log.trace("Could not get value map for '{}' for event {}", path, event);
+        if ( resource.getResourceType() == null ) {
             return false;
         }
-        // FIXME: derivatives from mix:Message are not detected
-        if (hasMixin(valueMap, JcrResourceBundle.MIXIN_MESSAGE)) {
-            log.debug("Found new dictionary entry: New {} resource in '{}' detected", JcrResourceBundle.MIXIN_MESSAGE, path);
+        if (resource.isResourceType(JcrResourceBundle.RT_MESSAGE_ENTRY)) {
+            log.debug("Found new dictionary entry: New {} resource in '{}' detected", JcrResourceBundle.RT_MESSAGE_ENTRY, change.getPath());
             return true;
         }
-        if (path.endsWith(".json")) {
+        final ValueMap valueMap = resource.getValueMap();
+        // FIXME: derivatives from mix:Message are not detected
+        if (hasMixin(valueMap, JcrResourceBundle.MIXIN_MESSAGE)) {
+            log.debug("Found new dictionary entry: New {} resource in '{}' detected", JcrResourceBundle.MIXIN_MESSAGE, change.getPath());
+            return true;
+        }
+        if (change.getPath().endsWith(".json")) {
             // check for mixin
             if (hasMixin(valueMap, JcrResourceBundle.MIXIN_LANGUAGE)) {
-                log.debug("Found new dictionary: New {} resource in '{}' detected", JcrResourceBundle.MIXIN_LANGUAGE, path);
+                log.debug("Found new dictionary: New {} resource in '{}' detected", JcrResourceBundle.MIXIN_LANGUAGE, change.getPath());
                 return true;
             }
         }
diff --git a/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java b/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
index baafe75..2f7a822 100644
--- a/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
+++ b/src/test/java/org/apache/sling/i18n/impl/JcrResourceBundleTest.java
@@ -328,6 +328,30 @@
                 // TODO Auto-generated method stub
 
             }
+
+            @Override
+            public Resource getParent(Resource child) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public boolean hasChildren(Resource resource) {
+                // TODO Auto-generated method stub
+                return false;
+            }
+
+            @Override
+            public Resource copy(String srcAbsPath, String destAbsPath) throws PersistenceException {
+                // TODO Auto-generated method stub
+                return null;
+            }
+
+            @Override
+            public Resource move(String srcAbsPath, String destAbsPath) throws PersistenceException {
+                // TODO Auto-generated method stub
+                return null;
+            }
         };
 
         createTestContent();
diff --git a/src/test/java/org/apache/sling/i18n/it/ResourceBundleProviderIT.java b/src/test/java/org/apache/sling/i18n/it/ResourceBundleProviderIT.java
index bc30a1c..df3a47a 100644
--- a/src/test/java/org/apache/sling/i18n/it/ResourceBundleProviderIT.java
+++ b/src/test/java/org/apache/sling/i18n/it/ResourceBundleProviderIT.java
@@ -18,12 +18,13 @@
  */
 package org.apache.sling.i18n.it;
 
+import static org.apache.sling.testing.paxexam.SlingOptions.slingExtensionModels;
+import static org.apache.sling.testing.paxexam.SlingOptions.slingLaunchpadOakTar;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.fail;
-import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
+import static org.ops4j.pax.exam.CoreOptions.junitBundles;
 
-import java.io.File;
 import java.util.Locale;
 import java.util.ResourceBundle;
 
@@ -35,15 +36,13 @@
 import org.apache.sling.i18n.ResourceBundleProvider;
 import org.apache.sling.i18n.impl.Message;
 import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.paxexam.util.SlingPaxOptions;
+import org.apache.sling.testing.paxexam.TestSupport;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
-import org.ops4j.pax.exam.CoreOptions;
 import org.ops4j.pax.exam.Option;
 import org.ops4j.pax.exam.junit.PaxExam;
-import org.ops4j.pax.exam.options.DefaultCompositeOption;
 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
 import org.ops4j.pax.exam.spi.reactors.PerClass;
 import org.slf4j.Logger;
@@ -51,7 +50,7 @@
 
 @RunWith(PaxExam.class)
 @ExamReactorStrategy(PerClass.class)
-public class ResourceBundleProviderIT {
+public class ResourceBundleProviderIT extends TestSupport {
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -73,6 +72,22 @@
     private Node enRoot;
 
     @org.ops4j.pax.exam.Configuration
+    public Option[] configuration() {
+        final String workingDirectory = workingDirectory(); // from TestSupport
+        final int httpPort = findFreePort(); // from TestSupport
+
+        return new Option[]{
+            baseConfiguration(), // from TestSupport
+            slingLaunchpadOakTar(workingDirectory, httpPort), // from SlingOptions
+            slingExtensionModels(), // from SlingOptions (for illustration)
+            // build artifact
+            testBundle("bundle.filename"), // from TestSupport
+            // testing
+            junitBundles()
+        };
+    }
+/*
+    @org.ops4j.pax.exam.Configuration
     public Option[] config() {
         final File thisProjectsBundle = new File(System.getProperty( "bundle.file.name", "BUNDLE_FILE_NOT_SET" ));
         final String launchpadVersion = System.getProperty("sling.launchpad.version", "LAUNCHPAD_VERSION_NOT_SET");
@@ -84,7 +99,7 @@
                 mavenBundle("org.apache.sling", "org.apache.sling.commons.osgi", "2.4.0")
                 ).getOptions();
     }
-
+*/
     static abstract class Retry {
         Retry(int timeoutMsec) {
             final long timeout = System.currentTimeMillis() + timeoutMsec;