ContextAwareServiceCollectionResolverImpl.java
- /*
- * #%L
- * wcm.io
- * %%
- * Copyright (C) 2022 wcm.io
- * %%
- * Licensed 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.
- * #L%
- */
- package io.wcm.sling.commons.caservice.impl;
- import java.util.Collection;
- import java.util.concurrent.TimeUnit;
- import java.util.function.BiFunction;
- import java.util.stream.Stream;
- import org.apache.sling.api.adapter.Adaptable;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
- import org.osgi.framework.BundleContext;
- import org.osgi.framework.ServiceReference;
- import org.slf4j.Logger;
- import org.slf4j.LoggerFactory;
- import com.github.benmanes.caffeine.cache.Caffeine;
- import com.github.benmanes.caffeine.cache.LoadingCache;
- import com.github.benmanes.caffeine.cache.RemovalCause;
- import io.wcm.sling.commons.caservice.ContextAwareService;
- import io.wcm.sling.commons.caservice.ContextAwareServiceCollectionResolver;
- class ContextAwareServiceCollectionResolverImpl<S extends ContextAwareService, D>
- implements ContextAwareServiceCollectionResolver<S, D> {
- private final Collection<ServiceReference<S>> serviceReferenceCollection;
- private final ResourcePathResolver resourcePathResolver;
- // cache of service trackers for each SPI interface
- private final LoadingCache<ServiceReference<S>, CollectionItemDecoration<S, D>> decorationCache;
- private static final Logger log = LoggerFactory.getLogger(ContextAwareServiceCollectionResolverImpl.class);
- ContextAwareServiceCollectionResolverImpl(@NotNull Collection<ServiceReference<S>> serviceReferenceCollection,
- @NotNull BiFunction<@NotNull ServiceReference<S>, @Nullable S, @Nullable D> decorator,
- @NotNull ResourcePathResolver resourcePathResolver,
- @NotNull BundleContext bundleContext) {
- this.serviceReferenceCollection = serviceReferenceCollection;
- this.resourcePathResolver = resourcePathResolver;
- this.decorationCache = buildCache(decorator, bundleContext);
- }
- private static <S extends ContextAwareService, D> LoadingCache<ServiceReference<S>, CollectionItemDecoration<S, D>> buildCache(
- @NotNull BiFunction<@NotNull ServiceReference<S>, @Nullable S, @Nullable D> decorator, @NotNull BundleContext bundleContext) {
- return Caffeine.newBuilder()
- // expire cached entry after 24h
- .expireAfterAccess(24, TimeUnit.HOURS)
- // unget service on removal
- .removalListener((ServiceReference<S> key, CollectionItemDecoration<S, D> value, RemovalCause cause) -> {
- log.debug("Remove service {}", value);
- bundleContext.ungetService(key);
- })
- // build cache lazily
- .build((ServiceReference<S> serviceReference) -> {
- CollectionItemDecoration<S, D> item = new CollectionItemDecoration<>(serviceReference, decorator, bundleContext);
- log.debug("Add service {}", item);
- return item;
- });
- }
- @Override
- public @Nullable S resolve(@Nullable Adaptable adaptable) {
- return getMatching(adaptable)
- .map(CollectionItemDecoration::getService)
- .findFirst().orElse(null);
- }
- @Override
- @SuppressWarnings("null")
- public @NotNull Stream<S> resolveAll(@Nullable Adaptable adaptable) {
- return getMatching(adaptable)
- .map(CollectionItemDecoration::getService);
- }
- @Override
- public @Nullable D resolveDecorated(@Nullable Adaptable adaptable) {
- return getMatching(adaptable)
- .map(CollectionItemDecoration::getDecoration)
- .findFirst().orElse(null);
- }
- @Override
- @SuppressWarnings("null")
- public @NotNull Stream<D> resolveAllDecorated(@Nullable Adaptable adaptable) {
- return getMatching(adaptable)
- .map(CollectionItemDecoration::getDecoration);
- }
- private @NotNull Stream<CollectionItemDecoration<S, D>> getMatching(@Nullable Adaptable adaptable) {
- String resourcePath = resourcePathResolver.get(adaptable);
- return serviceReferenceCollection.stream()
- .map(decorationCache::get)
- .filter(CollectionItemDecoration::isValid)
- .filter(item -> item.matches(resourcePath));
- }
- @Override
- public void close() {
- this.decorationCache.invalidateAll();
- }
- }