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

diru pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-whiteboard.git

commit 184243311bb2b02021ce21e5adc88402266c01c6
Author: Dirk Rudolph <d...@apache.org>
AuthorDate: Mon Jun 7 15:32:34 2021 +0200

    add API to schedule sitemap regeneration
---
 sitemap/pom.xml                                    |   9 +-
 .../org/apache/sling/sitemap/SitemapService.java   |  29 +++++
 .../sling/sitemap/impl/SitemapScheduler.java       |  46 +++++--
 .../sling/sitemap/impl/SitemapServiceImpl.java     |  73 ++++++++++-
 .../impl/SitemapServiceImplSchedulingTest.java     | 135 +++++++++++++++++++++
 5 files changed, 273 insertions(+), 19 deletions(-)

diff --git a/sitemap/pom.xml b/sitemap/pom.xml
index 0302154..cea534d 100644
--- a/sitemap/pom.xml
+++ b/sitemap/pom.xml
@@ -137,6 +137,10 @@
             <artifactId>org.osgi.service.event</artifactId>
         </dependency>
         <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.util.tracker</artifactId>
+        </dependency>
+        <dependency>
             <groupId>org.apache.jackrabbit</groupId>
             <artifactId>jackrabbit-jcr-commons</artifactId>
             <version>2.21.3</version>
@@ -221,11 +225,6 @@
         </dependency>
         <dependency>
             <groupId>org.osgi</groupId>
-            <artifactId>org.osgi.util.tracker</artifactId>
-            <scope>test</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.osgi</groupId>
             <artifactId>org.osgi.resource</artifactId>
             <scope>test</scope>
         </dependency>
diff --git a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java 
b/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java
index 190db7f..a5aa2b9 100644
--- a/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java
+++ b/sitemap/src/main/java/org/apache/sling/sitemap/SitemapService.java
@@ -34,6 +34,35 @@ public interface SitemapService {
     String PROPERTY_SITEMAP_ROOT = "sitemapRoot";
 
     /**
+     * Calls all registered SitemapSchedulers to schedule (re)generation for 
all sitemap roots and names.
+     */
+    void scheduleGeneration();
+
+    /**
+     * Calls all registered SitemapSchedulers registered for the given name to 
schedule (re)generation.
+     *
+     * @param name
+     */
+    void scheduleGeneration(String name);
+
+    /**
+     * Calls all registered SitemapSchedulers with a search path containing 
the given resource to schedule
+     * (re)generation for all names.
+     *
+     * @param sitemapRoot
+     */
+    void scheduleGeneration(Resource sitemapRoot);
+
+    /**
+     * Calls all registered SitemapSchedulers with a search path containing 
the given resource and being registered for
+     * the given name to schedule (re)generation.
+     *
+     * @param sitemapRoot
+     * @param name
+     */
+    void scheduleGeneration(Resource sitemapRoot, String name);
+
+    /**
      * Returns the urls to the given {@link Resource}'s sitemaps, if any.
      * <p>
      * The returned urls may contain a sitemap index when there are multiple 
sitemaps generated for the given sitemap
diff --git 
a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java 
b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java
index 078e938..0ce4615 100644
--- a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java
+++ b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapScheduler.java
@@ -42,10 +42,10 @@ import java.util.*;
 import static org.apache.sling.sitemap.impl.SitemapUtil.findSitemapRoots;
 
 @Component(
-        service = Runnable.class,
+        service = {SitemapScheduler.class, Runnable.class},
         configurationPolicy = ConfigurationPolicy.REQUIRE,
         property = {
-                Scheduler.PROPERTY_SCHEDULER_CONCURRENT + "=false",
+                Scheduler.PROPERTY_SCHEDULER_CONCURRENT + ":Boolean=false",
                 Scheduler.PROPERTY_SCHEDULER_RUN_ON + "=" + 
Scheduler.VALUE_RUN_ON_SINGLE
         }
 )
@@ -94,25 +94,45 @@ public class SitemapScheduler implements Runnable {
 
     @Override
     public void run() {
+        run(Arrays.asList(names));
+    }
+
+    public void run(String name) {
+        run(Collections.singleton(name));
+    }
+
+    public void run(Resource sitemapRoot) {
+        run(sitemapRoot, Arrays.asList(names));
+    }
+
+    public void run(Resource sitemapRoot, Collection<String> names) {
+        Set<String> applicableNames = 
generatorManager.getApplicableNames(sitemapRoot, names);
+        for (String applicableName : applicableNames) {
+            Map<String, Object> jobProperties = new HashMap<>();
+            
jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_NAME, 
applicableName);
+            
jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_ROOT, 
sitemapRoot.getPath());
+            Job job = jobManager.addJob(SitemapGeneratorExecutor.JOB_TOPIC, 
jobProperties);
+            LOG.debug("Added job {}", job.getId());
+        }
+    }
+
+    void run(ResourceResolver resolver) {
+        run(resolver, Arrays.asList(names));
+    }
+
+    private void run(Collection<String> names) {
         try (ResourceResolver resolver = 
resourceResolverFactory.getServiceResourceResolver(AUTH)) {
-            run(resolver);
+            run(resolver, names);
         } catch (LoginException ex) {
             LOG.warn("Failed start sitemap jobs: {}", ex.getMessage(), ex);
         }
     }
 
-    public void run(ResourceResolver resolver) {
+    private void run(ResourceResolver resolver, Collection<String> names) {
         Iterator<Resource> sitemapRoots = findSitemapRoots(resolver, 
searchPath);
         while (sitemapRoots.hasNext()) {
-            Resource sitemapRoot = sitemapRoots.next();
-            Set<String> applicableNames = 
generatorManager.getApplicableNames(sitemapRoot, Arrays.asList(names));
-            for (String applicableName : applicableNames) {
-                Map<String, Object> jobProperties = new HashMap<>();
-                
jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_NAME, 
applicableName);
-                
jobProperties.put(SitemapGeneratorExecutor.JOB_PROPERTY_SITEMAP_ROOT, 
sitemapRoot.getPath());
-                Job job = 
jobManager.addJob(SitemapGeneratorExecutor.JOB_TOPIC, jobProperties);
-                LOG.debug("Added job {}", job.getId());
-            }
+            run(sitemapRoots.next(), names);
         }
     }
+
 }
diff --git 
a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java 
b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java
index aa3703c..b8925e9 100644
--- 
a/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java
+++ 
b/sitemap/src/main/java/org/apache/sling/sitemap/impl/SitemapServiceImpl.java
@@ -26,10 +26,12 @@ import org.apache.sling.sitemap.SitemapService;
 import org.apache.sling.sitemap.common.SitemapLinkExternalizer;
 import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
+import org.osgi.framework.*;
 import org.osgi.service.component.annotations.*;
 import org.osgi.service.metatype.annotations.AttributeDefinition;
 import org.osgi.service.metatype.annotations.Designate;
 import org.osgi.service.metatype.annotations.ObjectClassDefinition;
+import org.osgi.util.tracker.ServiceTracker;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -67,15 +69,23 @@ public class SitemapServiceImpl implements SitemapService {
     @Reference
     private SitemapStorage storage;
 
+    private ServiceTracker<SitemapScheduler, SitemapScheduler> schedulers;
     private Set<String> onDemandNames;
     private int maxSize;
     private int maxEntries;
 
     @Activate
-    protected void activate(Configuration configuration) {
+    protected void activate(Configuration configuration, BundleContext 
bundleContext) {
         onDemandNames = 
Arrays.stream(configuration.onDemandNames()).collect(Collectors.toSet());
         maxSize = configuration.maxSize();
         maxEntries = configuration.maxEntries();
+        schedulers = new ServiceTracker<>(bundleContext, 
SitemapScheduler.class, null);
+        schedulers.open();
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        schedulers.close();
     }
 
     public Set<String> getSitemapNamesServedOnDemand() {
@@ -86,6 +96,67 @@ public class SitemapServiceImpl implements SitemapService {
         return size <= maxSize && entries <= maxEntries;
     }
 
+    @Override
+    public void scheduleGeneration() {
+        if (schedulers.getServiceReferences() == null) {
+            return;
+        }
+        for (ServiceReference<SitemapScheduler> scheduler : 
schedulers.getServiceReferences()) {
+            schedulers.getService(scheduler).run();
+        }
+    }
+
+    @Override
+    public void scheduleGeneration(String name) {
+        if (schedulers.getServiceReferences() == null) {
+            return;
+        }
+        try {
+            Filter filter = FrameworkUtil.createFilter("(names=" + name + ")");
+            for (ServiceReference<SitemapScheduler> scheduler : 
schedulers.getServiceReferences()) {
+                if (filter.match(scheduler)) {
+                    schedulers.getService(scheduler).run(name);
+                }
+            }
+        } catch (InvalidSyntaxException ex) {
+            LOG.warn("Failed to build filter for given argument: {}", name, 
ex);
+        }
+    }
+
+    @Override
+    public void scheduleGeneration(Resource sitemapRoot) {
+        if (schedulers.getServiceReferences() == null || 
!SitemapUtil.isSitemapRoot(sitemapRoot)) {
+            return;
+        }
+        for (ServiceReference<SitemapScheduler> scheduler : 
schedulers.getServiceReferences()) {
+            Object searchPath = scheduler.getProperty("searchPath");
+            if (searchPath instanceof String && 
sitemapRoot.getPath().startsWith(searchPath + "/")) {
+                schedulers.getService(scheduler).run(sitemapRoot);
+            }
+        }
+    }
+
+    @Override
+    public void scheduleGeneration(Resource sitemapRoot, String name) {
+        if (schedulers.getServiceReferences() == null || 
!SitemapUtil.isSitemapRoot(sitemapRoot)) {
+            return;
+        }
+        try {
+            Filter filter = FrameworkUtil.createFilter("(names=" + name + ")");
+            for (ServiceReference<SitemapScheduler> scheduler : 
schedulers.getServiceReferences()) {
+                if (!filter.match(scheduler)) {
+                    continue;
+                }
+                Object searchPath = scheduler.getProperty("searchPath");
+                if (searchPath instanceof String && 
sitemapRoot.getPath().startsWith(searchPath + "/")) {
+                    schedulers.getService(scheduler).run(sitemapRoot, 
Collections.singleton(name));
+                }
+            }
+        } catch (InvalidSyntaxException ex) {
+            LOG.warn("Failed to build filter for given argument: {}", name, 
ex);
+        }
+    }
+
     @NotNull
     @Override
     public Collection<SitemapInfo> getSitemapInfo(@NotNull Resource resource) {
diff --git 
a/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java
 
b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java
new file mode 100644
index 0000000..c495344
--- /dev/null
+++ 
b/sitemap/src/test/java/org/apache/sling/sitemap/impl/SitemapServiceImplSchedulingTest.java
@@ -0,0 +1,135 @@
+/*
+ * 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.sitemap.impl;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.event.jobs.JobManager;
+import org.apache.sling.serviceusermapping.ServiceUserMapped;
+import org.apache.sling.sitemap.generator.SitemapGenerator;
+import org.apache.sling.testing.mock.sling.junit5.SlingContext;
+import org.apache.sling.testing.mock.sling.junit5.SlingContextExtension;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
+import org.mockito.junit.jupiter.MockitoExtension;
+
+import java.util.Collections;
+
+import static org.mockito.Mockito.*;
+
+@ExtendWith({SlingContextExtension.class, MockitoExtension.class})
+public class SitemapServiceImplSchedulingTest {
+
+    public final SlingContext context = new SlingContext();
+
+    private final SitemapServiceImpl subject = new SitemapServiceImpl();
+    private final SitemapStorage storage = new SitemapStorage();
+    private final SitemapGeneratorManager generatorManager = new 
SitemapGeneratorManager();
+
+    @Mock
+    private ServiceUserMapped serviceUser;
+    @Mock
+    private JobManager jobManager;
+    @Mock
+    private SitemapGenerator generator;
+
+    private Resource root1;
+    private Resource root2;
+    private SitemapScheduler scheduler1;
+    private SitemapScheduler scheduler2;
+
+    @BeforeEach
+    public void setup() {
+        root1 = context.create().resource("/content/site/de", 
Collections.singletonMap(
+                "sitemapRoot", Boolean.TRUE
+        ));
+        root2 = context.create().resource("/content/microsite/de", 
Collections.singletonMap(
+                "sitemapRoot", Boolean.TRUE
+        ));
+
+        context.registerService(ServiceUserMapped.class, serviceUser, 
"subServiceName", "sitemap-writer");
+        context.registerService(ServiceUserMapped.class, serviceUser, 
"subServiceName", "sitemap-reader");
+        context.registerService(SitemapGenerator.class, generator);
+        context.registerService(JobManager.class, jobManager);
+        context.registerInjectActivateService(generatorManager);
+        context.registerInjectActivateService(storage);
+        context.registerInjectActivateService(subject);
+
+        scheduler1 = context.registerInjectActivateService(spy(new 
SitemapScheduler()),
+                "names", "<default>",
+                "searchPath", "/content/site"
+        );
+        scheduler2 = context.registerInjectActivateService(spy(new 
SitemapScheduler()),
+                "names", new String[]{"<default>", "foo"},
+                "searchPath", "/content/microsite"
+        );
+    }
+
+    @Test
+    public void testAllSchedulersCalled() {
+        // when
+        subject.scheduleGeneration();
+
+        // then
+        verify(scheduler1, times(1)).run();
+        verify(scheduler2, times(1)).run();
+    }
+
+    @Test
+    public void testAllSchedulersCalledForName() {
+        // when
+        subject.scheduleGeneration("<default>");
+
+        // then
+        verify(scheduler1, times(1)).run("<default>");
+        verify(scheduler2, times(1)).run("<default>");
+    }
+
+    @Test
+    public void testSchedulersCalledForName() {
+        // when
+        subject.scheduleGeneration("foo");
+
+        // then
+        verify(scheduler1, never()).run();
+        verify(scheduler2, times(1)).run("foo");
+    }
+
+    @Test
+    public void testSchedulersCalledForPath() {
+        // when
+        subject.scheduleGeneration(root1);
+
+        // then
+        verify(scheduler1, times(1)).run(root1);
+        verify(scheduler2, never()).run();
+    }
+
+    @Test
+    public void testSchedulersCalledForPathAndName() {
+        // when
+        subject.scheduleGeneration(root1, "foo");
+        subject.scheduleGeneration(root2, "foo");
+
+        // then
+        verify(scheduler1, never()).run(eq(root1), argThat(collection -> 
collection.contains("foo")));
+        verify(scheduler2, times(1)).run(eq(root2), argThat(collection -> 
collection.contains("foo")));
+    }
+}

Reply via email to