001/*
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing,
013 * software distributed under the License is distributed on an
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015 * KIND, either express or implied.  See the License for the
016 * specific language governing permissions and limitations
017 * under the License.
018 */
019package org.apache.felix.ipojo.util;
020
021import org.apache.felix.ipojo.ComponentInstance;
022import org.apache.felix.ipojo.IPOJOServiceFactory;
023import org.apache.felix.ipojo.dependency.impl.ServiceReferenceManager;
024import org.osgi.framework.BundleContext;
025import org.osgi.framework.Filter;
026import org.osgi.framework.InvalidSyntaxException;
027import org.osgi.framework.ServiceReference;
028
029import java.util.*;
030import java.util.concurrent.locks.ReentrantReadWriteLock;
031
032/**
033 * Abstract dependency model.
034 * This class is the parent class of every service dependency. It manages the most
035 * part of dependency management. This class creates an interface between the service
036 * tracker and the concrete dependency.
037 *
038 * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
039 */
040public abstract class DependencyModel {
041
042    /**
043     * Dependency state : BROKEN.
044     * A broken dependency cannot be fulfilled anymore. The dependency becomes
045     * broken when a used service disappears in the static binding policy.
046     */
047    public static final int BROKEN = -1;
048    /**
049     * Dependency state : UNRESOLVED.
050     * A dependency is unresolved if the dependency is not valid and no service
051     * providers are available.
052     */
053    public static final int UNRESOLVED = 0;
054    /**
055     * Dependency state : RESOLVED.
056     * A dependency is resolved if the dependency is optional or at least one
057     * provider is available.
058     */
059    public static final int RESOLVED = 1;
060    /**
061     * Binding policy : Dynamic.
062     * In this policy, services can appears and departs without special treatment.
063     */
064    public static final int DYNAMIC_BINDING_POLICY = 0;
065    /**
066     * Binding policy : Static.
067     * Once a service is used, if this service disappears the dependency becomes
068     * {@link DependencyModel#BROKEN}. The instance needs to be recreated.
069     */
070    public static final int STATIC_BINDING_POLICY = 1;
071    /**
072     * Binding policy : Dynamic-Priority.
073     * In this policy, services can appears and departs. However, once a service
074     * with a highest ranking (according to the used comparator) appears, this
075     * new service is re-injected.
076     */
077    public static final int DYNAMIC_PRIORITY_BINDING_POLICY = 2;
078    /**
079     * The service reference manager.
080     */
081    protected final ServiceReferenceManager m_serviceReferenceManager;
082    /**
083     * The manager handling context sources.
084     */
085    private final ContextSourceManager m_contextSourceManager;
086    /**
087     * Listener object on which invoking the {@link DependencyStateListener#validate(DependencyModel)}
088     * and {@link DependencyStateListener#invalidate(DependencyModel)} methods.
089     */
090    private final DependencyStateListener m_listener;
091    /**
092     * The instance requiring the service.
093     */
094    private final ComponentInstance m_instance;
095    /**
096     * Does the dependency bind several providers ?
097     */
098    private boolean m_aggregate;
099    /**
100     * Is the dependency optional ?
101     */
102    private boolean m_optional;
103    /**
104     * The required specification.
105     * Cannot change once set.
106     */
107    private Class m_specification;
108    /**
109     * Bundle context used by the dependency.
110     * (may be a {@link org.apache.felix.ipojo.ServiceContext}).
111     */
112    private BundleContext m_context;
113    /**
114     * The actual state of the dependency.
115     * {@link DependencyModel#UNRESOLVED} at the beginning.
116     */
117    private int m_state;
118    /**
119     * The Binding policy of the dependency.
120     */
121    private int m_policy = DYNAMIC_BINDING_POLICY;
122    /**
123     * The tracker used by this dependency to track providers.
124     */
125    private Tracker m_tracker;
126    /**
127     * Map {@link ServiceReference} -> Service Object.
128     * This map stores service object, and so is able to handle
129     * iPOJO custom policies.
130     */
131    private Map<ServiceReference, ServiceBindingHolder> m_serviceObjects = new HashMap<ServiceReference, ServiceBindingHolder>();
132    /**
133     * The current list of bound services.
134     */
135    private List<ServiceReference> m_boundServices = new ArrayList<ServiceReference>();
136    /**
137     * The lock ensuring state consistency of the dependency.
138     * This lock can be acquired from all collaborators.
139     */
140    private ReentrantReadWriteLock m_lock = new ReentrantReadWriteLock();
141
142    /**
143     * The listeners of the dependency model.
144     */
145    private final List<DependencyModelListener> m_listeners = new ArrayList<DependencyModelListener>();
146
147    /**
148     * Creates a DependencyModel.
149     * If the dependency has no comparator and follows the
150     * {@link DependencyModel#DYNAMIC_PRIORITY_BINDING_POLICY} policy
151     * the OSGi Service Reference Comparator is used.
152     *
153     * @param specification the required specification
154     * @param aggregate     is the dependency aggregate ?
155     * @param optional      is the dependency optional ?
156     * @param filter        the LDAP filter
157     * @param comparator    the comparator object to sort references
158     * @param policy        the binding policy
159     * @param context       the bundle context (or service context)
160     * @param listener      the dependency lifecycle listener to notify from dependency
161     * @param ci            instance managing the dependency
162     *                      state changes.
163     */
164    public DependencyModel(Class specification, boolean aggregate, boolean optional, Filter filter,
165                           Comparator<ServiceReference> comparator, int policy,
166                           BundleContext context, DependencyStateListener listener, ComponentInstance ci) {
167        m_specification = specification;
168        m_aggregate = aggregate;
169        m_optional = optional;
170
171        m_instance = ci;
172
173        m_policy = policy;
174        // If the dynamic priority policy is chosen, and we have no comparator, fix it to OSGi standard service reference comparator.
175        if (m_policy == DYNAMIC_PRIORITY_BINDING_POLICY && comparator == null) {
176            comparator = new ServiceReferenceRankingComparator();
177        }
178
179        if (context != null) {
180            m_context = context;
181            // If the context is null, it gonna be set later using the setBundleContext method.
182        }
183
184        m_serviceReferenceManager = new ServiceReferenceManager(this, filter, comparator);
185
186        if (filter != null) {
187            try {
188                m_contextSourceManager = new ContextSourceManager(this);
189            } catch (InvalidSyntaxException e) {
190                throw new IllegalArgumentException(e);
191            }
192        } else {
193            m_contextSourceManager = null;
194        }
195        m_state = UNRESOLVED;
196        m_listener = listener;
197    }
198
199    /**
200     * Opens the tracking.
201     * This method computes the dependency state.
202     * <p/>
203     * As the dependency is starting, locking is not required here.
204     *
205     * @see DependencyModel#computeAndSetDependencyState()
206     */
207    public void start() {
208        m_state = UNRESOLVED;
209        m_tracker = new Tracker(m_context, m_specification.getName(), m_serviceReferenceManager);
210        m_serviceReferenceManager.open();
211        m_tracker.open();
212
213        if (m_contextSourceManager != null) {
214            m_contextSourceManager.start();
215        }
216
217        computeAndSetDependencyState();
218    }
219
220    /**
221     * Gets the bundle context used by the dependency.
222     * @return the bundle context
223     */
224    public BundleContext getBundleContext() {
225        // Immutable member, no lock required.
226        return m_context;
227    }
228
229    /**
230     * This callback is called by ranking interceptor to notify the dependency that the selected service set has
231     * changed and must be recomputed.
232     */
233    public void invalidateSelectedServices() {
234        m_serviceReferenceManager.invalidateSelectedServices();
235    }
236
237    public void invalidateMatchingServices() {
238        m_serviceReferenceManager.invalidateMatchingServices();
239    }
240
241    /**
242     * Closes the tracking.
243     * The dependency becomes {@link DependencyModel#UNRESOLVED}
244     * at the end of this method.
245     */
246    public void stop() {
247        // We're stopping, we must take the exclusive lock
248        try {
249            acquireWriteLockIfNotHeld();
250            if (m_tracker != null) {
251                m_tracker.close();
252                m_tracker = null;
253            }
254            m_boundServices.clear();
255            m_serviceReferenceManager.close();
256            ungetAllServices();
257            m_state = UNRESOLVED;
258            if (m_contextSourceManager != null) {
259                m_contextSourceManager.stop();
260            }
261        } finally {
262            releaseWriteLockIfHeld();
263        }
264    }
265
266    /**
267     * Ungets all 'get' service references.
268     * This also clears the service object map.
269     * The method is called while holding the exclusive lock.
270     */
271    private void ungetAllServices() {
272        for (Map.Entry<ServiceReference, ServiceBindingHolder> entry : m_serviceObjects.entrySet()) {
273            ServiceReference ref = entry.getKey();
274            ServiceBindingHolder sbh = entry.getValue();
275            if (m_tracker != null) {
276                m_tracker.ungetService(ref);
277            }
278            if (sbh.factory != null) {
279                sbh.factory.ungetService(m_instance, sbh.service);
280            }
281            m_serviceReferenceManager.unweavingServiceBinding(sbh);
282        }
283        m_serviceObjects.clear();
284    }
285
286    /**
287     * Is the reference set frozen (cannot change anymore)?
288     * This method must be override by concrete dependency to support
289     * the static binding policy. In fact, this method allows optimizing
290     * the static dependencies to become frozen only when needed.
291     * This method returns <code>false</code> by default.
292     * The method must always return <code>false</code> for non-static dependencies.
293     *
294     * @return <code>true</code> if the reference set is frozen.
295     */
296    public boolean isFrozen() {
297        return false;
298    }
299
300    /**
301     * Unfreezes the dependency.
302     * This method must be override by concrete dependency to support
303     * the static binding policy. This method is called after tracking restarting.
304     */
305    public void unfreeze() {
306        // nothing to do
307    }
308
309    /**
310     * Does the service reference match ? This method must be overridden by
311     * concrete dependencies if they need advanced testing on service reference
312     * (that cannot be expressed in the LDAP filter). By default this method
313     * returns <code>true</code>.
314     *
315     * @param ref the tested reference.
316     * @return <code>true</code> if the service reference matches.
317     */
318    public boolean match(ServiceReference ref) {
319        return true;
320    }
321
322    /**
323     * Computes the actual dependency state.
324     * This methods invokes the {@link DependencyStateListener}.
325     * If this method is called without the write lock, it takes it. Anyway, the lock will be released before called
326     * the
327     * callbacks.
328     */
329    private void computeAndSetDependencyState() {
330        try {
331            boolean mustCallValidate = false;
332            boolean mustCallInvalidate = false;
333
334            acquireWriteLockIfNotHeld();
335
336            // The dependency is broken, nothing else can be done
337            if (m_state == BROKEN) {
338                return;
339            }
340
341            if (m_optional || !m_serviceReferenceManager.isEmpty()) {
342                // The dependency is valid
343                if (m_state == UNRESOLVED) {
344                    m_state = RESOLVED;
345                    mustCallValidate = true;
346                }
347            } else {
348                // The dependency is invalid
349                if (m_state == RESOLVED) {
350                    m_state = UNRESOLVED;
351                    mustCallInvalidate = true;
352                }
353            }
354
355            // Invoke callback in a non-synchronized region
356            // First unlock the lock
357            releaseWriteLockIfHeld();
358            // Now we can call the callbacks
359            if (mustCallInvalidate) {
360                invalidate();
361            } else if (mustCallValidate) {
362                validate();
363            }
364        } finally {
365            // If we are still holding the exclusive lock, unlock it.
366            releaseWriteLockIfHeld();
367        }
368
369    }
370
371    /**
372     * Gets the first bound service reference.
373     *
374     * @return <code>null</code> if no more provider is available,
375     *         else returns the first reference from the matching set.
376     */
377    public ServiceReference getServiceReference() {
378        // Read lock required
379        try {
380            acquireReadLockIfNotHeld();
381            if (m_boundServices.isEmpty()) {
382                return null;
383            } else {
384                return m_boundServices.get(0);
385            }
386        } finally {
387            releaseReadLockIfHeld();
388        }
389    }
390
391    /**
392     * Gets bound service references.
393     *
394     * @return the sorted (if a comparator is used) array of matching service
395     *         references, <code>null</code> if no references are available.
396     */
397    public ServiceReference[] getServiceReferences() {
398        // Read lock required
399        try {
400            acquireReadLockIfNotHeld();
401            if (m_boundServices.isEmpty()) {
402                return null;
403            }
404            return m_boundServices.toArray(new ServiceReference[m_boundServices.size()]);
405        } finally {
406            releaseReadLockIfHeld();
407        }
408    }
409
410    /**
411     * Gets the list of currently used service references.
412     * If no service references, returns <code>null</code>
413     *
414     * @return the list of used reference (according to the service tracker).
415     */
416    public List<ServiceReference> getUsedServiceReferences() {
417        // Read lock required
418        try {
419            acquireReadLockIfNotHeld();
420            // The list must confront actual matching services with already get services from the tracker.
421
422            int size = m_boundServices.size();
423            List<ServiceReference> usedByTracker = null;
424            if (m_tracker != null) {
425                usedByTracker = m_tracker.getUsedServiceReferences();
426            }
427            if (size == 0 || usedByTracker == null) {
428                return null;
429            }
430
431            List<ServiceReference> list = new ArrayList<ServiceReference>(1);
432            for (ServiceReference ref : m_boundServices) {
433                if (usedByTracker.contains(ref)) {
434                    list.add(ref); // Add the service in the list.
435                    if (!isAggregate()) { // IF we are not multiple, return the list when the first element is found.
436                        return list;
437                    }
438                }
439            }
440
441            return list;
442        } finally {
443            releaseReadLockIfHeld();
444        }
445    }
446
447    /**
448     * @return the component instance on which this dependency is plugged.
449     */
450    public ComponentInstance getComponentInstance() {
451        // No lock required as m_instance is final
452        return m_instance;
453    }
454
455    /**
456     * Gets the number of actual matching references.
457     *
458     * @return the number of matching references
459     */
460    public int getSize() {
461        try {
462            acquireReadLockIfNotHeld();
463            return m_boundServices.size();
464        } finally {
465            releaseReadLockIfHeld();
466        }
467    }
468
469    /**
470     * Concrete dependency callback.
471     * This method is called when a new service needs to be
472     * re-injected in the underlying concrete dependency.
473     *
474     * @param ref the service reference to inject.
475     */
476    public abstract void onServiceArrival(ServiceReference ref);
477
478    /**
479     * Concrete dependency callback.
480     * This method is called when a used service (already injected) is leaving.
481     *
482     * @param ref the leaving service reference.
483     */
484    public abstract void onServiceDeparture(ServiceReference ref);
485
486    /**
487     * Concrete dependency callback.
488     * This method is called when a used service (already injected) is modified.
489     *
490     * @param ref the modified service reference.
491     */
492    public abstract void onServiceModification(ServiceReference ref);
493
494    /**
495     * Concrete dependency callback.
496     * This method is called when the dependency is reconfigured and when this
497     * reconfiguration implies changes on the matching service set ( and by the
498     * way on the injected service).
499     *
500     * @param departs  the service leaving the matching set.
501     * @param arrivals the service arriving in the matching set.
502     */
503    public abstract void onDependencyReconfiguration(ServiceReference[] departs, ServiceReference[] arrivals);
504
505    /**
506     * Calls the listener callback to notify the new state of the current
507     * dependency.
508     * No lock hold when calling this callback.
509     */
510    private void invalidate() {
511        m_listener.invalidate(this);
512        // Notify dependency invalidation to listeners
513        notifyListeners(DependencyEventType.INVALIDATE, null, null);
514    }
515
516    /**
517     * Calls the listener callback to notify the new state of the current
518     * dependency.
519     * No lock hold when calling this callback.
520     */
521    private void validate() {
522        m_listener.validate(this);
523        // Notify dependency validation to listeners
524        notifyListeners(DependencyEventType.VALIDATE, null, null);
525    }
526
527    /**
528     * Gets the actual state of the dependency.
529     * @return the state of the dependency.
530     */
531    public int getState() {
532        try {
533            acquireReadLockIfNotHeld();
534            return m_state;
535        } finally {
536            releaseReadLockIfHeld();
537        }
538    }
539
540    /**
541     * Gets the tracked specification.
542     *
543     * @return the Class object tracked by the dependency.
544     */
545    public Class getSpecification() {
546        return m_specification;
547    }
548
549    /**
550     * Sets the required specification of this service dependency.
551     * This operation is not supported if the dependency tracking has already begun.
552     * So, we don't have to hold a lock.
553     *
554     * @param specification the required specification.
555     */
556    public void setSpecification(Class specification) {
557        if (m_tracker == null) {
558            m_specification = specification;
559        } else {
560            throw new UnsupportedOperationException("Dynamic specification change is not yet supported");
561        }
562    }
563
564    /**
565     * Acquires the write lock only and only if the write lock is not already held by the current thread.
566     * @return {@literal true} if the lock was acquired within the method, {@literal false} otherwise.
567     */
568    public boolean acquireWriteLockIfNotHeld() {
569        if (! m_lock.isWriteLockedByCurrentThread()) {
570            m_lock.writeLock().lock();
571            return true;
572        }
573        return false;
574    }
575
576    /**
577     * Releases the write lock only and only if the write lock is held by the current thread.
578     * @return {@literal true} if the lock has no more holders, {@literal false} otherwise.
579     */
580    public boolean releaseWriteLockIfHeld() {
581        if (m_lock.isWriteLockedByCurrentThread()) {
582            m_lock.writeLock().unlock();
583        }
584        return m_lock.getWriteHoldCount() == 0;
585    }
586
587    /**
588     * Acquires the read lock only and only if no read lock is already held by the current thread.
589     *
590     * As the introspection methods provided by this method are java 6+, we just take a read lock.
591     * @return {@literal true} if the lock was acquired within the method, {@literal false} otherwise.
592     */
593    public boolean acquireReadLockIfNotHeld() {
594        m_lock.readLock().lock();
595        return true;
596    }
597
598    /**
599     * Releases the read lock only and only if the read lock is held by the current thread.
600     * * As the introspection methods provided by this method are java 6+, we just unlock the read lock.
601     * @return {@literal true} if the lock has no more holders, {@literal false} otherwise.
602     */
603    public boolean releaseReadLockIfHeld() {
604        try {
605            m_lock.readLock().unlock();
606        } catch (IllegalMonitorStateException e) {
607            // Oupsy we were not holding the lock...
608        }
609        return true;
610    }
611
612    /**
613     * Returns the dependency filter (String form).
614     *
615     * @return the String form of the LDAP filter used by this dependency,
616     *         <code>null</code> if not set.
617     */
618    public String getFilter() {
619        Filter filter;
620        try {
621            acquireReadLockIfNotHeld();
622            filter = m_serviceReferenceManager.getFilter();
623        } finally {
624            releaseReadLockIfHeld();
625        }
626
627        if (filter == null) {
628            return null;
629        } else {
630            return filter.toString();
631        }
632    }
633
634    /**
635     * Sets the filter of the dependency. This method recomputes the
636     * matching set and call the onDependencyReconfiguration callback.
637     *
638     * @param filter the new LDAP filter.
639     */
640    public void setFilter(Filter filter) {
641        try {
642            acquireWriteLockIfNotHeld();
643            ServiceReferenceManager.ChangeSet changeSet = m_serviceReferenceManager.setFilter(filter, m_tracker);
644            // We call this method when holding the lock, but the method may decide to release the lock to invoke
645            // callbacks, so we must defensively unlock the lock in the finally block.
646            applyReconfiguration(changeSet);
647        } finally {
648            releaseWriteLockIfHeld();
649        }
650    }
651
652    /**
653     * Applies the given reconfiguration.
654     * This method check if the current thread is holding the write lock, if not, acquire it.
655     * The lock will be released before calling callbacks. As a consequence, the caller has to check if the lock is
656     * still hold when this method returns.
657     * @param changeSet the reconfiguration changes
658     */
659    public void applyReconfiguration(ServiceReferenceManager.ChangeSet changeSet) {
660        List<ServiceReference> arr = new ArrayList<ServiceReference>();
661        List<ServiceReference> dep = new ArrayList<ServiceReference>();
662
663        try  {
664            acquireWriteLockIfNotHeld();
665            if (m_tracker == null) {
666                // Nothing else to do.
667                return;
668            } else {
669                // Update bindings
670                m_boundServices.clear();
671                if (m_aggregate) {
672                    m_boundServices = new ArrayList<ServiceReference>(changeSet.selected);
673                    arr = changeSet.arrivals;
674                    dep = changeSet.departures;
675                } else {
676                    ServiceReference used = null;
677                    if (!m_boundServices.isEmpty()) {
678                        used = m_boundServices.get(0);
679                    }
680
681                    if (!changeSet.selected.isEmpty()) {
682                        final ServiceReference best = changeSet.newFirstReference;
683                        // We didn't a provider
684                        if (used == null) {
685                            // We are not bound with anyone yet, so take the first of the selected set
686                            m_boundServices.add(best);
687                            arr.add(best);
688                        } else {
689                            // A provider was already bound, did we changed ?
690                            if (changeSet.selected.contains(used)) {
691                                // We are still valid - but in dynamic priority, we may have to change
692                                if (getBindingPolicy() == DYNAMIC_PRIORITY_BINDING_POLICY && used != best) {
693                                    m_boundServices.add(best);
694                                    dep.add(used);
695                                    arr.add(best);
696                                } else {
697                                    // We restore the old binding.
698                                    m_boundServices.add(used);
699                                }
700                            } else {
701                                // The used service has left.
702                                m_boundServices.add(best);
703                                dep.add(used);
704                                arr.add(best);
705                            }
706                        }
707                    } else {
708                        // We don't have any service anymore
709                        if (used != null) {
710                            arr.add(used);
711                        }
712                    }
713                }
714            }
715        } finally {
716            releaseWriteLockIfHeld();
717        }
718
719        // This method releases the exclusive lock.
720        computeAndSetDependencyState();
721
722        // As the previous method has released the lock, we can call the callback safely.
723        onDependencyReconfiguration(
724                dep.toArray(new ServiceReference[dep.size()]),
725                arr.toArray(new ServiceReference[arr.size()]));
726
727        // Notify dependency reconfiguration to listeners
728        notifyListeners(DependencyEventType.RECONFIGURED, null, null);
729    }
730
731    public boolean isAggregate() {
732        try {
733            acquireReadLockIfNotHeld();
734            return m_aggregate;
735        } finally {
736            releaseReadLockIfHeld();
737        }
738    }
739
740    /**
741     * Sets the aggregate attribute of the current dependency.
742     * If the tracking is opened, it will call arrival and departure callbacks.
743     *
744     * @param isAggregate the new aggregate attribute value.
745     */
746    public void setAggregate(boolean isAggregate) {
747        // Acquire the write lock here.
748        acquireWriteLockIfNotHeld();
749        List<ServiceReference> arrivals = new ArrayList<ServiceReference>();
750        List<ServiceReference> departures = new ArrayList<ServiceReference>();
751        try {
752            if (m_tracker == null) { // Not started ...
753                m_aggregate = isAggregate;
754            } else {
755                // We become aggregate.
756                if (!m_aggregate && isAggregate) {
757                    m_aggregate = true;
758                    // Call the callback on all non already injected service.
759                    if (m_state == RESOLVED) {
760
761                        for (ServiceReference ref : m_serviceReferenceManager.getSelectedServices()) {
762                            if (!m_boundServices.contains(ref)) {
763                                m_boundServices.add(ref);
764                                arrivals.add(ref);
765                            }
766                        }
767                    }
768                } else if (m_aggregate && !isAggregate) {
769                    m_aggregate = false;
770                    // We become non-aggregate.
771                    if (m_state == RESOLVED) {
772                        List<ServiceReference> list = new ArrayList<ServiceReference>(m_boundServices);
773                        for (int i = 1; i < list.size(); i++) { // The loop begin at 1, as the 0 stays injected.
774                            m_boundServices.remove(list.get(i));
775                            departures.add(list.get(i));
776                        }
777                    }
778                }
779                // Else, do nothing.
780            }
781        } finally {
782            releaseWriteLockIfHeld();
783        }
784
785        // TODO shouldn't we call onDependencyReconfiguration here????
786
787        // Now call callbacks, the lock is not held anymore
788        // Only one of the list is not empty..
789        try {
790            acquireReadLockIfNotHeld();
791            for (ServiceReference ref : arrivals) {
792                onServiceArrival(ref);
793                // Notify service binding to listeners
794                notifyListeners(DependencyEventType.BINDING, ref, m_serviceObjects.get(ref).service);
795            }
796            for (ServiceReference ref : departures) {
797                onServiceDeparture(ref);
798                // Notify service unbinding to listeners
799                notifyListeners(DependencyEventType.UNBINDING, ref, m_serviceObjects.get(ref).service);
800            }
801        } finally {
802            releaseReadLockIfHeld();
803        }
804
805
806    }
807
808    /**
809     * Sets the optionality attribute of the current dependency.
810     *
811     * @param isOptional the new optional attribute value.
812     */
813    public void setOptionality(boolean isOptional) {
814        try {
815            acquireWriteLockIfNotHeld();
816            m_optional = isOptional;
817            computeAndSetDependencyState();
818        } finally {
819            releaseWriteLockIfHeld();
820        }
821    }
822
823    public boolean isOptional() {
824        try {
825            acquireReadLockIfNotHeld();
826            return m_optional;
827        } finally {
828            releaseReadLockIfHeld();
829        }
830    }
831
832    /**
833     * Gets the used binding policy.
834     *
835     * @return the current binding policy.
836     */
837    public int getBindingPolicy() {
838        try {
839            acquireReadLockIfNotHeld();
840            return m_policy;
841        } finally {
842            releaseReadLockIfHeld();
843        }
844
845    }
846
847    /**
848     * Gets the used comparator name.
849     * <code>null</code> if no comparator (i.e. the OSGi one is used).
850     *
851     * @return the comparator class name or <code>null</code> if the dependency doesn't use a comparator.
852     */
853    public String getComparator() {
854        final Comparator<ServiceReference> comparator;
855        try {
856            acquireReadLockIfNotHeld();
857            comparator = m_serviceReferenceManager.getComparator();
858        } finally {
859            releaseReadLockIfHeld();
860        }
861
862        if (comparator != null) {
863            return comparator.getClass().getName();
864        } else {
865            return null;
866        }
867    }
868
869    public void setComparator(Comparator<ServiceReference> cmp) {
870        try {
871            acquireWriteLockIfNotHeld();
872            m_serviceReferenceManager.setComparator(cmp);
873        } finally {
874            releaseWriteLockIfHeld();
875        }
876    }
877
878    /**
879     * Sets the bundle context used by this dependency.
880     * This operation is not supported if the tracker is already opened, and as a consequence does not require locking.
881     *
882     * @param context the bundle context or service context to use
883     */
884    public void setBundleContext(BundleContext context) {
885        if (m_tracker == null) { // Not started ...
886            m_context = context;
887        } else {
888            throw new UnsupportedOperationException("Dynamic bundle (i.e. service) context change is not supported");
889        }
890    }
891
892    /**
893     * Gets a service object for the given reference.
894     * The service object is stored to handle custom policies.
895     *
896     * @param ref the wanted service reference
897     * @return the service object attached to the given reference
898     */
899    public Object getService(ServiceReference ref) {
900        return getService(ref, true);
901    }
902
903    /**
904     * Gets a service object for the given reference.
905     *
906     * @param ref   the wanted service reference
907     * @param store enables / disables the storing of the reference.
908     * @return the service object attached to the given reference
909     */
910    public Object getService(ServiceReference ref, boolean store) {
911        if (m_tracker == null) {
912            // The tracker is already closed, we can't access the service anymore.
913            return null;
914        }
915
916        // If we already have the service object, just return it.
917        if (m_serviceObjects.containsKey(ref)) {
918            return m_serviceObjects.get(ref).service;
919        }
920
921        ServiceBindingHolder holder = null;
922        Object svc = m_tracker.getService(ref);
923        IPOJOServiceFactory factory = null;
924
925        if (svc instanceof IPOJOServiceFactory) {
926            factory = (IPOJOServiceFactory) svc;
927            svc = factory.getService(m_instance);
928        }
929        holder = new ServiceBindingHolder(ref, factory, svc);
930
931        svc = m_serviceReferenceManager.weavingServiceBinding(holder);
932
933        if (store) {
934            try {
935                acquireWriteLockIfNotHeld();
936                // The service object may have been modified by the interceptor, update the holder
937                if (svc != holder.service) {
938                    m_serviceObjects.put(ref, new ServiceBindingHolder(ref, factory, svc));
939                } else {
940                    m_serviceObjects.put(ref, holder);
941                }
942            } finally {
943                releaseWriteLockIfHeld();
944            }
945        }
946        return svc;
947    }
948
949    /**
950     * Ungets a used service reference.
951     *
952     * @param ref the reference to unget.
953     */
954    public void ungetService(ServiceReference ref) {
955        m_tracker.ungetService(ref);
956        ServiceBindingHolder sbh;
957        try {
958            acquireWriteLockIfNotHeld();
959            sbh = m_serviceObjects.remove(ref);
960        } finally {
961            releaseWriteLockIfHeld();
962        }
963
964        // Call the callback outside the lock.
965        if (sbh != null && sbh.factory != null) {
966            sbh.factory.ungetService(m_instance, sbh.service);
967        }
968
969        m_serviceReferenceManager.unweavingServiceBinding(sbh);
970
971    }
972
973    public ContextSourceManager getContextSourceManager() {
974        // Final member, no lock required.
975        return m_contextSourceManager;
976    }
977
978    /**
979     * Gets the dependency id.
980     *
981     * @return the dependency id. Specification name by default.
982     */
983    public String getId() {
984        // Immutable, no lock required.
985        return getSpecification().getName();
986    }
987
988    /**
989     * Callbacks call by the ServiceReferenceManager when the selected service set has changed.
990     * @param set the change set.
991     */
992    public void onChange(ServiceReferenceManager.ChangeSet set) {
993        try {
994            acquireWriteLockIfNotHeld();
995            // First handle the static case with a frozen state
996            if (isFrozen() && getState() != BROKEN) {
997                for (ServiceReference ref : set.departures) {
998                    // Check if any of the service that have left was in used.
999                    if (m_boundServices.contains(ref)) {
1000                        // Static dependency broken.
1001                        m_state = BROKEN;
1002
1003                        // We are going to call callbacks, releasing the lock.
1004                        ServiceBindingHolder sbh = m_serviceObjects.get(ref);
1005                        releaseWriteLockIfHeld();
1006
1007                        // Notify listeners
1008                        notifyListeners(DependencyEventType.UNBINDING, ref, sbh.service);
1009                        notifyListeners(DependencyEventType.DEPARTURE, ref, null);
1010
1011                        invalidate();  // This will invalidate the instance.
1012                        m_instance.stop(); // Stop the instance
1013                        unfreeze();
1014                        m_instance.start();
1015                        return;
1016                    }
1017                }
1018            }
1019
1020            List<ServiceReference> arrivals = new ArrayList<ServiceReference>();
1021            List<ServiceReference> departures = new ArrayList<ServiceReference>();
1022
1023            // Manage departures
1024            // We unbind all bound services that are leaving.
1025            for (ServiceReference ref : set.departures) {
1026                if (m_boundServices.contains(ref)) {
1027                    // We were using the reference
1028                    m_boundServices.remove(ref);
1029                    departures.add(ref);
1030                }
1031            }
1032
1033            // Manage arrivals
1034            // For aggregate dependencies, call onServiceArrival for all services not-yet-bound and in the order of the
1035            // selection.
1036            if (m_aggregate) {
1037                // If the dependency is not already in used,
1038                // the bindings must be sorted as in set.selected
1039                if (m_serviceObjects.isEmpty() || DYNAMIC_PRIORITY_BINDING_POLICY == getBindingPolicy()) {
1040                    m_boundServices.clear();
1041                    m_boundServices.addAll(set.selected);
1042                }
1043
1044                // Now we notify from the arrival.
1045                // If we didn't add the reference yet, we add it.
1046                for (ServiceReference ref : set.arrivals) {
1047                    // We bind all not-already bound services, so it's an arrival
1048                    if (!m_boundServices.contains(ref)) {
1049                        m_boundServices.add(ref);
1050                    }
1051                    arrivals.add(ref);
1052                }
1053            } else {
1054                if (!set.selected.isEmpty()) {
1055                    final ServiceReference best = set.selected.get(0);
1056                    // We have a provider
1057                    if (m_boundServices.isEmpty()) {
1058                        // We are not bound with anyone yet, so take the first of the selected set
1059                        m_boundServices.add(best);
1060                        arrivals.add(best);
1061                    } else {
1062                        final ServiceReference current = m_boundServices.get(0);
1063                        // We are already bound, to the rebinding decision depends on the binding strategy
1064                        if (getBindingPolicy() == DYNAMIC_PRIORITY_BINDING_POLICY) {
1065                            // Rebinding in the DP binding policy if the bound one if not the new best one.
1066                            if (current != best) {
1067                                m_boundServices.remove(current);
1068                                m_boundServices.add(best);
1069                                departures.add(current);
1070                                arrivals.add(best);
1071                            }
1072                        } else {
1073                            // In static and dynamic binding policy, if the service is not yet used and the new best is not
1074                            // the currently selected one, we should switch.
1075                            boolean isUsed = m_serviceObjects.containsKey(current);
1076                            if (!isUsed && current != best) {
1077                                m_boundServices.remove(current);
1078                                m_boundServices.add(best);
1079                                departures.add(current);
1080                                arrivals.add(best);
1081                            }
1082                        }
1083                    }
1084                }
1085            }
1086
1087            // Before leaving the protected region, copy used services.
1088            Map<ServiceReference, ServiceBindingHolder> services = new HashMap<ServiceReference, ServiceBindingHolder>(m_serviceObjects);
1089
1090            // Leaving the locked region to invoke callbacks
1091            releaseWriteLockIfHeld();
1092
1093            for (ServiceReference ref : departures) {
1094                onServiceDeparture(ref);
1095                // Notify service unbinding to listeners
1096                final ServiceBindingHolder sbh = services.get(ref);
1097                if (sbh != null) {
1098                    notifyListeners(DependencyEventType.UNBINDING, ref, sbh.service);
1099                } else {
1100                    notifyListeners(DependencyEventType.UNBINDING, ref, null);
1101                }
1102                // Unget the service reference.
1103                ungetService(ref);
1104            }
1105            for (ServiceReference ref : arrivals) {
1106                onServiceArrival(ref);
1107                // Notify service binding to listeners
1108                final ServiceBindingHolder sbh = services.get(ref);
1109                if (sbh != null) {
1110                    notifyListeners(DependencyEventType.BINDING, ref, sbh.service);
1111                } else {
1112                    notifyListeners(DependencyEventType.BINDING, ref, null);
1113                }
1114            }
1115            // Do we have a modified service ?
1116            if (set.modified != null && m_boundServices.contains(set.modified)) {
1117                onServiceModification(set.modified);
1118                // TODO call boundServiceModified on listeners???
1119            }
1120
1121            // Did our state changed ?
1122            // this method will manage its own synchronization.
1123            computeAndSetDependencyState();
1124        } finally {
1125            releaseWriteLockIfHeld();
1126        }
1127    }
1128
1129    public ServiceReferenceManager getServiceReferenceManager() {
1130        return m_serviceReferenceManager;
1131    }
1132
1133    public Tracker getTracker() {
1134        return m_tracker;
1135    }
1136
1137    public enum DependencyEventType {
1138        VALIDATE,
1139        INVALIDATE,
1140        ARRIVAL,
1141        MODIFIED,
1142        DEPARTURE,
1143        BINDING,
1144        UNBINDING,
1145        RECONFIGURED
1146    }
1147
1148    /**
1149     * Add the given listener to the dependency model's list of listeners.
1150     *
1151     * @param listener the {@code DependencyModelListener} object to be added
1152     * @throws NullPointerException if {@code listener} is {@code null}
1153     */
1154    public void addListener(DependencyModelListener listener) {
1155        if (listener == null) {
1156            throw new NullPointerException("null listener");
1157        }
1158        synchronized (m_listeners) {
1159            m_listeners.add(listener);
1160        }
1161    }
1162
1163    /**
1164     * Remove the given listener from the dependency model's list of listeners.
1165     *
1166     * @param listener the {@code DependencyModelListener} object to be removed
1167     * @throws NullPointerException if {@code listener} is {@code null}
1168     * @throws NoSuchElementException if {@code listener} wasn't present in the dependency model's list of listeners
1169     */
1170    public void removeListener(DependencyModelListener listener) {
1171        if (listener == null) {
1172            throw new NullPointerException("null listener");
1173        }
1174        synchronized (m_listeners) {
1175            // We definitely cannot rely on listener's equals method...
1176            // ...so we need to manually search for the listener, using ==.
1177            int i = -1;
1178            for(int j = m_listeners.size() -1; j>=0 ; j--) {
1179                if (m_listeners.get(j) == listener) {
1180                    // Found!
1181                    i = j;
1182                    break;
1183                }
1184            }
1185            if (i != -1) {
1186                m_listeners.remove(i);
1187            } else {
1188                throw new NoSuchElementException("no such listener");
1189            }
1190        }
1191    }
1192
1193    /**
1194     * Notify all listeners that a change has occurred in this dependency model.
1195     *
1196     * @param type the type of event
1197     * @param service the reference of the concerned service (may be null)
1198     * @param object the concerned service object (may be null)
1199     */
1200    public void notifyListeners(DependencyEventType type, ServiceReference<?> service, Object object) {
1201        // Get a snapshot of the listeners
1202        List<DependencyModelListener> tmp;
1203        synchronized (m_listeners) {
1204            tmp = new ArrayList<DependencyModelListener>(m_listeners);
1205        }
1206        // Do notify, outside the m_listeners lock
1207        for (DependencyModelListener l : tmp) {
1208            try {
1209                switch (type) {
1210                    case VALIDATE:
1211                        l.validate(this);
1212                        break;
1213                    case INVALIDATE:
1214                        l.invalidate(this);
1215                        break;
1216                    case ARRIVAL:
1217                        l.matchingServiceArrived(this, service);
1218                        break;
1219                    case MODIFIED:
1220                        l.matchingServiceModified(this, service);
1221                        break;
1222                    case DEPARTURE:
1223                        l.matchingServiceDeparted(this, service);
1224                        break;
1225                    case BINDING:
1226                        l.serviceBound(this, service, object);
1227                        break;
1228                    case UNBINDING:
1229                        l.serviceUnbound(this, service, object);
1230                        break;
1231                    case RECONFIGURED:
1232                        l.reconfigured(this);
1233                        break;
1234                }
1235            } catch (Throwable e) {
1236                // Put a warning on the logger, and continue
1237                getComponentInstance().getFactory().getLogger().log(Log.WARNING,
1238                        String.format(
1239                                "[%s] A DependencyModelListener has failed: %s",
1240                                getComponentInstance().getInstanceName(),
1241                                e.getMessage())
1242                        , e);
1243            }
1244        }
1245    }
1246
1247    /**
1248     * Removes all the listeners from this dependency before it gets disposed.
1249     */
1250    public void cleanup() {
1251        synchronized (m_listeners) {
1252            m_listeners.clear();
1253        }
1254    }
1255
1256    /**
1257     * Service binding structure.
1258     */
1259    public class ServiceBindingHolder {
1260
1261        public final Object service;
1262        public final IPOJOServiceFactory factory;
1263        public final ServiceReference reference;
1264
1265        private ServiceBindingHolder(ServiceReference reference, IPOJOServiceFactory factory, Object service) {
1266            this.service = service;
1267            this.factory = factory;
1268            this.reference = reference;
1269        }
1270
1271        private ServiceBindingHolder(ServiceReference reference, Object service) {
1272            this.service = service;
1273            this.factory = null;
1274            this.reference = reference;
1275        }
1276    }
1277}