/* * 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; } } }