This is an automated email from the ASF dual-hosted git repository. sseifert pushed a commit to branch feature/SLING-13001-adapter-manager-impl in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-sling-mock.git
commit f8059c8165324a2c1cdab6144501766de7ffcaa0 Author: Stefan Seifert <[email protected]> AuthorDate: Wed Nov 12 16:53:05 2025 +0100 update MockAdapterManagerImpl to logic from Sling Adapter 2.2.0 --- .../testing/mock/sling/MockAdapterManagerImpl.java | 264 ++++++++------------- .../sling/testing/mock/sling/package-info.java | 2 +- 2 files changed, 102 insertions(+), 164 deletions(-) diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/MockAdapterManagerImpl.java b/core/src/main/java/org/apache/sling/testing/mock/sling/MockAdapterManagerImpl.java index b5f74e7..538befb 100644 --- a/core/src/main/java/org/apache/sling/testing/mock/sling/MockAdapterManagerImpl.java +++ b/core/src/main/java/org/apache/sling/testing/mock/sling/MockAdapterManagerImpl.java @@ -23,7 +23,6 @@ import java.util.Arrays; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; -import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; @@ -35,16 +34,16 @@ import org.apache.sling.adapter.internal.AdaptionImpl; import org.apache.sling.api.SlingConstants; import org.apache.sling.api.adapter.AdapterFactory; import org.apache.sling.api.adapter.AdapterManager; -import org.apache.sling.commons.osgi.PropertiesUtil; -import org.jetbrains.annotations.NotNull; import org.osgi.framework.Constants; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; -import org.osgi.service.component.ComponentContext; import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.service.component.annotations.ReferenceCardinality; import org.osgi.service.component.annotations.ReferencePolicy; +import org.osgi.util.converter.Converter; +import org.osgi.util.converter.Converters; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,43 +51,23 @@ import static org.apache.sling.api.adapter.AdapterFactory.ADAPTABLE_CLASSES; import static org.apache.sling.api.adapter.AdapterFactory.ADAPTER_CLASSES; /** - * This is a copy of org.apache.sling.adapter.internal.AdpaterManagerImpl from Sling Adapter 2.1.6, - * with all calls to SyntheticResource.setAdapterManager/unsetAdapterManager disabled, because this would + * This is a copy of org.apache.sling.adapter.internal.AdpaterManagerImpl from Sling Adapter 2.2.0, + * with all calls to SlingAdaptable.setAdapterManager/unsetAdapterManager disabled, because this would * break the {@link ThreadsafeMockAdapterManagerWrapper} concept. * Additionally the reference to PackageAdmin is disabled. */ @Component( - immediate = true, service = AdapterManager.class, + immediate = true, property = { Constants.SERVICE_DESCRIPTION + "=Sling Adapter Manager", Constants.SERVICE_VENDOR + "=The Apache Software Foundation" - }, - reference = - @Reference( - name = "AdapterFactory", - service = AdapterFactory.class, - cardinality = ReferenceCardinality.MULTIPLE, - policy = ReferencePolicy.DYNAMIC, - bind = "bindAdapterFactory", - unbind = "unbindAdapterFactory")) + }) public class MockAdapterManagerImpl implements AdapterManager { private final Logger log = LoggerFactory.getLogger(getClass()); - /** - * The OSGi <code>ComponentContext</code> to retrieve - * {@link AdapterFactory} service instances. - */ - private volatile ComponentContext context; - - /** - * A list of {@link AdapterFactory} services bound to this manager before - * the manager has been activated. These bound services will be accessed as - * soon as the manager is being activated. - */ - private final List<ServiceReference<AdapterFactory>> boundAdapterFactories = - new LinkedList<ServiceReference<AdapterFactory>>(); + static final String ALLOWED_IN_PRIVATE = "adapter.allowed.in.private.package"; /** * A map of {@link AdapterFactoryDescriptorMap} instances. The map is @@ -98,8 +77,7 @@ public class MockAdapterManagerImpl implements AdapterManager { * * @see AdapterFactoryDescriptorMap */ - private final Map<String, AdapterFactoryDescriptorMap> descriptors = - new HashMap<String, AdapterFactoryDescriptorMap>(); + private final Map<String, AdapterFactoryDescriptorMap> descriptors = new HashMap<>(); /** * Matrix of {@link AdapterFactoryDescriptor} instances primarily indexed by the fully @@ -111,11 +89,10 @@ public class MockAdapterManagerImpl implements AdapterManager { * whenever an adapter factory is registered on unregistered. */ private final ConcurrentMap<String, Map<String, List<AdapterFactoryDescriptor>>> factoryCache = - new ConcurrentHashMap<String, Map<String, List<AdapterFactoryDescriptor>>>(); + new ConcurrentHashMap<>(); // DISABLED IN THIS COPY OF CLASS /* - @Reference private PackageAdmin packageAdmin; */ @@ -128,9 +105,7 @@ public class MockAdapterManagerImpl implements AdapterManager { * @see org.apache.sling.api.adapter.AdapterManager#getAdapter(java.lang.Object, java.lang.Class) */ @Override - @SuppressWarnings("null") - public <AdapterType> AdapterType getAdapter( - @NotNull final Object adaptable, @NotNull final Class<AdapterType> type) { + public <AdapterType> AdapterType getAdapter(final Object adaptable, final Class<AdapterType> type) { // get the adapter factories for the type of adaptable object final Map<String, List<AdapterFactoryDescriptor>> factories = getAdapterFactories(adaptable.getClass()); @@ -140,7 +115,7 @@ public class MockAdapterManagerImpl implements AdapterManager { if (descList != null && descList.size() > 0) { for (AdapterFactoryDescriptor desc : descList) { - final AdapterFactory factory = desc == null ? null : desc.getFactory(); + final AdapterFactory factory = desc.getFactory(); // have the factory adapt the adaptable if the factory exists if (factory != null) { @@ -165,64 +140,55 @@ public class MockAdapterManagerImpl implements AdapterManager { /** * Activate the manager. - * Bind all already registered factories - * @param context Component context */ - protected void activate(final ComponentContext context) { - this.context = context; - - // register all adapter factories bound before activation - final List<ServiceReference<AdapterFactory>> refs; - synchronized (this.boundAdapterFactories) { - refs = new ArrayList<ServiceReference<AdapterFactory>>(this.boundAdapterFactories); - boundAdapterFactories.clear(); - } - for (final ServiceReference<AdapterFactory> reference : refs) { - registerAdapterFactory(context, reference); - } - - // final "enable" this manager by setting the instance - // DISABLED IN THIS COPY OF CLASS - // SyntheticResource.setAdapterManager(this); + // DISABLED IN THIS COPY OF CLASS + /* + @Activate + public AdapterManagerImpl() { + // "enable" this manager by setting the instance + SlingAdaptable.setAdapterManager(this); } + */ /** * Deactivate - * @param context Not used */ - protected void deactivate(final ComponentContext context) { + @Deactivate + protected void deactivate() { // DISABLED IN THIS COPY OF CLASS - // SyntheticResource.unsetAdapterManager(this); - this.context = null; + /* + SlingAdaptable.unsetAdapterManager(this); + */ } /** * Bind a new adapter factory. - * @param reference Service reference */ - protected void bindAdapterFactory(final ServiceReference<AdapterFactory> reference) { - boolean create = true; - if (context == null) { - synchronized (this.boundAdapterFactories) { - if (context == null) { - boundAdapterFactories.add(reference); - create = false; - } - } - } - if (create) { - registerAdapterFactory(context, reference); - } + @Reference( + service = AdapterFactory.class, + updated = "updatedAdapterFactory", + cardinality = ReferenceCardinality.MULTIPLE, + policy = ReferencePolicy.DYNAMIC) + protected void bindAdapterFactory(final AdapterFactory factory, final ServiceReference<AdapterFactory> reference) { + registerAdapterFactory(factory, reference); } /** * Unbind a adapter factory. - * @param reference Service reference */ - protected void unbindAdapterFactory(final ServiceReference reference) { + protected void unbindAdapterFactory(final ServiceReference<AdapterFactory> reference) { unregisterAdapterFactory(reference); } + /** + * Modify a adapter factory. + */ + protected void updatedAdapterFactory( + final AdapterFactory factory, final ServiceReference<AdapterFactory> reference) { + unregisterAdapterFactory(reference); + registerAdapterFactory(factory, reference); + } + // ---------- unit testing stuff only -------------------------------------- /** @@ -249,11 +215,17 @@ public class MockAdapterManagerImpl implements AdapterManager { * Unregisters the {@link AdapterFactory} referred to by the service * <code>reference</code> from the registry. */ - @SuppressWarnings("null") private void registerAdapterFactory( - final ComponentContext context, final ServiceReference<AdapterFactory> reference) { - final String[] adaptables = PropertiesUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES)); - final String[] adapters = PropertiesUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES)); + final AdapterFactory factory, final ServiceReference<AdapterFactory> reference) { + final Converter converter = Converters.standardConverter(); + final String[] adaptables = + converter.convert(reference.getProperty(ADAPTABLE_CLASSES)).to(String[].class); + final String[] adapters = + converter.convert(reference.getProperty(ADAPTER_CLASSES)).to(String[].class); + final boolean allowedInPrivatePackage = converter + .convert(reference.getProperty(ALLOWED_IN_PRIVATE)) + .defaultValue(false) + .to(Boolean.class); if (adaptables == null || adaptables.length == 0 || adapters == null || adapters.length == 0) { return; @@ -262,28 +234,24 @@ public class MockAdapterManagerImpl implements AdapterManager { // DISABLED IN THIS COPY OF CLASS /* for (String clazz : adaptables) { - if (!checkPackage(packageAdmin, clazz)) { + if (!allowedInPrivatePackage && !checkPackage(packageAdmin, clazz)) { log.warn("Adaptable class {} in factory service {} is not in an exported package.", clazz, reference.getProperty(Constants.SERVICE_ID)); } } for (String clazz : adapters) { - if (!checkPackage(packageAdmin, clazz)) { + if (!allowedInPrivatePackage && !checkPackage(packageAdmin, clazz)) { log.warn("Adapter class {} in factory service {} is not in an exported package.", clazz, reference.getProperty(Constants.SERVICE_ID)); } } */ - final AdapterFactoryDescriptor factoryDesc = new AdapterFactoryDescriptor(context, reference, adapters); + final AdapterFactoryDescriptor factoryDesc = new AdapterFactoryDescriptor(factory, adapters, adaptables); for (final String adaptable : adaptables) { AdapterFactoryDescriptorMap adfMap = null; synchronized (this.descriptors) { - adfMap = descriptors.get(adaptable); - if (adfMap == null) { - adfMap = new AdapterFactoryDescriptorMap(); - descriptors.put(adaptable, adfMap); - } + adfMap = descriptors.computeIfAbsent(adaptable, key -> new AdapterFactoryDescriptorMap()); } synchronized (adfMap) { adfMap.put(reference, factoryDesc); @@ -294,12 +262,12 @@ public class MockAdapterManagerImpl implements AdapterManager { this.factoryCache.clear(); // register adaption - final Dictionary<String, Object> props = new Hashtable<String, Object>(); + final Dictionary<String, Object> props = new Hashtable<>(); props.put(SlingConstants.PROPERTY_ADAPTABLE_CLASSES, adaptables); props.put(SlingConstants.PROPERTY_ADAPTER_CLASSES, adapters); - ServiceRegistration<Adaption> adaptionRegistration = - this.context.getBundleContext().registerService(Adaption.class, AdaptionImpl.INSTANCE, props); + factoryDesc.setAdaption( + reference.getBundle().getBundleContext().registerService(Adaption.class, AdaptionImpl.INSTANCE, props)); if (log.isDebugEnabled()) { log.debug("Registered service {} with {} : {} and {} : {}", new Object[] { Adaption.class.getName(), @@ -309,7 +277,6 @@ public class MockAdapterManagerImpl implements AdapterManager { Arrays.toString(adapters) }); } - factoryDesc.setAdaption(adaptionRegistration); } static String getPackageName(String clazz) { @@ -340,59 +307,45 @@ public class MockAdapterManagerImpl implements AdapterManager { * Unregisters the {@link AdapterFactory} referred to by the service * <code>reference</code> from the registry. */ - private void unregisterAdapterFactory(final ServiceReference reference) { - synchronized (this.boundAdapterFactories) { - boundAdapterFactories.remove(reference); - } - final String[] adaptables = PropertiesUtil.toStringArray(reference.getProperty(ADAPTABLE_CLASSES)); - final String[] adapters = PropertiesUtil.toStringArray(reference.getProperty(ADAPTER_CLASSES)); - - if (adaptables == null || adaptables.length == 0 || adapters == null || adapters.length == 0) { - return; + private void unregisterAdapterFactory(final ServiceReference<AdapterFactory> reference) { + final List<AdapterFactoryDescriptorMap> list = new ArrayList<>(); + synchronized (this.descriptors) { + for (final AdapterFactoryDescriptorMap map : this.descriptors.values()) { + list.add(map); + } } - - boolean factoriesModified = false; - AdapterFactoryDescriptorMap adfMap = null; - AdapterFactoryDescriptor removedDescriptor = null; - for (final String adaptable : adaptables) { - synchronized (this.descriptors) { - adfMap = this.descriptors.get(adaptable); - } - if (adfMap != null) { - synchronized (adfMap) { - AdapterFactoryDescriptor factoryDesc = adfMap.remove(reference); - if (factoryDesc != null) { - factoriesModified = true; - // A single ServiceReference should correspond to a single Adaption service being registered - // Since the code paths above does not fully guarantee it though, let's keep this check in place - if (removedDescriptor != null && removedDescriptor != factoryDesc) { - log.error( - "When unregistering reference {} got duplicate service descriptors {} and {}. Unregistration of {} services may be incomplete.", - new Object[] {reference, removedDescriptor, factoryDesc, Adaption.class.getName()}); - } - removedDescriptor = factoryDesc; - } + for (final AdapterFactoryDescriptorMap map : list) { + synchronized (map) { + final AdapterFactoryDescriptor factoryDesc = map.remove(reference); + if (factoryDesc != null) { + removedDescriptor = factoryDesc; } } } - // only remove cache if some adapter factories have actually been - // removed - if (factoriesModified) { - this.factoryCache.clear(); - } - // unregister adaption if (removedDescriptor != null) { - removedDescriptor.getAdaption().unregister(); + // only remove cache if some adapter factories have actually been + // removed + this.factoryCache.clear(); + + final ServiceRegistration<Adaption> reg = removedDescriptor.getAdaption(); + if (reg != null) { + removedDescriptor.setAdaption(null); + try { + reg.unregister(); + } catch (final IllegalStateException ignore) { + // ignore IAE on shutdown + } + } if (log.isDebugEnabled()) { log.debug("Unregistered service {} with {} : {} and {} : {}", new Object[] { Adaption.class.getName(), SlingConstants.PROPERTY_ADAPTABLE_CLASSES, - Arrays.toString(adaptables), + Arrays.toString(removedDescriptor.getAdaptables()), SlingConstants.PROPERTY_ADAPTER_CLASSES, - Arrays.toString(adapters) + Arrays.toString(removedDescriptor.getAdapters()) }); } } @@ -434,7 +387,7 @@ public class MockAdapterManagerImpl implements AdapterManager { * <code>clazz</code>. */ private Map<String, List<AdapterFactoryDescriptor>> createAdapterFactoryMap(final Class<?> clazz) { - final Map<String, List<AdapterFactoryDescriptor>> afm = new HashMap<String, List<AdapterFactoryDescriptor>>(); + final Map<String, List<AdapterFactoryDescriptor>> afm = new HashMap<>(); // AdapterFactories for this class AdapterFactoryDescriptorMap afdMap = null; @@ -444,7 +397,7 @@ public class MockAdapterManagerImpl implements AdapterManager { if (afdMap != null) { final List<AdapterFactoryDescriptor> afdSet; synchronized (afdMap) { - afdSet = new ArrayList<AdapterFactoryDescriptor>(afdMap.values()); + afdSet = new ArrayList<>(afdMap.values()); } for (final AdapterFactoryDescriptor afd : afdSet) { final String[] adapters = afd.getAdapters(); @@ -452,7 +405,7 @@ public class MockAdapterManagerImpl implements AdapterManager { // to handle service ranking, we add to the end of the list or create a new list List<AdapterFactoryDescriptor> factoryDescriptors = afm.get(adapter); if (factoryDescriptors == null) { - factoryDescriptors = new ArrayList<AdapterFactoryDescriptor>(); + factoryDescriptors = new ArrayList<>(); afm.put(adapter, factoryDescriptors); } factoryDescriptors.add(afd); @@ -495,12 +448,9 @@ public class MockAdapterManagerImpl implements AdapterManager { // for each target class copy the entry to dest and put it in the list or create the list for (Map.Entry<String, List<AdapterFactoryDescriptor>> entry : scMap.entrySet()) { - List<AdapterFactoryDescriptor> factoryDescriptors = dest.get(entry.getKey()); + final List<AdapterFactoryDescriptor> factoryDescriptors = + dest.computeIfAbsent(entry.getKey(), id -> new ArrayList<>()); - if (factoryDescriptors == null) { - factoryDescriptors = new ArrayList<AdapterFactoryDescriptor>(); - dest.put(entry.getKey(), factoryDescriptors); - } for (AdapterFactoryDescriptor descriptor : entry.getValue()) { factoryDescriptors.add(descriptor); } @@ -519,13 +469,8 @@ public class MockAdapterManagerImpl implements AdapterManager { * tuple. Of course only the first entry (this is the reason for having a sorted * map) for such a given tuple is actually being used. If that first instance is * removed the eventual second instance may actually be used instead. - * - * <p> - * Copy of import org.apache.sling.adapter.internal.AdapterFactoryDescriptorMap, - * signature has changed in adapter impl 2.2.0. - * </p> */ - static class AdapterFactoryDescriptorMap + private static class AdapterFactoryDescriptorMap extends TreeMap<ServiceReference<AdapterFactory>, AdapterFactoryDescriptor> { private static final long serialVersionUID = 2L; @@ -535,36 +480,25 @@ public class MockAdapterManagerImpl implements AdapterManager { * The <code>AdapterFactoryDescriptor</code> is an entry in the * {@link AdapterFactoryDescriptorMap} conveying the list of adapter (target) * types and the respective {@link AdapterFactory}. - * <p> - * Copy of import org.apache.sling.adapter.internal.AdapterFactoryDescriptor, - * signature has changed in adapter impl 2.2.0. - * </p> */ - static class AdapterFactoryDescriptor { + private static class AdapterFactoryDescriptor { - private volatile AdapterFactory factory; + private final AdapterFactory factory; private final String[] adapters; - private final ServiceReference<AdapterFactory> reference; - - private final ComponentContext context; + private final String[] adaptables; private volatile ServiceRegistration<Adaption> adaption; public AdapterFactoryDescriptor( - final ComponentContext context, - final ServiceReference<AdapterFactory> reference, - final String[] adapters) { - this.reference = reference; - this.context = context; + final AdapterFactory factory, final String[] adapters, final String[] adaptables) { + this.factory = factory; this.adapters = adapters; + this.adaptables = adaptables; } public AdapterFactory getFactory() { - if (factory == null) { - factory = context.locateService("AdapterFactory", reference); - } return factory; } @@ -572,11 +506,15 @@ public class MockAdapterManagerImpl implements AdapterManager { return adapters; } + public String[] getAdaptables() { + return adaptables; + } + public ServiceRegistration<Adaption> getAdaption() { return adaption; } - public void setAdaption(ServiceRegistration<Adaption> adaption) { + public void setAdaption(final ServiceRegistration<Adaption> adaption) { this.adaption = adaption; } } diff --git a/core/src/main/java/org/apache/sling/testing/mock/sling/package-info.java b/core/src/main/java/org/apache/sling/testing/mock/sling/package-info.java index 95061d9..c3a3dc8 100644 --- a/core/src/main/java/org/apache/sling/testing/mock/sling/package-info.java +++ b/core/src/main/java/org/apache/sling/testing/mock/sling/package-info.java @@ -19,5 +19,5 @@ /** * Mock implementation of selected Sling APIs. */ [email protected]("4.0.0") [email protected]("5.0.0") package org.apache.sling.testing.mock.sling;
