| /******************************************************************************* |
| * Copyright (c) 2008, 2010 VMware Inc. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * VMware Inc. - initial contribution |
| *******************************************************************************/ |
| |
| package org.eclipse.virgo.kernel.userregion.internal; |
| |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| |
| import org.eclipse.virgo.nano.shim.scope.Scope; |
| import org.eclipse.virgo.nano.shim.scope.ScopeFactory; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServiceReference; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import org.eclipse.virgo.kernel.install.artifact.ScopeServiceRepository; |
| |
| /** |
| * {@link ServiceScopingStrategy} encapsulates the service scoping algorithms used by {@link ServiceScopingRegistryHook} |
| * . |
| * <p /> |
| * |
| * <strong>Concurrent Semantics</strong><br /> |
| * |
| * Thread safe. |
| * |
| */ |
| final class ServiceScopingStrategy { |
| |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| private final ScopeFactory scopeFactory; |
| |
| private ScopeServiceRepository scopeServiceRepository; |
| |
| public ServiceScopingStrategy(ScopeFactory scopeFactory, ScopeServiceRepository scopeServiceRepository) { |
| this.scopeFactory = scopeFactory; |
| this.scopeServiceRepository = scopeServiceRepository; |
| } |
| |
| /** |
| * Returns true if and only the given service reference is potentially visible from the given bundle context. This |
| * is the case if and only if the service reference is in the same scope as the bundle context or the service |
| * reference is in the global scope. |
| * <p/> |
| * The given service reference may not be actually visible from the given bundle context if the given service |
| * reference is in the global scope, the given bundle context is scoped, and there is another service in the scope |
| * which shadows the given service reference. |
| */ |
| boolean isPotentiallyVisible(ServiceReference<?> serviceReference, BundleContext consumingBundleContext) { |
| boolean matchesScope = true; |
| Scope serviceScope = getServiceScope(serviceReference); |
| if (serviceScope != null && !serviceScope.isGlobal()) { |
| Scope bundleScope = this.scopeFactory.getBundleScope(consumingBundleContext.getBundle()); |
| if (!bundleScope.equals(serviceScope)) { |
| matchesScope = false; |
| } |
| } |
| return matchesScope; |
| } |
| |
| /** |
| * Takes the given collection of service references and restricts the collection to the scope of the given bundle |
| * context. It is not sufficient simply to discard service references in non-matching scopes because the global |
| * scope may (see below) be searched after an application scope. |
| * <p/> |
| * The exact behaviour depends on the {@link ScopeServiceRepository} which models, with varying degrees of accuracy, |
| * the services which are published in a particular scope. One variation in accuracy is due to the fact that web |
| * bundles typically have their application context files in a directory other than <code>META-INF/spring</code> and |
| * this is not currently taken into account when the repository is built. Another variation is due to the fact that |
| * Spring DM supports a manifest header which specifies the directory containing application context files. Again |
| * this is not currently taken into account when the repository is built. |
| */ |
| void scopeReferences(Collection<ServiceReference<?>> references, BundleContext consumingBundleContext, String className, String filter) { |
| Bundle consumingBundle = consumingBundleContext.getBundle(); |
| Scope lookupScope = getLookupScope(consumingBundle, className, filter); |
| Scope consumerScope = getBundleScope(consumingBundle); |
| |
| /* |
| * If the consumer is scoped, look in the consumer's scope before looking in the global scope. This avoids wrongly using |
| * the global scope when the service model did not include services which are nevertheless present in the application scope. |
| * If some of the service references are in the consumer's scope, restrict the set to just those. |
| */ |
| if (lookupScope.isGlobal() && !consumerScope.isGlobal()) { |
| Collection<ServiceReference<?>> scopedReferences = getScopedReferences(references, consumerScope); |
| if (!scopedReferences.isEmpty()) { |
| removeAllExcept(references, scopedReferences); |
| return; |
| } |
| } |
| restrictServicesToScope(references, lookupScope); |
| } |
| |
| private void removeAllExcept(Collection<ServiceReference<?>> references, Collection<ServiceReference<?>> scopedReferences) { |
| /* |
| * The simple implementation of clearing references and then using addAll to add |
| * in scopedReferences is no good as the find hook is passed a shrinkable collection |
| * that does not support add or addAll. |
| */ |
| Iterator<ServiceReference<?>> iterator = references.iterator(); |
| while (iterator.hasNext()) { |
| ServiceReference<?> ref = iterator.next(); |
| if (!scopedReferences.contains(ref)) { |
| iterator.remove(); |
| } |
| } |
| } |
| |
| private Collection<ServiceReference<?>> getScopedReferences(Collection<ServiceReference<?>> references, Scope scope) { |
| Collection<ServiceReference<?>> scopedReferences = new HashSet<ServiceReference<?>>(); |
| logger.debug("References input to getScopedReferences: {}", references.size()); |
| Iterator<ServiceReference<?>> iterator = references.iterator(); |
| while (iterator.hasNext()) { |
| ServiceReference<?> ref = iterator.next(); |
| Scope serviceScope = getServiceScope(ref); |
| if (scope.equals(serviceScope)) { |
| logger.debug("Adding {} ", ref); |
| scopedReferences.add(ref); |
| } |
| } |
| logger.debug("References output from getScopedReferences: {}", scopedReferences.size()); |
| return scopedReferences; |
| } |
| |
| private void restrictServicesToScope(Collection<ServiceReference<?>> references, Scope scope) { |
| logger.debug("Before filtering: {}", references.size()); |
| Iterator<ServiceReference<?>> iterator = references.iterator(); |
| while (iterator.hasNext()) { |
| ServiceReference<?> ref = (ServiceReference<?>) iterator.next(); |
| Scope serviceScope = getServiceScope(ref); |
| if (!scope.equals(serviceScope)) { |
| logger.debug("Removing {} ", ref); |
| iterator.remove(); |
| } |
| } |
| logger.debug("After filtering: {}", references.size()); |
| } |
| |
| /** |
| * Gets the {@link Scope} in which this lookup is being performed. |
| */ |
| private Scope getLookupScope(Bundle consumer, String name, String filter) { |
| Scope consumerScope = getBundleScope(consumer); |
| /* |
| * The lookup scope is that of the consuming bundle unless the consuming bundle is in an application scope with |
| * no service matching the given name and filter in which case the lookup scope is global. |
| */ |
| Scope lookupScope = !consumerScope.isGlobal() && !scopeHasMatchingService(consumerScope, name, filter) ? this.scopeFactory.getGlobalScope() |
| : consumerScope; |
| logger.debug("{} > {} [{}] ({})", new Object[] { lookupScope, name, filter, consumer }); |
| return lookupScope; |
| } |
| |
| private Scope getBundleScope(Bundle consumer) { |
| return this.scopeFactory.getBundleScope(consumer); |
| } |
| |
| private boolean scopeHasMatchingService(Scope scope, String name, String filter) { |
| try { |
| return this.scopeServiceRepository.scopeHasMatchingService(scope.getScopeName(), name, filter); |
| } catch (InvalidSyntaxException e) { |
| logger.warn("Filter '{}' is not valid", e, filter); |
| return false; |
| } |
| } |
| |
| private Scope getServiceScope(ServiceReference<?> ref) { |
| try { |
| return this.scopeFactory.getServiceScope(ref); |
| } catch (IllegalStateException ise) { |
| return null; |
| } |
| } |
| |
| } |