blob: 7bba1ccd200529a53e102db195e2b0769b771203 [file] [log] [blame]
/*
* 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.ipojo.dependency.impl;
import org.apache.felix.ipojo.Factory;
import org.apache.felix.ipojo.dependency.interceptors.ServiceBindingInterceptor;
import org.apache.felix.ipojo.dependency.interceptors.ServiceRankingInterceptor;
import org.apache.felix.ipojo.dependency.interceptors.ServiceTrackingInterceptor;
import org.apache.felix.ipojo.dependency.interceptors.TransformedServiceReference;
import org.apache.felix.ipojo.util.*;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;
import java.util.*;
import static org.apache.felix.ipojo.util.DependencyModel.DependencyEventType;
import static org.apache.felix.ipojo.util.DependencyModel.DependencyEventType.ARRIVAL;
import static org.apache.felix.ipojo.util.DependencyModel.DependencyEventType.DEPARTURE;
import static org.apache.felix.ipojo.util.DependencyModel.DependencyEventType.MODIFIED;
/**
* This class is handling the transformations between the base service set and the selected service set.
* It handles the matching services and the selected service set.
* As this class is tied to the dependency model, it reuses the same locks objects.
*/
public class ServiceReferenceManager implements TrackerCustomizer {
/**
* The dependency.
*/
private final DependencyModel m_dependency;
/**
* The list of all matching service references. This list is a
* subset of tracked references. This set is computed according
* to the filter and the {@link DependencyModel#match(ServiceReference)} method.
*/
private final Map<ServiceReference, TransformedServiceReference> m_matchingReferences = new
LinkedHashMap<ServiceReference, TransformedServiceReference>();
/**
* The comparator to sort service references.
*/
private Comparator<ServiceReference> m_comparator;
/**
* The LDAP filter object selecting service references
* from the set of providers providing the required specification.
*/
private Filter m_filter;
/**
* The list of selected service references.
*/
private List<? extends ServiceReference> m_selectedReferences = new ArrayList<ServiceReference>();
/**
* The service ranking interceptor.
*/
private ServiceRankingInterceptor m_rankingInterceptor;
/**
* Service Ranking Interceptor trackers.
*/
private Tracker m_rankingInterceptorTracker;
/**
* Service Tracking Interceptor trackers.
*/
private Tracker m_trackingInterceptorTracker;
/**
* Service Binding Interceptor trackers.
*/
private Tracker m_bindingInterceptorTracker;
/**
* The set of tracking interceptors.
* TODO this set should be sorted according to the OSGi ranking policy.
* The filter is always the last interceptor.
*/
private LinkedList<ServiceTrackingInterceptor> m_trackingInterceptors = new
LinkedList<ServiceTrackingInterceptor>();
/**
* The set of binding interceptors.
* TODO this set should be sorted according to the OSGi ranking policy.
*/
private LinkedList<ServiceBindingInterceptor> m_bindingInterceptors = new
LinkedList<ServiceBindingInterceptor>();
/**
* Creates the service reference manager.
*
* @param dep the dependency
* @param filter the filter
* @param comparator the comparator
*/
public ServiceReferenceManager(DependencyModel dep, Filter filter, Comparator<ServiceReference> comparator) {
m_dependency = dep;
m_filter = filter;
// The Filter based service tracking interceptor needs to be created every time even if the filter is null.
// This arises from the potential re-implementation of the match method in the dependency implementation.
// It must be the last interceptor as the chain ends on the filter matching. (FELIX-4199)
m_trackingInterceptors.addLast(new FilterBasedServiceTrackingInterceptor(m_filter));
if (comparator != null) {
m_comparator = comparator;
m_rankingInterceptor = new ComparatorBasedServiceRankingInterceptor(comparator);
} else {
m_rankingInterceptor = new EmptyBasedServiceRankingInterceptor();
}
}
public void open() {
// The opening order matters, first binding, then ranking and finally tracking.
m_bindingInterceptorTracker = new Tracker(m_dependency.getBundleContext(),
ServiceBindingInterceptor.class.getName(),
new TrackerCustomizer() {
public boolean addingService(ServiceReference reference) {
return DependencyProperties.match(reference, m_dependency);
}
public void addedService(ServiceReference reference) {
ServiceBindingInterceptor interceptor = (ServiceBindingInterceptor) m_bindingInterceptorTracker
.getService(reference);
if (interceptor != null) {
addBindingInterceptor(interceptor);
} else {
m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
"Cannot retrieve the interceptor object from service reference " + reference
.getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
(Factory.INSTANCE_NAME_PROPERTY));
}
}
public void modifiedService(ServiceReference reference, Object service) {
// Not supported.
}
public void removedService(ServiceReference reference, Object service) {
if (service != null && service instanceof ServiceBindingInterceptor &&
m_bindingInterceptors.contains(service)
) {
removeBindingInterceptor((ServiceBindingInterceptor) service);
}
}
}
);
m_bindingInterceptorTracker.open();
// Initialize the service interceptor tracker.
m_rankingInterceptorTracker = new Tracker(m_dependency.getBundleContext(), ServiceRankingInterceptor.class.getName(),
new TrackerCustomizer() {
public boolean addingService(ServiceReference reference) {
return DependencyProperties.match(reference, m_dependency);
}
public void addedService(ServiceReference reference) {
ServiceRankingInterceptor interceptor = (ServiceRankingInterceptor) m_rankingInterceptorTracker
.getService(reference);
if (interceptor != null) {
setRankingInterceptor(interceptor);
} else {
m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
"Cannot retrieve the interceptor object from service reference " + reference
.getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
(Factory.INSTANCE_NAME_PROPERTY));
}
}
public void modifiedService(ServiceReference reference, Object service) {
// Not supported yet.
// TODO it would be nice to support the modification of the interceptor TARGET property.
}
public void removedService(ServiceReference reference, Object service) {
if (service == m_rankingInterceptor) {
m_rankingInterceptor.close(m_dependency);
// Do we have another one ?
ServiceReference anotherReference = m_rankingInterceptorTracker.getServiceReference();
if (anotherReference != null) {
ServiceRankingInterceptor interceptor = (ServiceRankingInterceptor) m_rankingInterceptorTracker
.getService(anotherReference);
if (interceptor != null) setRankingInterceptor(interceptor);
} else if (m_comparator != null) {
// If we have a comparator, we restore the comparator.
setComparator(m_comparator);
} else {
// If we have neither comparator nor interceptor use an empty interceptor.
setRankingInterceptor(new EmptyBasedServiceRankingInterceptor());
}
}
}
});
m_rankingInterceptorTracker.open();
m_trackingInterceptorTracker = new Tracker(m_dependency.getBundleContext(),
ServiceTrackingInterceptor.class.getName(),
new TrackerCustomizer() {
public boolean addingService(ServiceReference reference) {
return DependencyProperties.match(reference, m_dependency);
}
public void addedService(ServiceReference reference) {
ServiceTrackingInterceptor interceptor = (ServiceTrackingInterceptor) m_trackingInterceptorTracker
.getService(reference);
if (interceptor != null) {
addTrackingInterceptor(interceptor);
} else {
m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
"Cannot retrieve the interceptor object from service reference " + reference
.getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
(Factory.INSTANCE_NAME_PROPERTY));
}
}
public void modifiedService(ServiceReference reference, Object service) {
// Not supported yet.
// TODO it would be nice to support the modification of the interceptor TARGET property.
}
public void removedService(ServiceReference reference, Object service) {
if (service != null && service instanceof ServiceTrackingInterceptor &&
m_trackingInterceptors.contains(service)
) {
removeTrackingInterceptor((ServiceTrackingInterceptor) service);
}
}
});
m_trackingInterceptorTracker.open();
}
private void addTrackingInterceptor(ServiceTrackingInterceptor interceptor) {
// A new interceptor arrives. Insert it at the beginning of the list.
ChangeSet changeset;
try {
m_dependency.acquireWriteLockIfNotHeld();
m_trackingInterceptors.addFirst(interceptor);
interceptor.open(m_dependency);
changeset = computeChangesInMatchingServices();
} finally {
m_dependency.releaseWriteLockIfHeld();
}
m_dependency.onChange(changeset);
}
private void removeTrackingInterceptor(ServiceTrackingInterceptor interceptor) {
ChangeSet changeset;
try {
m_dependency.acquireWriteLockIfNotHeld();
m_trackingInterceptors.remove(interceptor);
interceptor.close(m_dependency);
changeset = computeChangesInMatchingServices();
} finally {
m_dependency.releaseWriteLockIfHeld();
}
m_dependency.onChange(changeset);
}
private void addBindingInterceptor(ServiceBindingInterceptor interceptor) {
// A new interceptor arrives, open it.
// Binding interceptor cannot modify existing bindings.
try {
m_dependency.acquireWriteLockIfNotHeld();
m_bindingInterceptors.add(interceptor);
interceptor.open(m_dependency);
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
private void removeBindingInterceptor(ServiceBindingInterceptor interceptor) {
try {
m_dependency.acquireWriteLockIfNotHeld();
m_bindingInterceptors.remove(interceptor);
interceptor.close(m_dependency);
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
public Object weavingServiceBinding(DependencyModel.ServiceBindingHolder sbh) {
Object svc = sbh.service;
try {
m_dependency.acquireReadLockIfNotHeld();
for (ServiceBindingInterceptor interceptor : m_bindingInterceptors) {
// Interceptor are not allowed to return null.
svc = interceptor.getService(m_dependency, sbh.reference, svc);
}
} finally {
m_dependency.releaseReadLockIfHeld();
}
return svc;
}
public void unweavingServiceBinding(DependencyModel.ServiceBindingHolder sbh) {
try {
m_dependency.acquireReadLockIfNotHeld();
for (ServiceBindingInterceptor interceptor : m_bindingInterceptors) {
interceptor.ungetService(m_dependency, sbh.reference);
}
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
private ChangeSet computeChangesInMatchingServices() {
if (m_dependency.getTracker() == null || m_dependency.getTracker().getServiceReferences() == null) {
// Tracker closed, no problem
m_dependency.getComponentInstance().getFactory().getLogger().log(Logger.DEBUG,
"Tracker closed when recomputing dependency " + m_dependency.getId());
return new ChangeSet(Collections.<ServiceReference>emptyList(),
Collections.<ServiceReference>emptyList(),
Collections.<ServiceReference>emptyList(),
null,
null,
null,
null);
}
// The set of interceptor has changed.
try {
m_dependency.acquireWriteLockIfNotHeld();
// The tracker is open, we must recheck all services.
ServiceReference oldBest = getFirstService();
// Recompute the matching services.
m_matchingReferences.clear();
final List<ServiceReference> serviceReferencesList = m_dependency.getTracker().getServiceReferencesList();
if (serviceReferencesList != null) {
for (ServiceReference reference : serviceReferencesList) {
TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
ref = accept(ref);
if (ref != null) {
m_matchingReferences.put(reference, ref);
}
}
}
m_dependency.getComponentInstance().getFactory().getLogger().log(Logger.DEBUG,
"Matching services have been recomputed: " + ServiceReferenceUtils.toString(m_matchingReferences.values()));
// We have the new matching set.
List<ServiceReference> beforeRanking = getSelectedServices();
final List<ServiceReference> allServices = getMatchingServices();
List<ServiceReference> references;
if (allServices.isEmpty()) {
references = Collections.emptyList();
} else {
m_dependency.getComponentInstance().getFactory().getLogger().log(Logger.DEBUG,
"iPOJO >> Calling getServiceReferences on the interceptor " + m_rankingInterceptor);
references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
}
RankingResult result = computeDifferences(beforeRanking, references);
m_selectedReferences = result.selected;
ServiceReference newFirst = getFirstService();
ServiceReference modified = null;
if (ServiceReferenceUtils.haveSameServiceId(oldBest, newFirst) && ServiceReferenceUtils
.haveSameProperties(oldBest, newFirst)) {
modified = newFirst;
}
return new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest, getFirstService(),
null, modified);
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
public List<ServiceReference> getMatchingServices() {
try {
m_dependency.acquireReadLockIfNotHeld();
return new ArrayList<ServiceReference>(m_matchingReferences.values());
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public List<ServiceReference> getSelectedServices() {
try {
m_dependency.acquireReadLockIfNotHeld();
return new ArrayList<ServiceReference>(m_selectedReferences);
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public ServiceReference getFirstService() {
try {
m_dependency.acquireReadLockIfNotHeld();
if (m_selectedReferences.isEmpty()) {
return null;
}
return m_selectedReferences.get(0);
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public boolean contains(ServiceReference ref) {
try {
m_dependency.acquireReadLockIfNotHeld();
return m_selectedReferences.contains(ref);
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public void reset() {
try {
m_dependency.acquireWriteLockIfNotHeld();
m_rankingInterceptor.close(m_dependency);
for (ServiceTrackingInterceptor interceptor : m_trackingInterceptors) {
interceptor.close(m_dependency);
}
m_trackingInterceptors.clear();
m_matchingReferences.clear();
m_selectedReferences = new ArrayList<TransformedServiceReference>();
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
public boolean addingService(ServiceReference reference) {
// We accept all service references except if we are frozen or broken. In these case, just ignore everything.
// We are doing two tests, we must get the read lock
try {
m_dependency.acquireReadLockIfNotHeld();
return !(m_dependency.getState() == DependencyModel.BROKEN || m_dependency.isFrozen());
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
/**
* Checks if the given reference is accepted.
* This method is called when holding the write lock on the dependency.
*
* @param reference the reference
* @param <S> the service interface
* @return the transformed reference, null if rejected
*/
private <S> TransformedServiceReference<S> accept(TransformedServiceReference<S> reference) {
TransformedServiceReference<S> accumulator = reference;
for (ServiceTrackingInterceptor interceptor : m_trackingInterceptors) {
TransformedServiceReference<S> accepted = interceptor.accept(m_dependency,
m_dependency.getBundleContext(), accumulator);
if (accepted != null) {
accumulator = accepted;
} else {
// refused by an interceptor
m_dependency.getComponentInstance().getFactory().getLogger().log(Log.INFO,
"The service reference " + reference.getProperty(Constants.SERVICE_ID) + " was rejected by " +
"interceptor " + interceptor);
return null;
}
}
return accumulator;
}
public void addedService(ServiceReference reference) {
// A service was added to the tracker.
// First, check is the tracking interceptors are accepting it.
// The transformed reference is creates and check outside of the protected region.
TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
boolean match;
try {
m_dependency.acquireWriteLockIfNotHeld();
ref = accept(ref);
if (ref != null) {
m_matchingReferences.put(reference, ref);
}
match = ref != null;
} finally {
m_dependency.releaseWriteLockIfHeld();
}
if (match) {
// Callback invoked outside of locks.
// The called method is taking the write lock anyway.
onNewMatchingService(ref);
m_dependency.notifyListeners(ARRIVAL, ref, null);
}
}
private void onNewMatchingService(TransformedServiceReference reference) {
ServiceReference oldFirst;
RankingResult result;
try {
m_dependency.acquireWriteLockIfNotHeld();
// We store the currently 'first' service reference.
oldFirst = getFirstService();
// We apply our ranking strategy.
result = applyRankingOnArrival(reference);
// Set the selected services.
m_selectedReferences = result.selected;
} finally {
m_dependency.releaseWriteLockIfHeld();
}
// Fire the event (outside from the synchronized region)
fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
getFirstService(), null, null);
}
private void onModificationOfAMatchingService(TransformedServiceReference reference, Object service) {
ServiceReference oldFirst;
RankingResult result;
try {
m_dependency.acquireWriteLockIfNotHeld();
// We store the currently 'first' service reference.
oldFirst = getFirstService();
// We apply our ranking strategy.
result = applyRankingOnModification(reference);
// Set the selected services.
m_selectedReferences = result.selected;
} finally {
m_dependency.releaseWriteLockIfHeld();
}
// Fire the event (outside from the synchronized region)
fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
getFirstService(), service, reference);
}
private RankingResult applyRankingOnModification(ServiceReference reference) {
// TODO we are holding the lock here.
List<ServiceReference> beforeRanking = getSelectedServices();
List<ServiceReference> references = m_rankingInterceptor.onServiceModified(m_dependency, getMatchingServices(),
reference);
return computeDifferences(beforeRanking, references);
}
private void fireUpdate(List<ServiceReference> selectedServices, List<ServiceReference> departures,
List<ServiceReference> arrivals, ServiceReference oldFirst,
ServiceReference firstService, Object service, ServiceReference modified) {
ChangeSet set = new ChangeSet(selectedServices, departures, arrivals, oldFirst, firstService, service, modified);
m_dependency.onChange(set);
}
private RankingResult applyRankingOnArrival(ServiceReference ref) {
// TODO we are holding the lock here.
List<ServiceReference> beforeRanking = getSelectedServices();
List<ServiceReference> references = m_rankingInterceptor.onServiceArrival(m_dependency, getMatchingServices(),
ref);
// compute the differences
return computeDifferences(beforeRanking, references);
}
private RankingResult applyRankingOnDeparture(ServiceReference ref) {
// TODO we are holding the lock here.
List<ServiceReference> beforeRanking = getSelectedServices();
List<ServiceReference> references = m_rankingInterceptor.onServiceDeparture(m_dependency, getMatchingServices(),
ref);
return computeDifferences(beforeRanking, references);
}
private RankingResult computeDifferences(List<ServiceReference> beforeRanking, List<ServiceReference> ranked) {
// compute the differences
List<ServiceReference> departures = new ArrayList<ServiceReference>();
List<ServiceReference> arrivals = new ArrayList<ServiceReference>();
// All references that are no more in the set are considered as leaving services.
for (ServiceReference old : beforeRanking) {
if (!ServiceReferenceUtils.containsReferenceById(ranked, old)) {
departures.add(old);
}
}
// All references that are in `references` but not in `beforeRanking` are new services
for (ServiceReference newRef : ranked) {
if (!ServiceReferenceUtils.containsReferenceById(beforeRanking, newRef)) {
arrivals.add(newRef);
}
}
return new RankingResult(departures, arrivals, ranked);
}
public void modifiedService(ServiceReference reference, Object service) {
// We are handling a modified event, we have three case to handle
// 1) the service was matching and does not match anymore -> it's a departure.
// 2) the service was not matching and matches -> it's an arrival
// 3) the service was matching and still matches -> it's a modification.
// The dependency event to send
DependencyEventType eventType = null;
ServiceReference<?> eventRef = null;
try {
m_dependency.acquireWriteLockIfNotHeld();
if (m_matchingReferences.containsKey(reference)) {
// do we still accept the reference
TransformedServiceReference initial = m_matchingReferences.get(reference);
TransformedServiceReference accepted = new TransformedServiceReferenceImpl(reference);
accepted = accept(accepted);
if (accepted == null) {
// case 1
m_matchingReferences.remove(reference);
onDepartureOfAMatchingService(initial, service);
eventType = DEPARTURE;
eventRef = initial;
} else {
// Do we have a real change
if (!ServiceReferenceUtils.haveSameProperties(initial, accepted)) {
// case 3
m_matchingReferences.put(reference, accepted);
onModificationOfAMatchingService(accepted, service);
eventType = MODIFIED;
eventRef = accepted;
}
}
} else {
// Base does not contain the service, let's try to add it.
TransformedServiceReference transformed = new TransformedServiceReferenceImpl(reference);
transformed = accept(transformed);
if (transformed != null) {
// case 2
m_matchingReferences.put(reference, transformed);
onNewMatchingService(transformed);
eventType = ARRIVAL;
eventRef = transformed;
}
}
} finally {
m_dependency.releaseWriteLockIfHeld();
}
if (eventType != null) {
m_dependency.notifyListeners(eventType, eventRef, service);
}
}
public void onDepartureOfAMatchingService(TransformedServiceReference reference, Object service) {
ServiceReference oldFirst;
RankingResult result = null;
try {
m_dependency.acquireWriteLockIfNotHeld();
// We store the currently 'first' service reference.
oldFirst = getFirstService();
// We apply our ranking strategy.
result = applyRankingOnDeparture(reference);
// Set the selected services.
m_selectedReferences = result.selected;
} finally {
m_dependency.releaseWriteLockIfHeld();
}
// Fire the event (outside from the synchronized region)
fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
getFirstService(), service, null);
}
public void removedService(ServiceReference reference, Object service) {
// A service is leaving
// 1 - the service was in the matching set => real departure
// 2 - the service was not in the matching set => nothing do do.
TransformedServiceReference initial = null;
try {
m_dependency.acquireWriteLockIfNotHeld();
initial = m_matchingReferences.remove(reference);
if (initial != null) {
// Case 1
onDepartureOfAMatchingService(initial, service);
}
// else case 2.
} finally {
m_dependency.releaseWriteLockIfHeld();
}
// Call the listeners outside the locked region.
if (initial != null) {
m_dependency.notifyListeners(DEPARTURE, initial, service);
}
}
/**
* A new filter is set.
* We have to recompute the set of matching services.
*
* @param filter the new filter
* @param tracker the tracker
*/
public ChangeSet setFilter(Filter filter, Tracker tracker) {
try {
m_dependency.acquireWriteLockIfNotHeld();
m_filter = filter;
if (!m_trackingInterceptors.isEmpty()) {
ServiceTrackingInterceptor interceptor = m_trackingInterceptors.getLast();
if (interceptor != null && interceptor instanceof FilterBasedServiceTrackingInterceptor) {
// Remove it first.
m_trackingInterceptors.removeLast();
}
}
if (m_filter != null) {
// Add the new one.
ServiceTrackingInterceptor newInterceptor = new FilterBasedServiceTrackingInterceptor(m_filter);
m_trackingInterceptors.addLast(newInterceptor);
}
if (tracker == null) {
// Tracker closed, no problem
return new ChangeSet(Collections.<ServiceReference>emptyList(),
Collections.<ServiceReference>emptyList(),
Collections.<ServiceReference>emptyList(),
null,
null,
null,
null);
} else {
// The tracker is open, we must recheck all services.
ServiceReference oldBest = getFirstService();
// Recompute the matching services.
m_matchingReferences.clear();
final List<ServiceReference> serviceReferencesList = tracker.getServiceReferencesList();
if (serviceReferencesList != null) {
for (ServiceReference reference : serviceReferencesList) {
TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
ref = accept(ref);
if (ref != null) {
m_matchingReferences.put(reference, ref);
}
}
}
// We have the new matching set.
List<ServiceReference> beforeRanking = getSelectedServices();
final List<ServiceReference> allServices = getMatchingServices();
List<ServiceReference> references;
if (allServices.isEmpty()) {
references = Collections.emptyList();
} else {
references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
}
RankingResult result = computeDifferences(beforeRanking, references);
m_selectedReferences = result.selected;
return new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest, getFirstService(),
null, null);
}
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
public boolean isEmpty() {
try {
m_dependency.acquireReadLockIfNotHeld();
return m_selectedReferences.isEmpty();
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public Comparator<ServiceReference> getComparator() {
try {
m_dependency.acquireReadLockIfNotHeld();
return m_comparator;
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public void setComparator(Comparator<ServiceReference> cmp) {
try {
m_dependency.acquireWriteLockIfNotHeld();
if (cmp == null) {
m_comparator = new ServiceReferenceRankingComparator();
} else {
m_comparator = cmp;
}
// Be aware that this method will release the lock to call the dependency callback.
setRankingInterceptor(new ComparatorBasedServiceRankingInterceptor(m_comparator));
} finally {
m_dependency.releaseWriteLockIfHeld();
}
}
public Filter getFilter() {
try {
m_dependency.acquireReadLockIfNotHeld();
return m_filter;
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
public void setRankingInterceptor(ServiceRankingInterceptor interceptor) {
m_dependency.getComponentInstance().getFactory().getLogger().log(Log.INFO, "Dependency " + m_dependency.getId
() + " is getting a new ranking interceptor : " + interceptor);
ChangeSet changeSet;
try {
m_dependency.acquireWriteLockIfNotHeld();
ServiceReference oldBest = getFirstService();
List<ServiceReference> beforeRanking = getSelectedServices();
m_rankingInterceptor = interceptor;
m_rankingInterceptor.open(m_dependency);
final List<ServiceReference> allServices = getMatchingServices();
List<ServiceReference> references = Collections.emptyList();
if (!allServices.isEmpty()) {
references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
}
RankingResult result = computeDifferences(beforeRanking, references);
m_selectedReferences = result.selected;
changeSet = new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest,
getFirstService(), null, null);
} finally {
m_dependency.releaseWriteLockIfHeld();
}
// Calling onChange outside of the lock.
m_dependency.onChange(changeSet);
}
public void close() {
reset();
}
public void invalidateMatchingServices() {
ChangeSet changeset;
try {
m_dependency.acquireWriteLockIfNotHeld();
m_matchingReferences.clear();
changeset = computeChangesInMatchingServices();
} finally {
m_dependency.releaseWriteLockIfHeld();
}
m_dependency.onChange(changeset);
}
public void invalidateSelectedServices() {
ChangeSet changeset;
try {
m_dependency.acquireWriteLockIfNotHeld();
ServiceReference oldBest = getFirstService();
List<ServiceReference> beforeRanking = getSelectedServices();
m_selectedReferences.clear();
final List<ServiceReference> allServices = getMatchingServices();
List<ServiceReference> references = Collections.emptyList();
if (!allServices.isEmpty()) {
references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
}
RankingResult result = computeDifferences(beforeRanking, references);
m_selectedReferences = result.selected;
changeset = new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest,
getFirstService(), null, null);
} finally {
m_dependency.releaseWriteLockIfHeld();
}
m_dependency.onChange(changeset);
}
/**
* Gets the list of tracking interceptors attached to the current service dependency.
* @return the list of service references of the tracking interceptors participating to the resolution of the
* current service dependency. An empty list is returned is there are no participating interceptors.
* @since 1.11.0
*/
public List<ServiceReference> getTrackingInterceptorReferences() {
try {
m_dependency.acquireReadLockIfNotHeld();
if (m_trackingInterceptorTracker != null) {
List<ServiceReference> refs = m_trackingInterceptorTracker.getUsedServiceReferences();
if (refs != null) {
return refs;
}
}
return Collections.emptyList();
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
/**
* Gets the list of binding interceptors attached to the current service dependency.
* @return the list of service references of the binding interceptors participating to the resolution of the
* current service dependency. An empty list is returned is there are no participating interceptors.
* @since 1.11.0
*/
public List<ServiceReference> getBindingInterceptorReferences() {
try {
m_dependency.acquireReadLockIfNotHeld();
if (m_bindingInterceptorTracker != null) {
List<ServiceReference> refs = m_bindingInterceptorTracker.getUsedServiceReferences();
if (refs != null) {
return refs;
}
}
return Collections.emptyList();
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
/**
* Gets the service reference of the currently attached ranking interceptor. As only one ranking interceptor can
* be attached at a point on time, this is not a list but only one reference.
* @return the service reference of the ranking interceptor participating to the resolution of the current
* service dependency. {@code null} if no (external) ranking interceptor is currently attached.
* @since 1.11.0
*/
public ServiceReference getRankingInterceptorReference() {
try {
m_dependency.acquireReadLockIfNotHeld();
if (m_rankingInterceptorTracker != null) {
List<ServiceReference> references = m_rankingInterceptorTracker.getUsedServiceReferences();
if (references != null && ! references.isEmpty()) {
return references.get(0);
}
}
return null;
} finally {
m_dependency.releaseReadLockIfHeld();
}
}
private class RankingResult {
final List<ServiceReference> departures;
final List<ServiceReference> arrivals;
final List<ServiceReference> selected;
private RankingResult(List<ServiceReference> departures, List<ServiceReference> arrivals,
List<ServiceReference> selected) {
this.departures = departures;
this.arrivals = arrivals;
this.selected = selected;
}
}
public class ChangeSet {
public final List<ServiceReference> selected;
public final List<ServiceReference> departures;
public final List<ServiceReference> arrivals;
public final ServiceReference oldFirstReference;
public final ServiceReference newFirstReference;
public final Object service;
public final ServiceReference modified;
public ChangeSet(List<ServiceReference> selectedServices,
List<ServiceReference> departures, List<ServiceReference> arrivals,
ServiceReference oldFirst, ServiceReference newFirst,
Object service, ServiceReference modified) {
this.selected = selectedServices;
this.departures = departures;
this.arrivals = arrivals;
this.oldFirstReference = oldFirst;
this.newFirstReference = newFirst;
this.service = service;
this.modified = modified;
}
}
}