Package org.apache.felix.ipojo.dependency.impl

Source Code of org.apache.felix.ipojo.dependency.impl.ServiceReferenceManager$ChangeSet

/*
* 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() {
     
      /*
       * WARNING IMPORTANT Added a try/catch in case the bundle is not allowed to get interceptors.
       * This was throwing an uncaught exception at instance starting, and the instance remained invalid
       *
       * TODO Think what to do regarding visibility of interceptors
       *
       * G. Vega
       */
     
      try {
          // 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();
      }
      catch (SecurityException notEnoughPrivileges) {
       }
     
     
      try {
          // 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();
      }
      catch (SecurityException notEnoughPrivileges) {
       }

      try {
          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();
      }
      catch (SecurityException notEnoughPrivileges) {
       }

    }

    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;
        }
    }
}
TOP

Related Classes of org.apache.felix.ipojo.dependency.impl.ServiceReferenceManager$ChangeSet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.