Merge branch 'master' into issue/SLING-7584
diff --git a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
index 736c344..7d93bbb 100644
--- a/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
+++ b/src/main/java/org/apache/sling/models/impl/ModelAdapterFactory.java
@@ -31,6 +31,7 @@
import java.util.Collections;
import java.util.Dictionary;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
@@ -137,7 +138,7 @@
private static final Object REQUEST_MARKER_VALUE = new Object();
- private static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry {
+ private static class DisposalCallbackRegistryImpl implements DisposalCallbackRegistry, Disposable {
private List<DisposalCallback> callbacks = new ArrayList<>();
@@ -150,7 +151,9 @@
callbacks = Collections.unmodifiableList(callbacks);
}
- private void onDisposed() {
+
+ @Override
+ public void onDisposed() {
for (DisposalCallback callback : callbacks) {
callback.onDisposed();
}
@@ -158,11 +161,60 @@
}
+ private static class CombinedDisposable implements Disposable {
+ private final Collection<Disposable> delegates = Collections.synchronizedCollection(new HashSet<Disposable>());
+
+ private void add(Disposable disposable) {
+ delegates.add(disposable);
+ }
+
+ @Override
+ public void onDisposed() {
+ for (Disposable delegate : delegates) {
+ delegate.onDisposed();
+ }
+ }
+ }
+
+ private interface Disposable {
+ void onDisposed();
+ }
+
+ private static class RequestDisposalCallbacks {
+ private ConcurrentHashMap<ServletRequest, Disposable> callbacks = new ConcurrentHashMap<>();
+
+ public Collection<Disposable> values() {
+ return callbacks.values();
+ }
+
+ public Disposable remove(ServletRequest request) {
+ return callbacks.remove(request);
+ }
+
+ public void put(ServletRequest request, Disposable registry) {
+ synchronized (callbacks) {
+ CombinedDisposable combinedDisposable = null;
+ Disposable current = callbacks.get(request);
+ if (current == null) {
+ callbacks.put(request, registry);
+ return;
+ } else if (current instanceof CombinedDisposable) {
+ combinedDisposable = (CombinedDisposable) current;
+ } else {
+ combinedDisposable = new CombinedDisposable();
+ combinedDisposable.add(current);
+ callbacks.put(request, combinedDisposable);
+ }
+ combinedDisposable.add(registry);
+ }
+ }
+ }
+
private ReferenceQueue<Object> queue;
- private ConcurrentMap<java.lang.ref.Reference<Object>, DisposalCallbackRegistryImpl> disposalCallbacks;
+ private ConcurrentMap<java.lang.ref.Reference<Object>, Disposable> disposalCallbacks;
- private ConcurrentHashMap<ServletRequest, DisposalCallbackRegistryImpl> requestDisposalCallbacks;
+ private RequestDisposalCallbacks requestDisposalCallbacks;
@Override
public void run() {
@@ -173,7 +225,7 @@
java.lang.ref.Reference<?> ref = queue.poll();
while (ref != null) {
log.debug("calling disposal for {}.", ref.toString());
- DisposalCallbackRegistryImpl registry = disposalCallbacks.remove(ref);
+ Disposable registry = disposalCallbacks.remove(ref);
registry.onDisposed();
ref = queue.poll();
}
@@ -1072,7 +1124,7 @@
BundleContext bundleContext = ctx.getBundleContext();
this.queue = new ReferenceQueue<>();
this.disposalCallbacks = new ConcurrentHashMap<>();
- this.requestDisposalCallbacks = new ConcurrentHashMap<>();
+ this.requestDisposalCallbacks = new RequestDisposalCallbacks();
Hashtable<String, Object> properties = new Hashtable<>();
properties.put(Constants.SERVICE_VENDOR, "Apache Software Foundation");
properties.put(Constants.SERVICE_DESCRIPTION, "Sling Models OSGi Service Disposal Job");
@@ -1101,7 +1153,7 @@
protected void deactivate() {
this.adapterCache = null;
if (this.requestDisposalCallbacks != null) {
- for (final DisposalCallbackRegistryImpl requestRegistries : this.requestDisposalCallbacks.values()) {
+ for (final Disposable requestRegistries : this.requestDisposalCallbacks.values()) {
requestRegistries.onDisposed();
}
}
@@ -1314,7 +1366,7 @@
@Override
public void requestDestroyed(ServletRequestEvent sre) {
ServletRequest request = unwrapRequest(sre.getServletRequest());
- DisposalCallbackRegistryImpl registry = requestDisposalCallbacks.remove(request);
+ Disposable registry = requestDisposalCallbacks.remove(request);
if (registry != null) {
registry.onDisposed();
}
diff --git a/src/test/java/org/apache/sling/models/impl/RequestDisposalTest.java b/src/test/java/org/apache/sling/models/impl/RequestDisposalTest.java
index 99ec6b5..bcf8e4a 100644
--- a/src/test/java/org/apache/sling/models/impl/RequestDisposalTest.java
+++ b/src/test/java/org/apache/sling/models/impl/RequestDisposalTest.java
@@ -41,8 +41,10 @@
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Type;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Hashtable;
import java.util.Map;
+import java.util.Set;
import static org.mockito.Mockito.*;
import static org.junit.Assert.*;
@@ -66,7 +68,7 @@
private ModelAdapterFactory factory;
- private TestDisposalCallback callback;
+ private Set<TestDisposalCallback> callbacks;
@Before
public void setup() {
@@ -96,8 +98,7 @@
return attributes.get(invocation.getArguments()[0]);
}
});
-
- callback = new TestDisposalCallback();
+ callbacks = new HashSet<>();
}
@Test
@@ -112,11 +113,33 @@
TestModel model = factory.getAdapter(adaptableRequest, TestModel.class);
assertEquals("teststring", model.testString);
- assertFalse(callback.isDisposed());
+ assertNoneDisposed();
factory.requestDestroyed(new ServletRequestEvent(servletContext, destroyedRequest));
- assertTrue(callback.isDisposed());
+ assertAllDisposed();
+ }
+
+ @Test
+ public void testTwoInstancesWithInitializedRequest() {
+ // destroy a wrapper of the root request
+ SlingHttpServletRequest destroyedRequest = new SlingHttpServletRequestWrapper(request);
+ factory.requestInitialized(new ServletRequestEvent(servletContext, destroyedRequest));
+
+ // but adapt from a wrapper of a wrapper of that wrapper
+ SlingHttpServletRequest adaptableRequest = new SlingHttpServletRequestWrapper(new SlingHttpServletRequestWrapper(destroyedRequest));
+
+ TestModel model = factory.getAdapter(adaptableRequest, TestModel.class);
+ assertEquals("teststring", model.testString);
+
+ TestModel model2 = factory.getAdapter(adaptableRequest, TestModel.class);
+ assertEquals("teststring", model2.testString);
+
+ assertNoneDisposed();
+
+ factory.requestDestroyed(new ServletRequestEvent(servletContext, destroyedRequest));
+
+ assertAllDisposed();
}
@Test
@@ -130,11 +153,23 @@
TestModel model = factory.getAdapter(adaptableRequest, TestModel.class);
assertEquals("teststring", model.testString);
- assertFalse(callback.isDisposed());
+ assertNoneDisposed();
factory.requestDestroyed(new ServletRequestEvent(servletContext, destroyedRequest));
- assertFalse(callback.isDisposed());
+ assertNoneDisposed();
+ }
+
+ private void assertNoneDisposed() {
+ for (TestDisposalCallback callback : callbacks) {
+ assertFalse(callback.isDisposed());
+ }
+ }
+
+ private void assertAllDisposed() {
+ for (TestDisposalCallback callback : callbacks) {
+ assertTrue(callback.isDisposed());
+ }
}
@Model(adaptables = SlingHttpServletRequest.class)
@@ -154,7 +189,9 @@
@Nullable
@Override
- public Object getValue(@NotNull Object o, String s, @NotNull Type type, @NotNull AnnotatedElement annotatedElement, @NotNull DisposalCallbackRegistry disposalCallbackRegistry) {
+ public Object getValue(@Nonnull Object o, String s, @Nonnull Type type, @Nonnull AnnotatedElement annotatedElement, @Nonnull DisposalCallbackRegistry disposalCallbackRegistry) {
+ TestDisposalCallback callback = new TestDisposalCallback();
+ callbacks.add(callback);
disposalCallbackRegistry.addDisposalCallback(callback);
return "teststring";
}