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

royteeuwen pushed a commit to branch 
fix/resource-decorator-tracker-cleared-on-modified
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-resourceresolver.git

commit 6920af3cbb79f303b12778fdd6820d8749c52789
Author: Roy Teeuwen <[email protected]>
AuthorDate: Wed May 20 13:35:00 2026 +0200

    SLING-13206: ResourceDecorator services lost after @Modified on 
ResourceResolverFactoryActivator
    
    When OSGi fires the @Modified lifecycle callback (e.g. triggered by a bundle
    deployment causing a configuration change), modified() delegates to
    deactivateInternal() which calls resourceDecoratorTracker.close(), clearing
    all registered ResourceDecorator services.
    
    Because the decorators use ReferencePolicy.DYNAMIC, OSGi does not re-fire
    bindResourceDecorator after a @Modified event (no component restart). The
    tracker is therefore left permanently empty, silently breaking any feature
    that relies on ResourceDecorator (e.g. path-rewriting decorators).
    
    Fix: move resourceDecoratorTracker.close() out of deactivateInternal() and
    into deactivate() only. deactivateInternal() is still called from both
    @Modified and @Deactivate, but only the true component teardown path
    (@Deactivate) now closes the tracker.
    
    Also add a null-guard in ResourceDecoratorEntry.compareTo() to prevent NPE
    when ServiceReference is null.
---
 .../resourceresolver/impl/ResourceResolverFactoryActivator.java    | 7 ++++++-
 .../resourceresolver/impl/helper/ResourceDecoratorTracker.java     | 3 +++
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
index f8bdaee4..b92af50a 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/ResourceResolverFactoryActivator.java
@@ -369,6 +369,12 @@ public class ResourceResolverFactoryActivator {
         // factoryRegistrationHandler must be closed before bundleContext is 
set to null
         this.factoryRegistrationHandler.close();
         this.bundleContext = null;
+        // Close the decorator tracker only on true deactivation.  The tracker 
must NOT be
+        // closed inside deactivateInternal(), which is also called from 
@Modified.  When
+        // @Modified fires, OSGi does not re-fire bindResourceDecorator for 
already-bound
+        // DYNAMIC references, so closing the tracker here would permanently 
lose all
+        // registered ResourceDecorators for the lifetime of the component.
+        this.resourceDecoratorTracker.close();
         deactivateInternal();
     }
 
@@ -379,7 +385,6 @@ public class ResourceResolverFactoryActivator {
         this.changeListenerWhiteboard = null;
         this.resourceProviderTracker.deactivate();
         this.resourceProviderTracker = null;
-        this.resourceDecoratorTracker.close();
     }
 
     /**
diff --git 
a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
 
b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
index 3eea04a8..d600d73f 100644
--- 
a/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
+++ 
b/src/main/java/org/apache/sling/resourceresolver/impl/helper/ResourceDecoratorTracker.java
@@ -141,6 +141,9 @@ public class ResourceDecoratorTracker {
         }
 
         public int compareTo(final ResourceDecoratorEntry o) {
+            if (this.comparable == null || o.comparable == null) {
+                return 0;
+            }
             return comparable.compareTo(o.comparable);
         }
     }

Reply via email to