This is an automated email from the ASF dual-hosted git repository.
sseifert pushed a commit to branch master
in repository
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-sling-mock.git
The following commit(s) were added to refs/heads/master by this push:
new 8b52487 SLING-13146 ThreadsafeMockAdapterManagerWrapper: Reuse bundle
context for child threads (#60)
8b52487 is described below
commit 8b524878853dfed07303807bbd555e956526396d
Author: Stefan Seifert <[email protected]>
AuthorDate: Mon Apr 13 09:23:50 2026 +0200
SLING-13146 ThreadsafeMockAdapterManagerWrapper: Reuse bundle context for
child threads (#60)
---
core/pom.xml | 6 ++
.../sling/ThreadsafeMockAdapterManagerWrapper.java | 37 +++++++++--
.../ThreadsafeMockAdapterManagerWrapperTest.java | 76 ++++++++++++++++++++++
3 files changed, 113 insertions(+), 6 deletions(-)
diff --git a/core/pom.xml b/core/pom.xml
index 3ba54f0..7ec4186 100644
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -305,6 +305,12 @@
<version>1.0</version>
<scope>test</scope>
</dependency>
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>4.3.0</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
diff --git
a/core/src/main/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapper.java
b/core/src/main/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapper.java
index 52e085d..d55a81e 100644
---
a/core/src/main/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapper.java
+++
b/core/src/main/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapper.java
@@ -48,9 +48,8 @@ class ThreadsafeMockAdapterManagerWrapper implements
AdapterManager {
@Override
protected AdapterManagerBundleContextFactory childValue(
AdapterManagerBundleContextFactory parentValue) {
- // Create a new instance for child threads instead of
sharing the parent's instance
- // This prevents race conditions when parent and child
threads have different lifecycles
- return new AdapterManagerBundleContextFactory();
+ // create new factory for child thread, taking over bundle
context from parent thread if available
+ return new AdapterManagerBundleContextFactory(parentValue);
}
};
@@ -81,10 +80,24 @@ class ThreadsafeMockAdapterManagerWrapper implements
AdapterManager {
private static class AdapterManagerBundleContextFactory {
+ private AdapterManagerBundleContextFactory parent;
private BundleContext bundleContext;
+ AdapterManagerBundleContextFactory() {
+ // default constructor
+ }
+
+ AdapterManagerBundleContextFactory(AdapterManagerBundleContextFactory
parent) {
+ this.parent = parent;
+ this.bundleContext = parent.bundleContext;
+ }
+
public void setBundleContext(@NotNull final BundleContext
bundleContext) {
- log.debug("Set bundle context for AdapterManager,
bundleContext={}", bundleContext);
+ log.debug(
+ "Set bundle context for AdapterManager, bundleContext={},
factory={}, parent={}",
+ bundleContext,
+ this,
+ parent);
this.bundleContext = bundleContext;
// register adapter manager
@@ -96,6 +109,11 @@ class ThreadsafeMockAdapterManagerWrapper implements
AdapterManager {
}
public void clearBundleContext() {
+ log.debug(
+ "Clear bundle context for AdapterManager,
bundleContext={}, factory={}, parent={}",
+ bundleContext,
+ this,
+ parent);
this.bundleContext = null;
}
@@ -104,12 +122,19 @@ class ThreadsafeMockAdapterManagerWrapper implements
AdapterManager {
if (bundleContext == null) {
BundleContext newBundleContext = MockOsgi.newBundleContext();
log.warn(
- "Create new bundle context for adapter manager because
it was null, bundleContext={}",
- bundleContext);
+ "Create new bundle context for adapter manager because
it was null, bundleContext={}, factory={}, parent={}",
+ bundleContext,
+ this,
+ parent);
setBundleContext(newBundleContext);
}
ServiceReference<AdapterManager> serviceReference =
bundleContext.getServiceReference(AdapterManager.class);
if (serviceReference != null) {
+ log.trace(
+ "Get AdapterManager service from bundle context,
bundleContext={}, factory={}, parent={}",
+ bundleContext,
+ this,
+ parent);
return bundleContext.getService(serviceReference);
} else {
throw new RuntimeException("AdapterManager not registered in
bundle context.");
diff --git
a/core/src/test/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapperTest.java
b/core/src/test/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapperTest.java
new file mode 100644
index 0000000..af26ffb
--- /dev/null
+++
b/core/src/test/java/org/apache/sling/testing/mock/sling/ThreadsafeMockAdapterManagerWrapperTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.testing.mock.sling;
+
+import java.util.Objects;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.sling.api.resource.Resource;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.apache.sling.testing.mock.sling.junit.SlingContextBuilder;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import static org.awaitility.Awaitility.await;
+import static org.junit.Assert.assertNotNull;
+
+public class ThreadsafeMockAdapterManagerWrapperTest {
+
+ @Rule
+ public final SlingContext context = new
SlingContextBuilder(ResourceResolverType.RESOURCEPROVIDER_MOCK)
+ .registerSlingModelsFromClassPath(false)
+ .build();
+
+ @Before
+ public void setUp() {
+ context.registerAdapter(Resource.class, AdapterClass.class, new
AdapterClass());
+ }
+
+ @Test
+ public void testAdaptionInChildThread() {
+
assertNotNull(context.create().resource("/content/test").adaptTo(AdapterClass.class));
+ final MyService myService = context.registerService(new MyService());
+ final ScheduledExecutorService executor =
Executors.newScheduledThreadPool(10);
+ executor.schedule(
+ () -> myService.onChange(
+
Objects.requireNonNull(context.resourceResolver().getResource("/content/test"))),
+ 2,
+ TimeUnit.SECONDS);
+ await().until(() -> myService.getValue() != null);
+ }
+
+ private static class AdapterClass {}
+
+ private static class MyService {
+ private AdapterClass value;
+
+ public AdapterClass getValue() {
+ return this.value;
+ }
+
+ public void onChange(@NotNull Resource list) {
+ final AdapterClass result = list.adaptTo(AdapterClass.class);
+ value = result;
+ }
+ }
+}