| /* |
| * 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.felix.scr.impl.manager; |
| |
| import java.security.Permission; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.SortedMap; |
| import java.util.TreeMap; |
| import java.util.concurrent.CountDownLatch; |
| import java.util.concurrent.atomic.AtomicInteger; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| import org.apache.felix.scr.impl.helper.Coercions; |
| import org.apache.felix.scr.impl.inject.BindParameters; |
| import org.apache.felix.scr.impl.inject.MethodResult; |
| import org.apache.felix.scr.impl.inject.OpenStatus; |
| import org.apache.felix.scr.impl.inject.RefPair; |
| import org.apache.felix.scr.impl.inject.ReferenceMethod; |
| import org.apache.felix.scr.impl.inject.ReferenceMethods; |
| import org.apache.felix.scr.impl.metadata.ReferenceMetadata; |
| import org.apache.felix.scr.impl.metadata.ReferenceMetadata.ReferenceScope; |
| import org.apache.felix.scr.impl.metadata.ServiceMetadata.Scope; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Filter; |
| import org.osgi.framework.InvalidSyntaxException; |
| import org.osgi.framework.ServicePermission; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.component.ComponentConstants; |
| import org.osgi.service.component.ComponentException; |
| import org.osgi.service.log.LogService; |
| |
| /** |
| * The <code>DependencyManager</code> manages the references to services |
| * declared by a single <code><reference></code element in component |
| * descriptor. |
| */ |
| public class DependencyManager<S, T> implements ReferenceManager<S, T> |
| { |
| |
| // the component to which this dependency belongs |
| private final AbstractComponentManager<S> m_componentManager; |
| |
| // Reference to the metadata |
| private final ReferenceMetadata m_dependencyMetadata; |
| |
| private final int m_index; |
| |
| private volatile Customizer<S, T> m_customizer; |
| |
| //only set once, but it's not clear there is enough other synchronization to get the correct object before it's used. |
| private volatile ReferenceMethods m_bindMethods; |
| |
| //reset on filter change |
| private volatile ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> m_tracker; |
| |
| // the target service filter string |
| private volatile String m_target; |
| |
| private volatile int m_minCardinality; |
| |
| /** |
| * Constructor that receives several parameters. |
| * @param dependency An object that contains data about the dependency |
| * @param index index of the dependency manager in the metadata |
| */ |
| DependencyManager(AbstractComponentManager<S> componentManager, ReferenceMetadata dependency, int index) |
| { |
| m_componentManager = componentManager; |
| m_dependencyMetadata = dependency; |
| m_index = index; |
| m_customizer = newCustomizer(); |
| |
| m_minCardinality = defaultMinimumCardinality(dependency); |
| |
| // dump the reference information if DEBUG is enabled |
| if (m_componentManager.getLogger().isLogEnabled(LogService.LOG_DEBUG)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "Dependency Manager created {0}", |
| null, dependency.getDebugInfo()); |
| } |
| } |
| |
| private static int defaultMinimumCardinality(ReferenceMetadata dependency) |
| { |
| return dependency.isOptional() ? 0 : 1; |
| } |
| |
| int getIndex() |
| { |
| return m_index; |
| } |
| |
| /** |
| * Initialize binding methods. |
| */ |
| void initBindingMethods(ReferenceMethods bindMethods) |
| { |
| m_bindMethods = bindMethods; |
| } |
| |
| private interface Customizer<S, T> extends ServiceTrackerCustomizer<T, RefPair<S, T>, ExtendedServiceEvent> |
| { |
| /** |
| * attempt to obtain the services from the tracked service references that will be used in inital bind calls |
| * before activation. |
| * @param key TODO |
| * @return true if there are enough services for activation. |
| */ |
| boolean prebind(ComponentContextImpl<S> key); |
| |
| void close(); |
| |
| Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount); |
| |
| boolean isSatisfied(); |
| |
| void setTracker(ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker); |
| |
| void setTrackerOpened(); |
| |
| void setPreviousRefMap(Map<ServiceReference<T>, RefPair<S, T>> previousRefMap); |
| } |
| |
| private abstract class AbstractCustomizer implements Customizer<S, T> |
| { |
| private final Map<ServiceReference<T>, RefPair<S, T>> EMPTY_REF_MAP = Collections.emptyMap(); |
| |
| private volatile boolean trackerOpened; |
| |
| private volatile Map<ServiceReference<T>, RefPair<S, T>> previousRefMap = EMPTY_REF_MAP; |
| |
| @Override |
| public void setTracker(ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker) |
| { |
| m_tracker = tracker; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracker reset (closed)", null, getName() ); |
| trackerOpened = false; |
| } |
| |
| @Override |
| public boolean isSatisfied() |
| { |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| if (tracker == null) |
| { |
| return false; |
| } |
| return cardinalitySatisfied(tracker.getServiceCount()); |
| } |
| |
| protected ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> getTracker() |
| { |
| return m_tracker; |
| } |
| |
| /** |
| * |
| * @return whether the tracker |
| */ |
| protected boolean isActive() |
| { |
| return getTracker().isActive(); |
| } |
| |
| protected boolean isTrackerOpened() |
| { |
| return trackerOpened; |
| } |
| |
| @Override |
| public void setTrackerOpened() |
| { |
| trackerOpened = true; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracker opened", null, getName()); |
| } |
| |
| protected void deactivateTracker() |
| { |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| if (tracker != null) |
| { |
| tracker.deactivate(); |
| } |
| } |
| |
| protected Map<ServiceReference<T>, RefPair<S, T>> getPreviousRefMap() |
| { |
| return previousRefMap; |
| } |
| |
| @Override |
| public void setPreviousRefMap(Map<ServiceReference<T>, RefPair<S, T>> previousRefMap) |
| { |
| if (previousRefMap != null) |
| { |
| this.previousRefMap = previousRefMap; |
| } |
| else |
| { |
| this.previousRefMap = EMPTY_REF_MAP; |
| } |
| |
| } |
| |
| protected void ungetService(RefPair<S, T> ref) |
| { |
| ref.ungetServiceObjects(m_componentManager.getBundleContext()); |
| } |
| |
| protected void tracked(int trackingCount) |
| { |
| m_componentManager.tracked(trackingCount); |
| } |
| |
| } |
| |
| private class FactoryCustomizer extends AbstractCustomizer |
| { |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = newRefPair(serviceReference); |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| if (cardinalityJustSatisfied(serviceCount)) |
| { |
| m_componentManager.activateInternal(); |
| } |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| refPair.markDeleted(); |
| if (!cardinalitySatisfied(getTracker().getServiceCount())) |
| { |
| deactivateComponentManager(); |
| } |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| AtomicInteger trackingCount = new AtomicInteger(); |
| int serviceCount = getTracker().getTracked(true, trackingCount).size(); |
| return cardinalitySatisfied(serviceCount); |
| } |
| |
| @Override |
| public void close() |
| { |
| deactivateTracker(); |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| return Collections.emptyList(); |
| } |
| } |
| |
| private class MultipleDynamicCustomizer extends AbstractCustomizer |
| { |
| |
| private RefPair<S, T> lastRefPair; |
| private int lastRefPairTrackingCount; |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = getPreviousRefMap().get(serviceReference); |
| if (refPair == null) |
| { |
| refPair = newRefPair(serviceReference); |
| } |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic added {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean tracked = false; |
| if (getPreviousRefMap().remove(serviceReference) == null) |
| { |
| if (isActive()) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleDynamic already active, binding {2}", |
| null, getName(), trackingCount, serviceReference ); |
| m_componentManager.invokeBindMethod(DependencyManager.this, refPair, trackingCount); |
| if (refPair.isFailed()) |
| { |
| m_componentManager.registerMissingDependency(DependencyManager.this, serviceReference, |
| trackingCount); |
| } |
| } |
| else if (isTrackerOpened() && cardinalityJustSatisfied(serviceCount)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic, activating", |
| null, getName(), trackingCount); |
| tracked(trackingCount); |
| tracked = true; |
| m_componentManager.activateInternal(); |
| } |
| else |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleDynamic, inactive, doing nothing: tracker opened: {2}, optional: {3}", |
| null, getName(), trackingCount, isTrackerOpened(), isOptional() ); |
| } |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic added {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| if (!tracked) |
| { |
| tracked(trackingCount); |
| } |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic modified {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| if (isActive()) |
| { |
| m_componentManager.invokeUpdatedMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic modified {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| tracked(trackingCount); |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic removed {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| refPair.markDeleted(); |
| boolean unbind = cardinalitySatisfied(getTracker().getServiceCount()); |
| if (unbind) |
| { |
| if (isActive()) |
| { |
| m_componentManager.invokeUnbindMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleDynamic removed (unbind) {2}", |
| null, getName(), trackingCount, serviceReference ); |
| tracked(trackingCount); |
| } |
| else |
| { |
| lastRefPair = refPair; |
| lastRefPairTrackingCount = trackingCount; |
| tracked(trackingCount); |
| deactivateComponentManager(); |
| lastRefPair = null; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleDynamic removed (deactivate) {2}", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| ungetService(refPair); |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| int serviceCount = 0; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = getTracker().getTracked(true, trackingCount); |
| List<RefPair<S, T>> failed = new ArrayList<>(); |
| for (RefPair<S, T> refPair : tracked.values()) |
| { |
| if (getServiceObject(key, m_bindMethods.getBind(), refPair)) |
| { |
| serviceCount++; |
| } |
| else |
| { |
| failed.add(refPair); |
| } |
| } |
| if (cardinalitySatisfied(serviceCount)) |
| { |
| for (RefPair<S, T> refPair : failed) |
| { |
| m_componentManager.registerMissingDependency(DependencyManager.this, refPair.getRef(), |
| trackingCount.get()); |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| @Override |
| public void close() |
| { |
| AtomicInteger trackingCount = new AtomicInteger(); |
| for (RefPair<S, T> ref : getRefs(trackingCount)) |
| { |
| ungetService(ref); |
| } |
| deactivateTracker(); |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| if (lastRefPair == null) |
| { |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| if (tracker == null) |
| { |
| trackingCount.set(lastRefPairTrackingCount); |
| return Collections.emptyList(); |
| } |
| return getTracker().getTracked(null, trackingCount).values(); |
| } |
| else |
| { |
| trackingCount.set(lastRefPairTrackingCount); |
| return Collections.singletonList(lastRefPair); |
| } |
| } |
| } |
| |
| private class MultipleStaticGreedyCustomizer extends AbstractCustomizer |
| { |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = newRefPair(serviceReference); |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy added {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| tracked(trackingCount); |
| if (isActive()) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "Dependency Manager: Static dependency on {0}/{1} is broken", |
| null, getName(), m_dependencyMetadata.getInterface() ); |
| deactivateComponentManager(); |
| //event may be null during initial operations. |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| |
| } |
| else if (isTrackerOpened() && cardinalityJustSatisfied(serviceCount)) |
| { |
| m_componentManager.activateInternal(); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy added {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticGreedy modified {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean reactivate = false; |
| if (isActive()) |
| { |
| reactivate = m_componentManager.invokeUpdatedMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| tracked(trackingCount); |
| if (reactivate) |
| { |
| deactivateComponentManager(); |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy modified {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy removed {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| refPair.markDeleted(); |
| tracked(trackingCount); |
| if (isActive()) |
| { |
| //deactivate while ref is still tracked |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "Dependency Manager: Static dependency on {0}/{1} is broken", |
| null, getName(), m_dependencyMetadata.getInterface() ); |
| deactivateComponentManager(); |
| //try to reactivate after ref is no longer tracked. |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| else if (!cardinalitySatisfied(getTracker().getServiceCount())) //may be called from an old tracker, so getTracker() may give a different answer |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "Dependency Manager: Static dependency on {0}/{1} is broken", |
| null, getName(), m_dependencyMetadata.getInterface() ); |
| deactivateComponentManager(); |
| } |
| //This is unlikely |
| ungetService(refPair); |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticGreedy removed {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| int serviceCount = 0; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| final ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = tracker.getTracked( |
| cardinalitySatisfied(tracker.getServiceCount()), trackingCount); |
| for (RefPair<S, T> refPair : tracked.values()) |
| { |
| if (getServiceObject(key, m_bindMethods.getBind(), refPair)) |
| { |
| serviceCount++; |
| } |
| } |
| return cardinalitySatisfied(serviceCount); |
| } |
| |
| @Override |
| public void close() |
| { |
| AtomicInteger trackingCount = new AtomicInteger(); |
| for (RefPair<S, T> ref : getRefs(trackingCount)) |
| { |
| ungetService(ref); |
| } |
| deactivateTracker(); |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| if (tracker == null) |
| { |
| return Collections.emptyList(); |
| } |
| return tracker.getTracked(null, trackingCount).values(); |
| } |
| } |
| |
| private class MultipleStaticReluctantCustomizer extends AbstractCustomizer |
| { |
| |
| private final AtomicReference<Collection<RefPair<S, T>>> refs = new AtomicReference<>(); |
| private int trackingCount; |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = newRefPair(serviceReference); |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticReluctant added {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| tracked(trackingCount); |
| if (isTrackerOpened() && cardinalityJustSatisfied(serviceCount) && !isActive()) |
| { |
| m_componentManager.activateInternal(); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} MultipleStaticReluctant added {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticReluctant modified {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean reactivate = false; |
| Collection<RefPair<S, T>> refs = this.refs.get(); |
| if (isActive() && refs.contains(refPair)) |
| { |
| reactivate = m_componentManager.invokeUpdatedMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| tracked(trackingCount); |
| if (reactivate) |
| { |
| deactivateComponentManager(); |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticReluctant modified {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticReluctant removed {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| refPair.markDeleted(); |
| tracked(trackingCount); |
| Collection<RefPair<S, T>> refs = this.refs.get(); |
| if (isActive() && refs != null) |
| { |
| if (refs.contains(refPair)) |
| { |
| //we are tracking the used refs, so we can deactivate here. |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "Dependency Manager: Static dependency on {0}/{1} is broken", |
| null, getName(), m_dependencyMetadata.getInterface() ); |
| deactivateComponentManager(); |
| |
| // FELIX-2368: immediately try to reactivate |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| |
| } |
| } |
| else if (!cardinalitySatisfied(getTracker().getServiceCount())) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "Dependency Manager: Static dependency on {0}/{1} is broken", |
| null, getName(), m_dependencyMetadata.getInterface() ); |
| deactivateComponentManager(); |
| } |
| ungetService(refPair); |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} MultipleStaticReluctant removed {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| int serviceCount = 0; |
| Collection<RefPair<S, T>> refs = this.refs.get(); |
| if (refs != null) |
| { |
| //another thread is concurrently opening, and it got done already |
| for (RefPair<S, T> refPair : refs) |
| { |
| if (getServiceObject(key, m_bindMethods.getBind(), refPair)) |
| { |
| serviceCount++; |
| } |
| } |
| return cardinalitySatisfied(serviceCount); |
| } |
| refs = new ArrayList<>(); |
| AtomicInteger trackingCount = new AtomicInteger(); |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = getTracker().getTracked(true, trackingCount); |
| for (RefPair<S, T> refPair : tracked.values()) |
| { |
| if (getServiceObject(key, m_bindMethods.getBind(), refPair)) |
| { |
| serviceCount++; |
| } |
| refs.add(refPair); |
| } |
| if (this.refs.compareAndSet(null, refs)) |
| { |
| this.trackingCount = trackingCount.get(); |
| } |
| else |
| { |
| //some other thread got done first. If we have more refPairs, we might need to unget some services. |
| Collection<RefPair<S, T>> actualRefs = this.refs.get(); |
| refs.removeAll(actualRefs); |
| for (RefPair<S, T> ref : refs) |
| { |
| ungetService(ref); |
| } |
| } |
| return cardinalitySatisfied(serviceCount); |
| } |
| |
| @Override |
| public void close() |
| { |
| Collection<RefPair<S, T>> refs = this.refs.getAndSet(null); |
| if (refs != null) |
| { |
| for (RefPair<S, T> ref : refs) |
| { |
| ungetService(ref); |
| } |
| } |
| deactivateTracker(); |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| trackingCount.set(this.trackingCount); |
| Collection<RefPair<S, T>> refs = this.refs.get(); |
| return refs == null ? Collections.<RefPair<S, T>> emptyList() : refs; |
| } |
| } |
| |
| private class SingleDynamicCustomizer extends AbstractCustomizer |
| { |
| |
| private RefPair<S, T> refPair; |
| private int trackingCount; |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = getPreviousRefMap().get(serviceReference); |
| if (refPair == null) |
| { |
| refPair = newRefPair(serviceReference); |
| } |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic added {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean tracked = false; |
| if (getPreviousRefMap().remove(serviceReference) == null) |
| { |
| if (isActive()) |
| { |
| boolean invokeBind; |
| synchronized (getTracker().tracked()) |
| { |
| invokeBind = this.refPair == null |
| || (!isReluctant() && refPair.getRef().compareTo(this.refPair.getRef()) > 0); |
| } |
| if (invokeBind) |
| { |
| m_componentManager.invokeBindMethod(DependencyManager.this, refPair, trackingCount); |
| if (!refPair.isFailed()) |
| { |
| if (this.refPair != null) |
| { |
| m_componentManager.invokeUnbindMethod(DependencyManager.this, this.refPair, |
| trackingCount); |
| closeRefPair(); |
| } |
| } |
| else if (cardinalitySatisfied(0)) |
| { |
| m_componentManager.registerMissingDependency(DependencyManager.this, serviceReference, |
| trackingCount); |
| } |
| this.refPair = refPair; |
| } |
| } |
| else if (isTrackerOpened() && cardinalityJustSatisfied(serviceCount)) |
| { |
| tracked(trackingCount); |
| tracked = true; |
| m_componentManager.activateInternal(); |
| } |
| } |
| this.trackingCount = trackingCount; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic added {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| if (!tracked) |
| { |
| tracked(trackingCount); |
| } |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic modified {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean invokeUpdated; |
| synchronized (getTracker().tracked()) |
| { |
| invokeUpdated = isActive() && refPair == this.refPair; |
| } |
| if (invokeUpdated) |
| { |
| m_componentManager.invokeUpdatedMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| this.trackingCount = trackingCount; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic modified {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| tracked(trackingCount); |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic removed {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| refPair.markDeleted(); |
| boolean deactivate = false; |
| boolean untracked = true; |
| RefPair<S, T> oldRefPair = null; |
| RefPair<S, T> nextRefPair = null; |
| synchronized (getTracker().tracked()) |
| { |
| if (refPair == this.refPair && isActive()) |
| { |
| if (!getTracker().isEmpty()) |
| { |
| AtomicInteger trackingCount2 = new AtomicInteger(); |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = getTracker().getTracked(true, //TODO true here looks odd. |
| trackingCount2); |
| nextRefPair = tracked.values().iterator().next(); |
| } |
| |
| //n.b. we cannot use cardinalitySatisfied( serviceCount ) here as the call may come from an old tracker during target change. |
| if (isEffectivelyOptional() || nextRefPair != null) |
| { |
| oldRefPair = this.refPair; |
| this.refPair = null; |
| } |
| else |
| { |
| deactivate = true; //required and no replacement service, deactivate |
| } |
| } |
| else if (!cardinalitySatisfied(getTracker().getServiceCount()) && this.refPair == null) |
| { |
| deactivate = true; |
| } |
| } |
| if (nextRefPair != null) |
| { |
| m_componentManager.invokeBindMethod(DependencyManager.this, nextRefPair, trackingCount); |
| } |
| |
| if (oldRefPair != null) |
| { |
| this.trackingCount = trackingCount; |
| m_componentManager.invokeUnbindMethod(DependencyManager.this, oldRefPair, trackingCount); |
| synchronized (getTracker().tracked()) |
| { |
| this.refPair = nextRefPair; |
| } |
| tracked(trackingCount); |
| untracked = false; |
| } |
| else if (deactivate) |
| { |
| this.trackingCount = trackingCount; |
| tracked(trackingCount); |
| untracked = false; |
| deactivateComponentManager(); |
| } |
| if (oldRefPair != null) |
| { |
| ungetService(oldRefPair); |
| } |
| if (untracked) // not ours |
| { |
| this.trackingCount = trackingCount; |
| tracked(trackingCount); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleDynamic removed {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| RefPair<S, T> refPair = null; |
| boolean success = cardinalitySatisfied(0); |
| AtomicInteger trackingCount = new AtomicInteger(); |
| synchronized (getTracker().tracked()) |
| { |
| if (success || !getTracker().isEmpty()) |
| { |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = getTracker().getTracked(true, |
| trackingCount); |
| if (!tracked.isEmpty()) |
| { |
| refPair = tracked.values().iterator().next(); |
| this.refPair = refPair; |
| } |
| } |
| } |
| if (refPair != null) |
| { |
| success |= getServiceObject(key, m_bindMethods.getBind(), refPair); |
| if (refPair.isFailed() && cardinalitySatisfied(0)) |
| { |
| m_componentManager.registerMissingDependency(DependencyManager.this, refPair.getRef(), |
| trackingCount.get()); |
| } |
| } |
| return success; |
| } |
| |
| @Override |
| public void close() |
| { |
| closeRefPair(); |
| deactivateTracker(); |
| } |
| |
| private void closeRefPair() |
| { |
| if (refPair != null) |
| { |
| ungetService(refPair); |
| } |
| refPair = null; |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| Object monitor = getTracker() == null ? null : getTracker().tracked(); |
| if (monitor != null) |
| { |
| synchronized (monitor) |
| { |
| trackingCount.set(this.trackingCount); |
| return refPair == null ? Collections.<RefPair<S, T>> emptyList() : Collections.singleton(refPair); |
| } |
| } |
| else |
| { |
| return Collections.<RefPair<S, T>> emptyList(); |
| } |
| } |
| } |
| |
| private class SingleStaticCustomizer extends AbstractCustomizer |
| { |
| |
| private RefPair<S, T> refPair; |
| private int trackingCount; |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> serviceReference) |
| { |
| RefPair<S, T> refPair = newRefPair(serviceReference); |
| return refPair; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic added {2} (enter)", |
| null, getName(), trackingCount, serviceReference); |
| this.trackingCount = trackingCount; |
| tracked(trackingCount); |
| if (isActive()) |
| { |
| boolean reactivate; |
| synchronized (getTracker().tracked()) |
| { |
| reactivate = !isReluctant() |
| && (this.refPair == null || refPair.getRef().compareTo(this.refPair.getRef()) > 0); |
| } |
| if (reactivate) |
| { |
| deactivateComponentManager(); |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| else |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} SingleStatic active but new {2} is worse match than old {3}", |
| null, getName(), trackingCount, refPair, this.refPair); |
| } |
| } |
| else if (isTrackerOpened() && cardinalityJustSatisfied(serviceCount)) |
| { |
| m_componentManager.activateInternal(); |
| } |
| else |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "dm {0} tracking {1} SingleStatic active: {2} trackerOpened: {3} optional: {4}", |
| null, getName(), trackingCount, isActive(), isTrackerOpened(), isOptional() ); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic added {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic modified {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| boolean invokeUpdated; |
| final Object sync = getTracker().tracked(); |
| synchronized (sync) |
| { |
| invokeUpdated = isActive() && refPair == this.refPair; |
| } |
| boolean reactivate = false; |
| if (invokeUpdated) |
| { |
| reactivate = m_componentManager.invokeUpdatedMethod(DependencyManager.this, refPair, trackingCount); |
| } |
| this.trackingCount = trackingCount; |
| tracked(trackingCount); |
| if (reactivate) |
| { |
| deactivateComponentManager(); |
| synchronized (sync) |
| { |
| if (refPair == this.refPair) |
| { |
| this.refPair = null; |
| } |
| } |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic modified {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> serviceReference, RefPair<S, T> refPair, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic removed {2} (enter)", |
| null, getName(), trackingCount, serviceReference ); |
| refPair.markDeleted(); |
| this.trackingCount = trackingCount; |
| tracked(trackingCount); |
| boolean reactivate; |
| final Object sync = getTracker().tracked(); |
| synchronized (sync) |
| { |
| reactivate = (isActive() && refPair == this.refPair) |
| || (!cardinalitySatisfied(getTracker().getServiceCount())); |
| if (!reactivate && refPair == this.refPair) |
| { |
| this.refPair = null; |
| } |
| } |
| if (reactivate) |
| { |
| deactivateComponentManager(); |
| synchronized (sync) |
| { |
| if (refPair == this.refPair) |
| { |
| this.refPair = null; |
| } |
| } |
| if (event != null) |
| { |
| event.addComponentManager(m_componentManager); |
| } |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "dm {0} tracking {1} SingleStatic removed {2} (exit)", |
| null, getName(), trackingCount, serviceReference ); |
| } |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| boolean success = cardinalitySatisfied(0); |
| if (success || !getTracker().isEmpty()) |
| { |
| RefPair<S, T> refPair = null; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| synchronized (getTracker().tracked()) |
| { |
| SortedMap<ServiceReference<T>, RefPair<S, T>> tracked = getTracker().getTracked(true, |
| trackingCount); |
| if (!tracked.isEmpty()) |
| { |
| refPair = tracked.values().iterator().next(); |
| this.refPair = refPair; |
| } |
| } |
| if (refPair != null) |
| { |
| success |= getServiceObject(key, m_bindMethods.getBind(), refPair); |
| if (refPair.isFailed()) |
| { |
| m_componentManager.registerMissingDependency(DependencyManager.this, refPair.getRef(), |
| trackingCount.get()); |
| } |
| } |
| } |
| return success; |
| } |
| |
| @Override |
| public void close() |
| { |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = getTracker(); |
| if (tracker != null) |
| { |
| RefPair<S, T> ref; |
| synchronized (tracker.tracked()) |
| { |
| ref = refPair; |
| refPair = null; |
| } |
| if (ref != null) |
| { |
| ungetService(ref); |
| } |
| tracker.deactivate(); |
| } |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| Object monitor = getTracker() == null ? null : getTracker().tracked(); |
| if (monitor != null) |
| { |
| synchronized (monitor) |
| { |
| trackingCount.set(this.trackingCount); |
| return refPair == null ? Collections.<RefPair<S, T>> emptyList() : Collections.singleton(refPair); |
| } |
| } |
| else |
| { |
| return Collections.<RefPair<S, T>> emptyList(); |
| } |
| } |
| } |
| |
| private class NoPermissionsCustomizer implements Customizer<S, T> |
| { |
| |
| @Override |
| public boolean prebind(ComponentContextImpl<S> key) |
| { |
| return false; |
| } |
| |
| @Override |
| public void close() |
| { |
| } |
| |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) |
| { |
| return Collections.emptyList(); |
| } |
| |
| @Override |
| public boolean isSatisfied() |
| { |
| return isOptional(); |
| } |
| |
| @Override |
| public void setTracker(ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tRefPairServiceTracker) |
| { |
| } |
| |
| @Override |
| public void setTrackerOpened() |
| { |
| } |
| |
| @Override |
| public void setPreviousRefMap(Map<ServiceReference<T>, RefPair<S, T>> previousRefMap) |
| { |
| } |
| |
| @Override |
| public RefPair<S, T> addingService(ServiceReference<T> tServiceReference) |
| { |
| return null; |
| } |
| |
| @Override |
| public void addedService(ServiceReference<T> tServiceReference, RefPair<S, T> service, int trackingCount, |
| int serviceCount, ExtendedServiceEvent event) |
| { |
| } |
| |
| @Override |
| public void modifiedService(ServiceReference<T> tServiceReference, RefPair<S, T> service, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| } |
| |
| @Override |
| public void removedService(ServiceReference<T> tServiceReference, RefPair<S, T> service, int trackingCount, |
| ExtendedServiceEvent event) |
| { |
| } |
| } |
| |
| private String getServiceName() |
| { |
| return m_dependencyMetadata.getInterface(); |
| } |
| |
| boolean isOptional() |
| { |
| return m_dependencyMetadata.isOptional(); |
| } |
| |
| private boolean isEffectivelyOptional() |
| { |
| return m_minCardinality == 0; |
| } |
| |
| public boolean cardinalitySatisfied(int serviceCount) |
| { |
| return m_minCardinality <= serviceCount; |
| } |
| |
| private boolean cardinalityJustSatisfied(int serviceCount) |
| { |
| return m_minCardinality == serviceCount; |
| } |
| |
| private boolean isMultiple() |
| { |
| return m_dependencyMetadata.isMultiple(); |
| } |
| |
| private boolean isStatic() |
| { |
| return m_dependencyMetadata.isStatic(); |
| } |
| |
| private boolean isReluctant() |
| { |
| return m_dependencyMetadata.isReluctant(); |
| } |
| |
| //---------- Service tracking support ------------------------------------- |
| |
| void deactivate() |
| { |
| m_customizer.close(); |
| } |
| |
| /** |
| * Returns the number of services currently registered in the system, |
| * which match the service criteria (interface and optional target filter) |
| * configured for this dependency. The number returned by this method has |
| * no correlation to the number of services bound to this dependency |
| * manager. It is actually the maximum number of services which may be |
| * bound to this dependency manager. |
| * |
| * @see #isSatisfied() |
| */ |
| int size() |
| { |
| AtomicInteger trackingCount = new AtomicInteger(); |
| return m_tracker.getTracked(null, trackingCount).size(); |
| } |
| |
| private ServiceReference<T>[] getFrameworkServiceReferences(String targetFilter) |
| { |
| if (hasGetPermission()) |
| { |
| // get bundle context, may be null if component deactivated since getting bca |
| BundleContext bc = m_componentManager.getActivator().getBundleContext(); |
| if (bc == null) |
| { |
| return null; |
| } |
| |
| try |
| { |
| return (ServiceReference<T>[]) bc.getServiceReferences(m_dependencyMetadata.getInterface(), |
| targetFilter); |
| } |
| catch (IllegalStateException ise) |
| { |
| // bundle context is not valid any longer, cannot log |
| } |
| catch (InvalidSyntaxException ise) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_ERROR, "Unexpected problem with filter ''{0}''", |
| ise, targetFilter ); |
| return null; |
| } |
| } |
| |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "No permission to access the services", null); |
| return null; |
| } |
| |
| /** |
| * Returns a <code>ServiceReference</code> instances for a service |
| * implementing the interface and complying to the (optional) target filter |
| * declared for this dependency. If no matching service can be found |
| * <code>null</code> is returned. If the configured target filter is |
| * syntactically incorrect an error message is logged with the LogService |
| * and <code>null</code> is returned. If multiple matching services are |
| * registered the service with the highest service.ranking value is |
| * returned. If multiple matching services have the same service.ranking |
| * value, the service with the lowest service.id is returned. |
| * <p> |
| */ |
| private RefPair<S, T> getBestRefPair() |
| { |
| Collection<RefPair<S, T>> refs = m_customizer.getRefs(new AtomicInteger()); |
| if (refs.isEmpty()) |
| { |
| return null; |
| } |
| return refs.iterator().next(); |
| } |
| |
| /** |
| * Returns the service instance for the service reference returned by the |
| * {@link #getBestRefPair()} method. If this returns a |
| * non-<code>null</code> service instance the service is then considered |
| * bound to this instance. |
| * @param key TODO |
| */ |
| T getService(ComponentContextImpl<S> key) |
| { |
| RefPair<S, T> sr = getBestRefPair(); |
| return getService(key, sr); |
| } |
| |
| /** |
| * Returns an array of service instances for the service references returned |
| * by the customizer. If no services |
| * match the criteria configured for this dependency <code>null</code> is |
| * returned. All services returned by this method will be considered bound |
| * after this method returns. |
| * @param key TODO |
| */ |
| T[] getServices(ComponentContextImpl<S> key) |
| { |
| Collection<RefPair<S, T>> refs = m_customizer.getRefs(new AtomicInteger()); |
| List<T> services = new ArrayList<>(refs.size()); |
| for (RefPair<S, T> ref : refs) |
| { |
| T service = getService(key, ref); |
| if (service != null) |
| { |
| services.add(service); |
| } |
| } |
| return services.isEmpty() ? null : (T[]) services.toArray(new Object[services.size()]); |
| } |
| |
| //---------- bound services maintenance ----------------------------------- |
| |
| /* (non-Javadoc) |
| * @see org.apache.felix.scr.impl.manager.ReferenceManager#getServiceReferences() |
| */ |
| @Override |
| public List<ServiceReference<?>> getServiceReferences() |
| { |
| Collection<RefPair<S, T>> bound = m_customizer.getRefs(new AtomicInteger()); |
| List<ServiceReference<?>> result = new ArrayList<>(bound.size()); |
| for (RefPair<S, T> ref : bound) |
| { |
| result.add(ref.getRef()); |
| } |
| return result; |
| } |
| |
| /** |
| * Returns the RefPair containing the given service reference and the bound service |
| * or <code>null</code> if this is instance is not currently bound to that |
| * service. |
| * |
| * @param serviceReference The reference to the bound service |
| * |
| * @return RefPair the reference and service for the reference |
| * if the service is bound or <code>null</code> if the service is not |
| * bound. |
| */ |
| private RefPair<S, T> getRefPair(ServiceReference<T> serviceReference) |
| { |
| final ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = m_tracker; |
| if (tracker != null) |
| { |
| AtomicInteger trackingCount = new AtomicInteger(); |
| return tracker.getTracked(null, trackingCount).get(serviceReference); |
| } |
| return null; |
| } |
| |
| /** |
| * Returns the service described by the ServiceReference. If this instance |
| * is already bound the given service, that bound service instance is |
| * returned. Otherwise the service retrieved from the service registry |
| * and kept as a bound service for future use. |
| * @param key TODO |
| * @param serviceReference The reference to the service to be returned |
| * |
| * @return The requested service or <code>null</code> if no service is |
| * registered for the service reference (any more). |
| */ |
| T getService(ComponentContextImpl<S> key, ServiceReference<T> serviceReference) |
| { |
| // check whether we already have the service and return that one |
| RefPair<S, T> refPair = getRefPair(serviceReference); |
| return getService(key, refPair); |
| } |
| |
| private T getService(ComponentContextImpl<S> key, RefPair<S, T> refPair) |
| { |
| if (refPair == null) |
| { |
| //we don't know about this reference |
| return null; |
| } |
| T serviceObject; |
| if ((serviceObject = refPair.getServiceObject(key)) != null) |
| { |
| return serviceObject; |
| } |
| // otherwise acquire the service |
| final BundleContext bundleContext = m_componentManager.getBundleContext(); |
| if (bundleContext == null) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_ERROR, |
| "Bundle shut down while getting service {0} ({1}/{2,number,#})", null, getName(), |
| m_dependencyMetadata.getInterface(), refPair.getRef().getProperty(Constants.SERVICE_ID) ); |
| return null; |
| } |
| try |
| { |
| refPair.getServiceObject(key, bundleContext); |
| serviceObject = refPair.getServiceObject(key); |
| } |
| catch (Exception e) |
| { |
| // caused by getService() called on invalid bundle context |
| // or if there is a service reference cycle involving service |
| // factories ! |
| m_componentManager.getLogger().log(LogService.LOG_ERROR, "Failed getting service {0} ({1}/{2,number,#})", |
| e, getName(), m_dependencyMetadata.getInterface(), |
| refPair.getRef().getProperty(Constants.SERVICE_ID) ); |
| return null; |
| } |
| |
| // return the acquired service (may be null of course) |
| //even if we did not set the service object, all the getService are for the same bundle so will have the same object. |
| return serviceObject; |
| } |
| |
| //---------- DependencyManager core --------------------------------------- |
| |
| /* (non-Javadoc) |
| * @see org.apache.felix.scr.impl.manager.ReferenceManager#getName() |
| */ |
| @Override |
| public String getName() |
| { |
| return m_dependencyMetadata.getName(); |
| } |
| |
| public ReferenceMetadata getReferenceMetadata() |
| { |
| return m_dependencyMetadata; |
| } |
| |
| /** |
| * Returns <code>true</code> if this dependency manager is satisfied, that |
| * is if either the dependency is optional or the number of services |
| * registered in the framework and available to this dependency manager is |
| * not zero. |
| */ |
| @Override |
| public boolean isSatisfied() |
| { |
| return m_customizer.isSatisfied(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the component providing bundle has permission |
| * to get the service described by this reference. |
| */ |
| public boolean hasGetPermission() |
| { |
| if (System.getSecurityManager() != null) |
| { |
| Permission perm = new ServicePermission(getServiceName(), ServicePermission.GET); |
| return m_componentManager.getBundle().hasPermission(perm); |
| } |
| |
| // no security manager, hence permission given |
| return true; |
| } |
| |
| boolean prebind(ComponentContextImpl<S> key) |
| { |
| return m_customizer.prebind(key); |
| } |
| |
| public static final class OpenStatusImpl<S, T> implements OpenStatus<S, T> { |
| private final DependencyManager<S, T> dm; |
| |
| OpenStatusImpl(DependencyManager<S, T> dm) { |
| this.dm = dm; |
| } |
| @Override |
| public Collection<RefPair<S, T>> getRefs(AtomicInteger trackingCount) { |
| return dm.m_customizer.getRefs(trackingCount); |
| } |
| } |
| |
| /** |
| * initializes a dependency. This method binds all of the service |
| * occurrences to the instance object |
| * @param edgeInfo Edge info for the combination of this component instance and this dependency manager. |
| * |
| * @return true if the dependency is satisfied and at least the minimum |
| * number of services could be bound. Otherwise false is returned. |
| */ |
| OpenStatus<S, T> open(ComponentContextImpl<S> componentContext, EdgeInfo edgeInfo) |
| { |
| int serviceCount = 0; |
| final OpenStatus<S, T> status = new OpenStatusImpl<>(this); |
| Collection<RefPair<S, T>> refs; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| CountDownLatch openLatch; |
| synchronized (m_tracker.tracked()) |
| { |
| refs = m_customizer.getRefs(trackingCount); |
| edgeInfo.setOpen(trackingCount.get()); |
| openLatch = edgeInfo.getOpenLatch(); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "For dependency {0}, optional: {1}; to bind: {2}", |
| null, getName(), isOptional(), refs); |
| for (RefPair<S, T> refPair : refs) |
| { |
| if (!refPair.isDeleted() && !refPair.isFailed()) |
| { |
| serviceCount++; |
| } |
| } |
| openLatch.countDown(); |
| return (cardinalitySatisfied(serviceCount) ? status : null); |
| } |
| |
| boolean bind(final ComponentContextImpl<S> componentContext, final OpenStatus<S, T> status) |
| { |
| if (!invokeInitMethod(componentContext)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "For dependency {0}, failed to initialize object", |
| null, getName()); |
| return false; |
| } |
| final ReferenceMethod bindMethod = m_bindMethods.getBind(); |
| return this.bindDependency(componentContext, bindMethod, status); |
| } |
| |
| boolean bindDependency(final ComponentContextImpl<S> componentContext, |
| final ReferenceMethod bindMethod, |
| final OpenStatus<S, T> status) |
| { |
| int serviceCount = 0; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| for (final RefPair<S, T> refPair : status.getRefs(trackingCount)) |
| { |
| if (!refPair.isDeleted() && !refPair.isFailed()) |
| { |
| if (!doInvokeBindMethod(componentContext, bindMethod, refPair, trackingCount.get())) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "For dependency {0}, failed to invoke bind method on object {1}", |
| null, getName(), refPair ); |
| |
| } |
| serviceCount++; |
| } |
| } |
| return cardinalitySatisfied(serviceCount); |
| } |
| |
| /** |
| * Revoke the given bindings. This method cannot throw an exception since |
| * it must try to complete all that it can |
| * @param componentContext instance we are unbinding from. |
| * @param edgeInfo EdgeInfo for the combination of this component instance and this dependency manager. |
| */ |
| void close(ComponentContextImpl<S> componentContext, EdgeInfo edgeInfo) |
| { |
| // only invoke the unbind method if there is an instance (might be null |
| // in the delayed component situation) and the unbind method is declared. |
| boolean doUnbind = |
| componentContext != null && |
| (m_dependencyMetadata.getField() != null || m_dependencyMetadata.getUnbind() != null); |
| |
| AtomicInteger trackingCount = new AtomicInteger(); |
| Collection<RefPair<S, T>> refPairs; |
| CountDownLatch latch; |
| synchronized (m_tracker.tracked()) |
| { |
| refPairs = m_customizer.getRefs(trackingCount); |
| edgeInfo.setClose(trackingCount.get()); |
| latch = edgeInfo.getCloseLatch(); |
| } |
| |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "DependencyManager: {0} close component unbinding from {1} at tracking count {2} refpairs: {3}", |
| null, getName(), componentContext, trackingCount.get(), refPairs ); |
| m_componentManager.waitForTracked(trackingCount.get()); |
| for (RefPair<S, T> boundRef : refPairs) |
| { |
| if (doUnbind && !boundRef.isFailed()) |
| { |
| invokeUnbindMethod(componentContext, boundRef, trackingCount.get(), edgeInfo); |
| } |
| |
| boundRef.ungetServiceObject(componentContext); |
| |
| } |
| latch.countDown(); |
| } |
| |
| public void invokeBindMethodLate(final ServiceReference<T> ref, int trackingCount) |
| { |
| if ( m_tracker == null || !m_tracker.isActive() ) |
| { |
| m_componentManager.notifyWaiters(); |
| return; |
| } |
| if ( !isSatisfied() ) |
| { |
| return; |
| } |
| if (!isMultiple()) |
| { |
| Collection<RefPair<S, T>> refs = m_customizer.getRefs(new AtomicInteger()); |
| if (refs.isEmpty()) |
| { |
| return; |
| } |
| RefPair<S, T> test = refs.iterator().next(); |
| if (ref != test.getRef()) |
| { |
| //another ref is now better |
| return; |
| } |
| } |
| //TODO dynamic reluctant |
| RefPair<S, T> refPair = m_tracker.getService(ref); |
| if (refPair == null) |
| { |
| return; // The service is no longer available, probably because the tracker has been closed |
| } |
| //TODO this check is no longer correct, fix it! |
| // if (refPair.getServiceObject(key) != null) |
| // { |
| // m_componentManager.getLogger().log( LogService.LOG_DEBUG, |
| // "DependencyManager : late binding of service reference {1} skipped as service has already been located", |
| // new Object[] {ref}, null ); |
| // //something else got the reference and may be binding it. |
| // return; |
| // } |
| m_componentManager.invokeBindMethod(this, refPair, trackingCount); |
| } |
| |
| /** |
| * Calls the optional init reference method. |
| */ |
| boolean invokeInitMethod(final ComponentContextImpl<S> componentContext) |
| { |
| // The bind method is only invoked if the implementation object is not |
| // null. This is valid for both immediate and delayed components |
| if (m_bindMethods.getInit() != null) |
| { |
| final Object componentInstance = componentContext.getImplementationObject(false); |
| if (componentInstance != null) |
| { |
| return m_bindMethods.getInit().init(componentInstance, componentContext.getLogger()); |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Calls the bind method. In case there is an exception while calling the |
| * bind method, the service is not considered to be bound to the instance |
| * object |
| * <p> |
| * If the reference is singular and a service has already been bound to the |
| * component this method has no effect and just returns <code>true</code>. |
| * |
| * |
| * |
| * @param componentContext instance we are binding to |
| * @param refPair the service reference, service object tuple. |
| * @param trackingCount service event counter for this service. |
| * @param edgeInfo EdgeInfo for the combination of this instance and this dependency manager. |
| * @return true if the service should be considered bound. If no bind |
| * method is found or the method call fails, <code>true</code> is |
| * returned. <code>false</code> is only returned if the service must |
| * be handed over to the bind method but the service cannot be |
| * retrieved using the service reference. |
| */ |
| boolean invokeBindMethod(ComponentContextImpl<S> componentContext, RefPair<S, T> refPair, int trackingCount, |
| EdgeInfo info) |
| { |
| // The bind method is only invoked if the implementation object is not |
| // null. This is valid for both immediate and delayed components |
| if (componentContext.getImplementationObject(false) != null) |
| { |
| synchronized (m_tracker.tracked()) |
| { |
| if (info.outOfRange(trackingCount)) |
| { |
| //ignore events before open started or we will have duplicate binds. |
| return true; |
| } |
| } |
| //edgeInfo open has been set, so binding has started. |
| return doInvokeBindMethod(componentContext, m_bindMethods.getBind(), refPair, trackingCount); |
| |
| } |
| else |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "DependencyManager : component not yet created, assuming bind method call succeeded", null); |
| |
| return true; |
| } |
| } |
| |
| private boolean doInvokeBindMethod(ComponentContextImpl<S> componentContext, |
| final ReferenceMethod bindMethod, |
| RefPair<S, T> refPair, |
| int trackingCount) |
| { |
| if (!getServiceObject(componentContext, bindMethod, refPair)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_WARNING, |
| "DependencyManager : invokeBindMethod : Service not available from service registry for ServiceReference {0} for reference {1}", |
| null, refPair.getRef(), getName() ); |
| return false; |
| |
| } |
| MethodResult result = bindMethod.invoke(componentContext.getImplementationObject(false), |
| new BindParameters(componentContext, refPair), MethodResult.VOID); |
| if (result == null) |
| { |
| return false; |
| } |
| m_componentManager.setServiceProperties(result, trackingCount); |
| return true; |
| } |
| |
| /** |
| * Calls the updated method. |
| * |
| * @param componentContext instance we are calling updated on. |
| * @param refPair A service reference corresponding to the service whose service |
| * @param edgeInfo EdgeInfo for the combination of this instance and this dependency manager. |
| * @return {@code true} if reactivation is required. |
| */ |
| boolean invokeUpdatedMethod(ComponentContextImpl<S> componentContext, final RefPair<S, T> refPair, |
| int trackingCount, EdgeInfo info) |
| { |
| if (m_dependencyMetadata.getUpdated() == null && m_dependencyMetadata.getField() == null) |
| { |
| return false; |
| } |
| // The updated method is only invoked if the implementation object is not |
| // null. This is valid for both immediate and delayed components |
| if (componentContext != null) |
| { |
| synchronized (m_tracker.tracked()) |
| { |
| if (info.outOfRange(trackingCount)) |
| { |
| //ignore events after close started or we will have duplicate unbinds. |
| return false; |
| } |
| } |
| info.waitForOpen(m_componentManager, getName(), "invokeUpdatedMethod"); |
| if (!getServiceObject(componentContext, m_bindMethods.getUpdated(), refPair)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_WARNING, |
| "DependencyManager : invokeUpdatedMethod : Service not available from service registry for ServiceReference {0} for reference {1}", |
| null, refPair.getRef(), getName() ); |
| return false; |
| |
| } |
| final MethodResult methodResult = m_bindMethods.getUpdated().invoke( |
| componentContext.getImplementationObject(false), new BindParameters(componentContext, refPair), MethodResult.VOID); |
| if (methodResult != null) |
| { |
| m_componentManager.setServiceProperties(methodResult, trackingCount); |
| } |
| return methodResult == MethodResult.REACTIVATE; |
| } |
| else |
| { |
| // don't care whether we can or cannot call the updated method |
| // if the component instance has already been cleared by the |
| // close() method |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "DependencyManager : Component not set, no need to call updated method", null); |
| } |
| return false; |
| } |
| |
| /** |
| * Calls the unbind method. |
| * <p> |
| * If the reference is singular and the given service is not the one bound |
| * to the component this method has no effect and just returns |
| * <code>true</code>. |
| * |
| * @param componentContext instance we are unbinding from |
| * @param refPair A service reference, service pair that will be unbound |
| * @param trackingCount service event count for this reference |
| * @param info EdgeInfo for the combination of this instance and this dependency manager |
| */ |
| void invokeUnbindMethod(ComponentContextImpl<S> componentContext, final RefPair<S, T> refPair, int trackingCount, |
| EdgeInfo info) |
| { |
| // The unbind method is only invoked if the implementation object is not |
| // null. This is valid for both immediate and delayed components |
| if (componentContext != null) |
| { |
| synchronized (m_tracker.tracked()) |
| { |
| if (info.beforeRange(trackingCount)) |
| { |
| //never bound |
| return; |
| } |
| } |
| info.waitForOpen(m_componentManager, getName(), "invokeUnbindMethod"); |
| boolean outOfRange; |
| synchronized (m_tracker.tracked()) |
| { |
| outOfRange = info.afterRange(trackingCount); |
| } |
| if (outOfRange) |
| { |
| //wait for unbinds to complete |
| info.waitForClose(m_componentManager, getName(), "invokeUnbindMethod"); |
| //ignore events after close started or we will have duplicate unbinds. |
| return; |
| } |
| |
| if (!getServiceObject(componentContext, m_bindMethods.getUnbind(), refPair)) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_WARNING, |
| "DependencyManager : invokeUnbindMethod : Service not available from service registry for ServiceReference {0} for reference {1}", |
| null, refPair.getRef(), getName() ); |
| return; |
| |
| } |
| MethodResult methodResult = m_bindMethods.getUnbind().invoke( |
| componentContext.getImplementationObject(false), new BindParameters(componentContext, refPair), MethodResult.VOID); |
| if (methodResult != null) |
| { |
| m_componentManager.setServiceProperties(methodResult, trackingCount); |
| } |
| componentContext.getComponentServiceObjectsHelper().closeServiceObjects(refPair.getRef()); |
| } |
| else |
| { |
| // don't care whether we can or cannot call the unbind method |
| // if the component instance has already been cleared by the |
| // close() method |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "DependencyManager : Component not set, no need to call unbind method", null); |
| } |
| } |
| |
| //------------- Service target filter support ----------------------------- |
| |
| /** |
| * Returns <code>true</code> if the <code>properties</code> can be |
| * dynamically applied to the component to which the dependency manager |
| * belongs. |
| * <p> |
| * This method applies the following heuristics (in the given order): |
| * <ol> |
| * <li>If there is no change in the target filter for this dependency, the |
| * properties can be applied</li> |
| * <li>If the dependency is static and there are changes in the target |
| * filter we cannot dynamically apply the configuration because the filter |
| * may (assume they do for simplicity here) cause the bindings to change.</li> |
| * <li>If there is still at least one service matching the new target filter |
| * we can apply the configuration because the depdency is dynamic.</li> |
| * <li>If there are no more services matching the filter, we can still |
| * apply the configuration if the dependency is optional.</li> |
| * <li>Ultimately, if all other checks do not apply we cannot dynamically |
| * apply.</li> |
| * </ol> |
| */ |
| boolean canUpdateDynamically(Map<String, Object> properties) |
| { |
| // 1. no target filter change |
| final String newTarget = (String) properties.get(m_dependencyMetadata.getTargetPropertyName()); |
| final String currentTarget = getTarget(); |
| int newMinimumCardinality = getMinimumCardinality(properties); |
| if (m_minCardinality == newMinimumCardinality && ((currentTarget == null && newTarget == null) |
| || (currentTarget != null && currentTarget.equals(newTarget)))) |
| { |
| // can update if target filter is not changed, since there is |
| // no change is service binding |
| return true; |
| } |
| // invariant: target filter change |
| |
| // 2. if static policy, cannot update dynamically |
| // (for simplicity assuming change in target service binding) |
| if (m_dependencyMetadata.isStatic()) |
| { |
| // cannot update if services are statically bound and the target |
| // filter is modified, since there is (potentially at least) |
| // a change is service bindings |
| return false; |
| } |
| // invariant: target filter change + dynamic policy |
| |
| // 3. check optionality |
| if (newMinimumCardinality == 0) |
| { |
| // can update since even if no service matches the new filter, this |
| // makes no difference because the dependency is optional |
| return true; |
| } |
| // invariant: target filter change + mandatory + dynamic policy |
| |
| // 4. check target services matching the new filter |
| ServiceReference<T>[] refs = getFrameworkServiceReferences(newTarget); |
| if (refs != null) |
| { |
| // Return whether there are enough target services |
| return newMinimumCardinality <= refs.length; |
| } |
| // invariant: target filter change + dynamic policy + no more matching service + required |
| |
| // 5. There are no services, and some are required. |
| return false; |
| } |
| |
| /** |
| * Sets the target filter from target filter property contained in the |
| * properties. The filter is taken from a property whose name is derived |
| * from the dependency name and the suffix <code>.target</code> as defined |
| * for target properties on page 302 of the Declarative Services |
| * Specification, section 112.6. |
| * |
| * @param properties The properties containing the optional target service |
| * filter property |
| */ |
| void setTargetFilter(Map<String, Object> properties) |
| { |
| Integer minimumCardinality = getMinimumCardinality(properties); |
| setTargetFilter((String) properties.get(m_dependencyMetadata.getTargetPropertyName()), minimumCardinality); |
| } |
| |
| private int getMinimumCardinality(Map<String, Object> properties) |
| { |
| Integer minimumCardinality = null; |
| try |
| { |
| minimumCardinality = Coercions.coerceToInteger( |
| properties.get(m_dependencyMetadata.getMinCardinalityName())); |
| } |
| catch (ComponentException e) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_WARNING, |
| "Invalid minimum cardinality property for dependency {0}: {1}", |
| null, getName(), e.getMessage()); |
| } |
| if (minimumCardinality != null && (minimumCardinality < defaultMinimumCardinality(m_dependencyMetadata) |
| || (!m_dependencyMetadata.isMultiple() && minimumCardinality > 1))) |
| { |
| minimumCardinality = null; |
| } |
| if (minimumCardinality == null) |
| { |
| minimumCardinality = defaultMinimumCardinality(m_dependencyMetadata); |
| } |
| return minimumCardinality; |
| } |
| |
| private static final String OBJECTCLASS_CLAUSE = "(" + Constants.OBJECTCLASS + "="; |
| private static final String PROTOTYPE_SCOPE_CLAUSE = "(" + Constants.SERVICE_SCOPE + "=" + Constants.SCOPE_PROTOTYPE |
| + ")"; |
| |
| /** |
| * Sets the target filter of this dependency to the new filter value. If the |
| * new target filter is the same as the old target filter, this method has |
| * not effect. Otherwise any services currently bound but not matching the |
| * new filter are unbound. Likewise any registered services not currently |
| * bound but matching the new filter are bound. |
| * |
| * @param target The new target filter to be set. This may be |
| * <code>null</code> if no target filtering is to be used. |
| */ |
| private void setTargetFilter(String target, int minimumCardinality) |
| { |
| // if configuration does not set filter, use the value from metadata |
| if (target == null) |
| { |
| target = m_dependencyMetadata.getTarget(); |
| } |
| // do nothing if target filter does not change |
| if ((m_target == null && target == null) || (m_target != null && m_target.equals(target))) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "No change in target property for dependency {0}: currently registered: {1}", |
| null, getName(), m_tracker != null ); |
| if (m_tracker != null) |
| { |
| m_minCardinality = minimumCardinality; |
| return; |
| } |
| } |
| m_target = target; |
| |
| // three filters are created: |
| // classFilter = filters only on the service interface |
| // eventFilter = filters only on the provided target and service scope (if prototype required) |
| // initialReferenceFilter = classFilter & eventFilter |
| |
| // classFilter |
| // "(" + Constants.OBJECTCLASS + "=" + m_dependencyMetadata.getInterface() + ")" |
| final StringBuilder classFilterSB = new StringBuilder(); |
| classFilterSB.append(OBJECTCLASS_CLAUSE); |
| classFilterSB.append(m_dependencyMetadata.getInterface()); |
| classFilterSB.append(')'); |
| final String classFilterString = classFilterSB.toString(); |
| |
| // eventFilter |
| String eventFilterString; |
| if (m_target != null |
| && m_dependencyMetadata.getScope() == ReferenceScope.prototype_required) |
| { |
| final StringBuilder sb = new StringBuilder("(&").append(PROTOTYPE_SCOPE_CLAUSE).append(m_target).append(")"); |
| eventFilterString = sb.toString(); |
| } |
| else if ( m_dependencyMetadata.getScope() == ReferenceScope.prototype_required ) |
| { |
| eventFilterString = PROTOTYPE_SCOPE_CLAUSE; |
| } |
| else |
| { |
| eventFilterString = m_target; |
| } |
| |
| // initialReferenceFilter |
| final boolean multipleExpr = m_target != null |
| || m_dependencyMetadata.getScope() == ReferenceScope.prototype_required; |
| final StringBuilder initialReferenceFilterSB = new StringBuilder(); |
| if (multipleExpr) |
| { |
| initialReferenceFilterSB.append("(&"); |
| } |
| initialReferenceFilterSB.append(classFilterString); |
| |
| // if reference scope is prototype_required, we simply add |
| // (service.scope=prototype) to the filter |
| if (m_dependencyMetadata.getScope() == ReferenceScope.prototype_required) |
| { |
| initialReferenceFilterSB.append(PROTOTYPE_SCOPE_CLAUSE); |
| } |
| |
| // append target |
| if (m_target != null) |
| { |
| initialReferenceFilterSB.append(m_target); |
| } |
| if (multipleExpr) |
| { |
| initialReferenceFilterSB.append(')'); |
| } |
| String initialReferenceFilterString = initialReferenceFilterSB.toString(); |
| |
| final ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> oldTracker = m_tracker; |
| AtomicInteger trackingCount = new AtomicInteger(); |
| SortedMap<ServiceReference<T>, RefPair<S, T>> refMap = unregisterServiceListener(trackingCount); |
| if (trackingCount.get() != -1) |
| { |
| //wait for service events to complete before processing initial set from new tracker. |
| m_componentManager.waitForTracked(trackingCount.get()); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "Setting target property for dependency {0} to {1}", |
| null, getName(), target ); |
| BundleContext bundleContext = m_componentManager.getBundleContext(); |
| Filter eventFilter = null; |
| if (bundleContext != null) |
| { |
| if (eventFilterString != null) |
| { |
| try |
| { |
| eventFilter = bundleContext.createFilter(eventFilterString); |
| } |
| catch (InvalidSyntaxException ise) |
| { |
| m_componentManager.getLogger().log(LogService.LOG_ERROR, |
| "Invalid syntax in target property for dependency {0} to {1}", |
| null, getName(), target ); |
| |
| //create a filter that will never be satisfied |
| eventFilterString = "(component.id=-1)"; |
| try |
| { |
| eventFilter = bundleContext.createFilter(eventFilterString); |
| } |
| catch (InvalidSyntaxException e) |
| { |
| //this should not happen |
| return; |
| } |
| |
| } |
| } |
| } |
| else |
| { |
| m_componentManager.getLogger().log(LogService.LOG_ERROR, "Bundle is shut down for dependency {0} to {1}", |
| null, getName(), target ); |
| return; |
| } |
| |
| m_customizer.setPreviousRefMap(refMap); |
| boolean initialActive = oldTracker != null && oldTracker.isActive(); |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| "New service tracker for {0}, initial active: {1}, previous references: {2}, classFilter: {3}, eventFilter {4}, initialReferenceFilter {5}", |
| null, getName(), initialActive, refMap, classFilterString, eventFilter, |
| initialReferenceFilterString ); |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = new ServiceTracker<>( |
| bundleContext, m_customizer, initialActive, m_componentManager.getActivator(), eventFilter, |
| classFilterString, initialReferenceFilterString); |
| m_customizer.setTracker(tracker); |
| //set minimum cardinality |
| m_minCardinality = minimumCardinality; |
| |
| tracker.open(m_componentManager.getTrackingCount()); |
| m_customizer.setTrackerOpened(); |
| if (oldTracker != null) |
| { |
| oldTracker.completeClose(refMap); |
| } |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "registering service listener for dependency {0}", |
| null, getName()); |
| } |
| |
| private Customizer<S, T> newCustomizer() |
| { |
| Customizer<S, T> customizer; |
| if (!hasGetPermission()) |
| { |
| customizer = new NoPermissionsCustomizer(); |
| m_componentManager.getLogger().log(LogService.LOG_INFO, "No permission to get services for {0}", |
| null, getName()); |
| } |
| else if (m_componentManager.isFactory()) |
| { |
| customizer = new FactoryCustomizer(); |
| } |
| else if (isMultiple()) |
| { |
| if (isStatic()) |
| { |
| if (isReluctant()) |
| { |
| customizer = new MultipleStaticReluctantCustomizer(); |
| } |
| else |
| { |
| customizer = new MultipleStaticGreedyCustomizer(); |
| } |
| } |
| else |
| { |
| customizer = new MultipleDynamicCustomizer(); |
| } |
| } |
| else |
| { |
| if (isStatic()) |
| { |
| customizer = new SingleStaticCustomizer(); |
| } |
| else |
| { |
| customizer = new SingleDynamicCustomizer(); |
| } |
| } |
| return customizer; |
| } |
| |
| SortedMap<ServiceReference<T>, RefPair<S, T>> unregisterServiceListener(AtomicInteger trackingCount) |
| { |
| SortedMap<ServiceReference<T>, RefPair<S, T>> refMap; |
| ServiceTracker<T, RefPair<S, T>, ExtendedServiceEvent> tracker = m_tracker; |
| if (tracker != null) |
| { |
| refMap = tracker.close(trackingCount); |
| m_tracker = null; |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, "unregistering service listener for dependency {0}", |
| null, getName()); |
| } |
| else |
| { |
| refMap = new TreeMap<>(Collections.reverseOrder()); |
| m_componentManager.getLogger().log(LogService.LOG_DEBUG, |
| " No existing service listener to unregister for dependency {0}", null, getName()); |
| trackingCount.set(-1); |
| } |
| // m_registered = false; |
| return refMap; |
| } |
| |
| /* (non-Javadoc) |
| * @see org.apache.felix.scr.impl.manager.ReferenceManager#getTarget() |
| */ |
| @Override |
| public String getTarget() |
| { |
| return m_target; |
| } |
| |
| @Override |
| public String toString() |
| { |
| return "DependencyManager: Component [" + m_componentManager + "] reference [" + getName() + "]"; |
| } |
| |
| boolean getServiceObject(ComponentContextImpl<S> key, ReferenceMethod bindMethod, RefPair<S, T> refPair) |
| { |
| BundleContext bundleContext = m_componentManager.getBundleContext(); |
| if (bundleContext != null) |
| { |
| return bindMethod.getServiceObject(new BindParameters(key, refPair), bundleContext); |
| } |
| else |
| { |
| refPair.markFailed(); |
| return false; |
| } |
| } |
| |
| RefPair<S, T> newRefPair(ServiceReference<T> serviceReference) |
| { |
| if (m_dependencyMetadata.getScope() == ReferenceScope.bundle) |
| { |
| return new SingleRefPair<>(serviceReference); |
| } |
| if (m_componentManager.getComponentMetadata().getServiceScope() == Scope.singleton) |
| { |
| return new SinglePrototypeRefPair<>(serviceReference); |
| } |
| return new MultiplePrototypeRefPair<>(serviceReference); |
| } |
| |
| private void deactivateComponentManager() |
| { |
| m_componentManager.deactivateInternal(ComponentConstants.DEACTIVATION_REASON_REFERENCE, false, false); |
| } |
| |
| } |