blob: c6477664fbc50041fe2a6752fbb831c2a6bf5a5c [file] [log] [blame]
Clement Escoffiere050be02013-06-12 11:38:27 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package org.apache.felix.ipojo.dependency.impl;
21
22import org.apache.felix.ipojo.Factory;
23import org.apache.felix.ipojo.dependency.interceptors.ServiceRankingInterceptor;
24import org.apache.felix.ipojo.dependency.interceptors.ServiceTrackingInterceptor;
25import org.apache.felix.ipojo.dependency.interceptors.TransformedServiceReference;
26import org.apache.felix.ipojo.util.DependencyModel;
27import org.apache.felix.ipojo.util.Log;
28import org.apache.felix.ipojo.util.Tracker;
29import org.apache.felix.ipojo.util.TrackerCustomizer;
30import org.osgi.framework.Constants;
31import org.osgi.framework.Filter;
32import org.osgi.framework.ServiceReference;
33
34import java.util.*;
35
36/**
37 * This class is handling the transformations between the base service set and the selected service set.
38 * It handles the matching services and the selected service set.
39 * As this class is tied to the dependency model, it reuses the same locks objects.
40 */
41public class ServiceReferenceManager implements TrackerCustomizer {
42
43 /**
44 * The dependency.
45 */
46 private final DependencyModel m_dependency;
47 /**
48 * The list of all matching service references. This list is a
49 * subset of tracked references. This set is computed according
50 * to the filter and the {@link DependencyModel#match(ServiceReference)} method.
51 */
52 private final Map<ServiceReference, TransformedServiceReference> m_matchingReferences = new
53 LinkedHashMap<ServiceReference, TransformedServiceReference>();
54 /**
55 * The comparator to sort service references.
56 */
57 private Comparator<ServiceReference> m_comparator;
58 /**
59 * The LDAP filter object selecting service references
60 * from the set of providers providing the required specification.
61 */
62 private Filter m_filter;
63 /**
64 * The list of selected service references.
65 */
66 private List<? extends ServiceReference> m_selectedReferences = new ArrayList<ServiceReference>();
67 /**
68 * The service ranking interceptor.
69 */
70 private ServiceRankingInterceptor m_rankingInterceptor;
71 /**
72 * Service interceptor tracker.
73 */
74 private Tracker m_rankingInterceptorTracker;
75 private Tracker m_trackingInterceptorTracker;
76 /**
77 * The set of tracking interceptors.
78 * TODO this set should be ranking according to the OSGi ranking policy.
79 * The filter is always the last interceptor.
80 */
81 private LinkedList<ServiceTrackingInterceptor> m_trackingInterceptors = new
82 LinkedList<ServiceTrackingInterceptor>();
Clement Escoffierfc81ef22013-06-14 14:39:24 +000083 private List<ServiceReference> serviceReferencesList;
Clement Escoffiere050be02013-06-12 11:38:27 +000084
85 /**
86 * Creates the service reference manager.
87 *
88 * @param dep the dependency
89 * @param filter the filter
90 * @param comparator the comparator
91 */
92 public ServiceReferenceManager(DependencyModel dep, Filter filter, Comparator<ServiceReference> comparator) {
93 m_dependency = dep;
94 m_filter = filter;
95 if (m_filter != null) {
96 m_trackingInterceptors.addLast(new FilterBasedServiceTrackingInterceptor(m_filter));
97 }
98 if (comparator != null) {
99 m_comparator = comparator;
100 m_rankingInterceptor = new ComparatorBasedServiceRankingInterceptor(comparator);
101 } else {
102 m_rankingInterceptor = new EmptyBasedServiceRankingInterceptor();
103 }
104 }
105
106 public void open() {
107 m_trackingInterceptorTracker = new Tracker(m_dependency.getBundleContext(),
108 ServiceTrackingInterceptor.class.getName(),
109 new TrackerCustomizer() {
110
111 public boolean addingService(ServiceReference reference) {
112 return DependencyProperties.match(reference, m_dependency);
113 }
114
115 public void addedService(ServiceReference reference) {
116 ServiceTrackingInterceptor interceptor = (ServiceTrackingInterceptor) m_trackingInterceptorTracker
117 .getService(reference);
118
119 if (interceptor != null) {
120 addTrackingInterceptor(interceptor);
121 } else {
122 m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
123 "Cannot retrieve the interceptor object from service reference " + reference
124 .getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
125 (Factory.INSTANCE_NAME_PROPERTY));
126 }
127 }
128
129 public void modifiedService(ServiceReference reference, Object service) {
130 // Not supported yet.
131 // TODO it would be nice to support the modification of the interceptor TARGET property.
132 }
133
134 public void removedService(ServiceReference reference, Object service) {
135 if (service != null && m_trackingInterceptors.contains(service)) {
136 removeTrackingInterceptor((ServiceTrackingInterceptor) service);
137 }
138 }
139 });
140
141 m_trackingInterceptorTracker.open();
142
143 // Initialize the service interceptor tracker.
144 m_rankingInterceptorTracker = new Tracker(m_dependency.getBundleContext(), ServiceRankingInterceptor.class.getName(),
145 new TrackerCustomizer() {
146
147 public boolean addingService(ServiceReference reference) {
148 return DependencyProperties.match(reference, m_dependency);
149 }
150
151 public void addedService(ServiceReference reference) {
152 ServiceRankingInterceptor interceptor = (ServiceRankingInterceptor) m_rankingInterceptorTracker
153 .getService(reference);
154 if (interceptor != null) {
155 setRankingInterceptor(interceptor);
156 } else {
157 m_dependency.getComponentInstance().getFactory().getLogger().log(Log.ERROR,
158 "Cannot retrieve the interceptor object from service reference " + reference
159 .getProperty(Constants.SERVICE_ID) + " - " + reference.getProperty
160 (Factory.INSTANCE_NAME_PROPERTY));
161 }
162 }
163
164 public void modifiedService(ServiceReference reference, Object service) {
165 // Not supported yet.
166 // TODO it would be nice to support the modification of the interceptor TARGET property.
167 }
168
169 public void removedService(ServiceReference reference, Object service) {
170 if (service == m_rankingInterceptor) {
171 m_rankingInterceptor.close(m_dependency);
172 // Do we have another one ?
173 ServiceReference anotherReference = m_rankingInterceptorTracker.getServiceReference();
174 if (anotherReference != null) {
175 ServiceRankingInterceptor interceptor = (ServiceRankingInterceptor) m_rankingInterceptorTracker
176 .getService(anotherReference);
177 if (interceptor != null) setRankingInterceptor(interceptor);
178 } else if (m_comparator != null) {
179 // If we have a comparator, we restore the comparator.
180 setComparator(m_comparator);
181 } else {
182 // If we have neither comparator nor interceptor use an empty interceptor.
183 setRankingInterceptor(new EmptyBasedServiceRankingInterceptor());
184 }
185 }
186 }
187 });
188 m_rankingInterceptorTracker.open();
189 }
190
191 private void addTrackingInterceptor(ServiceTrackingInterceptor interceptor) {
192 // A new interceptor arrives. Insert it at the beginning of the list.
193 ChangeSet changeset;
194 try {
195 m_dependency.acquireWriteLockIfNotHeld();
196 m_trackingInterceptors.addFirst(interceptor);
197 interceptor.open(m_dependency);
198 changeset = computeChangesInMatchingServices();
199 } finally {
200 m_dependency.releaseWriteLockIfHeld();
201 }
202 m_dependency.onChange(changeset);
203 }
204
205 private void removeTrackingInterceptor(ServiceTrackingInterceptor interceptor) {
206 ChangeSet changeset;
207 try {
208 m_dependency.acquireWriteLockIfNotHeld();
209 m_trackingInterceptors.remove(interceptor);
210 interceptor.close(m_dependency);
211 changeset = computeChangesInMatchingServices();
212 } finally {
213 m_dependency.releaseWriteLockIfHeld();
214 }
215 m_dependency.onChange(changeset);
216 }
217
218 private ChangeSet computeChangesInMatchingServices() {
219 if (m_dependency.getTracker() == null || m_dependency.getTracker().getServiceReferences() == null) {
220 // Tracker closed, no problem
221 return new ChangeSet(Collections.<ServiceReference>emptyList(),
222 Collections.<ServiceReference>emptyList(),
223 Collections.<ServiceReference>emptyList(),
224 null,
225 null,
226 null,
227 null);
228 }
229 // The set of interceptor has changed.
230 try {
231 m_dependency.acquireWriteLockIfNotHeld();
232 // The tracker is open, we must recheck all services.
233 ServiceReference oldBest = getFirstService();
234 // Recompute the matching services.
235 m_matchingReferences.clear();
Clement Escoffierfc81ef22013-06-14 14:39:24 +0000236 serviceReferencesList = m_dependency.getTracker().getServiceReferencesList();
237 if (serviceReferencesList != null) {
238 for (ServiceReference reference : serviceReferencesList) {
239 TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
240 ref = accept(ref);
241 if (ref != null) {
242 m_matchingReferences.put(reference, ref);
243 }
Clement Escoffiere050be02013-06-12 11:38:27 +0000244 }
245 }
246
247 // We have the new matching set.
248 List<ServiceReference> beforeRanking = getSelectedServices();
249
250 final List<ServiceReference> allServices = getMatchingServices();
251 List<ServiceReference> references;
252 if (allServices.isEmpty()) {
253 references = Collections.emptyList();
254 } else {
255 references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
256 }
257
258 RankingResult result = computeDifferences(beforeRanking, references);
259 m_selectedReferences = result.selected;
260
261 ServiceReference newFirst = getFirstService();
262 ServiceReference modified = null;
263 if (ServiceReferenceUtils.haveSameServiceId(oldBest, newFirst) && ServiceReferenceUtils
264 .haveSameProperties(oldBest, newFirst)) {
265 modified = newFirst;
266 }
267 return new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest, getFirstService(),
268 null, modified);
269 } finally {
270 m_dependency.releaseWriteLockIfHeld();
271 }
272 }
273
274 public List<ServiceReference> getMatchingServices() {
275 try {
276 m_dependency.acquireReadLockIfNotHeld();
277 return new ArrayList<ServiceReference>(m_matchingReferences.values());
278 } finally {
279 m_dependency.releaseReadLockIfHeld();
280 }
281 }
282
283 public List<ServiceReference> getSelectedServices() {
284 try {
285 m_dependency.acquireReadLockIfNotHeld();
286 return new ArrayList<ServiceReference>(m_selectedReferences);
287 } finally {
288 m_dependency.releaseReadLockIfHeld();
289 }
290 }
291
292 public ServiceReference getFirstService() {
293 try {
294 m_dependency.acquireReadLockIfNotHeld();
295 if (m_selectedReferences.isEmpty()) {
296 return null;
297 }
298 return m_selectedReferences.get(0);
299 } finally {
300 m_dependency.releaseReadLockIfHeld();
301 }
302 }
303
304 public boolean contains(ServiceReference ref) {
305 try {
306 m_dependency.acquireReadLockIfNotHeld();
307 return m_selectedReferences.contains(ref);
308 } finally {
309 m_dependency.releaseReadLockIfHeld();
310 }
311 }
312
313 public void reset() {
314 try {
315 m_dependency.acquireWriteLockIfNotHeld();
316 m_rankingInterceptor.close(m_dependency);
317 for (ServiceTrackingInterceptor interceptor : m_trackingInterceptors) {
318 interceptor.close(m_dependency);
319 }
320 m_trackingInterceptors.clear();
321 m_matchingReferences.clear();
322 m_selectedReferences = new ArrayList<TransformedServiceReference>();
323 } finally {
324 m_dependency.releaseWriteLockIfHeld();
325 }
326
327 }
328
329 public boolean addingService(ServiceReference reference) {
330 // We accept all service references except if we are frozen or broken. In these case, just ignore everything.
331
332 // We are doing two tests, we must get the read lock
333 try {
334 m_dependency.acquireReadLockIfNotHeld();
335 return !(m_dependency.getState() == DependencyModel.BROKEN || m_dependency.isFrozen());
336 } finally {
337 m_dependency.releaseReadLockIfHeld();
338 }
339 }
340
341 /**
342 * Checks if the given reference is accepted.
343 * This method is called when holding the write lock on the dependency.
344 *
345 * @param reference the reference
346 * @param <S>
347 * @return the transformed reference, null if rejected
348 */
349 private <S> TransformedServiceReference<S> accept(TransformedServiceReference<S> reference) {
350 TransformedServiceReference<S> accumulator = reference;
351 for (ServiceTrackingInterceptor interceptor : m_trackingInterceptors) {
352 TransformedServiceReference<S> accepted = interceptor.accept(m_dependency, m_dependency.getBundleContext(), reference);
353 if (accepted != null) {
354 accumulator = accepted;
355 } else {
356 // refused by an interceptor
357 m_dependency.getComponentInstance().getFactory().getLogger().log(Log.INFO,
358 "The service reference " + reference.getProperty(Constants.SERVICE_ID) + " was rejected by " +
359 "interceptor " + interceptor);
360 return null;
361 }
362 }
363
364 return accumulator;
365 }
366
367 public void addedService(ServiceReference reference) {
368 // A service was added to the tracker.
369
370 // First, check is the tracking interceptors are accepting it.
371 // The transformed reference is creates and check outside of the protected region.
372 TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
373
374 boolean match;
375 try {
376 m_dependency.acquireWriteLockIfNotHeld();
377 ref = accept(ref);
378 if (ref != null) {
379 m_matchingReferences.put(reference, ref);
380 }
381 match = ref != null;
382 } finally {
383 m_dependency.releaseWriteLockIfHeld();
384 }
385
386 if (match) {
387 // Callback invoked outside of locks.
388 // The called method is taking the write lock anyway.
389 onNewMatchingService(ref);
390 }
391 }
392
393 private void onNewMatchingService(TransformedServiceReference reference) {
394 ServiceReference oldFirst;
395 RankingResult result;
396 try {
397 m_dependency.acquireWriteLockIfNotHeld();
398 // We store the currently 'first' service reference.
399 oldFirst = getFirstService();
400
401 // We apply our ranking strategy.
402 result = applyRankingOnArrival(reference);
403 // Set the selected services.
404 m_selectedReferences = result.selected;
405 } finally {
406 m_dependency.releaseWriteLockIfHeld();
407 }
408 // Fire the event (outside from the synchronized region)
409 fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
410 getFirstService(), null, null);
411 }
412
413 private void onModificationOfAMatchingService(TransformedServiceReference reference, Object service) {
414 ServiceReference oldFirst;
415 RankingResult result;
416 try {
417 m_dependency.acquireWriteLockIfNotHeld();
418 // We store the currently 'first' service reference.
419 oldFirst = getFirstService();
420
421 // We apply our ranking strategy.
422 result = applyRankingOnModification(reference);
423 // Set the selected services.
424 m_selectedReferences = result.selected;
425 } finally {
426 m_dependency.releaseWriteLockIfHeld();
427 }
428 // Fire the event (outside from the synchronized region)
429 fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
430 getFirstService(), service, reference);
431 }
432
433 private RankingResult applyRankingOnModification(ServiceReference reference) {
434 // TODO we are holding the lock here.
435 List<ServiceReference> beforeRanking = getSelectedServices();
436 List<ServiceReference> references = m_rankingInterceptor.onServiceModified(m_dependency, getMatchingServices(),
437 reference);
438 return computeDifferences(beforeRanking, references);
439 }
440
441 private void fireUpdate(List<ServiceReference> selectedServices, List<ServiceReference> departures,
442 List<ServiceReference> arrivals, ServiceReference oldFirst,
443 ServiceReference firstService, Object service, ServiceReference modified) {
444 ChangeSet set = new ChangeSet(selectedServices, departures, arrivals, oldFirst, firstService, service, modified);
445 m_dependency.onChange(set);
446 }
447
448 private RankingResult applyRankingOnArrival(ServiceReference ref) {
449 // TODO we are holding the lock here.
450 List<ServiceReference> beforeRanking = getSelectedServices();
451 List<ServiceReference> references = m_rankingInterceptor.onServiceArrival(m_dependency, getMatchingServices(),
452 ref);
453 // compute the differences
454 return computeDifferences(beforeRanking, references);
455
456 }
457
458 private RankingResult applyRankingOnDeparture(ServiceReference ref) {
459 // TODO we are holding the lock here.
460 List<ServiceReference> beforeRanking = getSelectedServices();
461 List<ServiceReference> references = m_rankingInterceptor.onServiceDeparture(m_dependency, getMatchingServices(),
462 ref);
463 return computeDifferences(beforeRanking, references);
464 }
465
466 private RankingResult computeDifferences(List<ServiceReference> beforeRanking, List<ServiceReference> ranked) {
467 // compute the differences
468 List<ServiceReference> departures = new ArrayList<ServiceReference>();
469 List<ServiceReference> arrivals = new ArrayList<ServiceReference>();
470 // All references that are no more in the set are considered as leaving services.
471 for (ServiceReference old : beforeRanking) {
472 if (!ServiceReferenceUtils.containsReferenceById(ranked, old)) {
473 departures.add(old);
474 }
475 }
476 // All references that are in `references` but not in `beforeRanking` are new services
477 for (ServiceReference newRef : ranked) {
478 if (!ServiceReferenceUtils.containsReferenceById(beforeRanking, newRef)) {
479 arrivals.add(newRef);
480 }
481 }
482
483 return new RankingResult(departures, arrivals, ranked);
484 }
485
486 public void modifiedService(ServiceReference reference, Object service) {
487 // We are handling a modified event, we have three case to handle
488 // 1) the service was matching and does not match anymore -> it's a departure.
489 // 2) the service was not matching and matches -> it's an arrival
490 // 3) the service was matching and still matches -> it's a modification.
491 try {
492 m_dependency.acquireWriteLockIfNotHeld();
493
494 if (m_matchingReferences.containsKey(reference)) {
495 // do we still accept the reference
496 TransformedServiceReference initial = m_matchingReferences.get(reference);
497 TransformedServiceReference accepted = new TransformedServiceReferenceImpl(reference);
498 accepted = accept(accepted);
499 if (accepted == null) {
500 // case 1
501 m_matchingReferences.remove(reference);
502 onDepartureOfAMatchingService(initial, service);
503 } else {
504 // Do we have a real change
505 if (!ServiceReferenceUtils.haveSameProperties(initial, accepted)) {
506 // case 3
507 m_matchingReferences.put(reference, accepted);
508 onModificationOfAMatchingService(accepted, service);
509 }
510 }
511 } else {
512 // Base does not contain the service, let's try to add it.
513 TransformedServiceReference transformed = new TransformedServiceReferenceImpl(reference);
514 transformed = accept(transformed);
515 if (transformed != null) {
516 // case 2
517 m_matchingReferences.put(reference, transformed);
518 onNewMatchingService(transformed);
519 }
520 }
521 } finally {
522 m_dependency.releaseWriteLockIfHeld();
523 }
524 }
525
526 public void onDepartureOfAMatchingService(TransformedServiceReference reference, Object service) {
527 ServiceReference oldFirst;
528 RankingResult result = null;
529 try {
530 m_dependency.acquireWriteLockIfNotHeld();
531 // We store the currently 'first' service reference.
532 oldFirst = getFirstService();
533 // We apply our ranking strategy.
534 result = applyRankingOnDeparture(reference);
535 // Set the selected services.
536 m_selectedReferences = result.selected;
537 } finally {
538 m_dependency.releaseWriteLockIfHeld();
539 }
540 // Fire the event (outside from the synchronized region)
541 fireUpdate(getSelectedServices(), result.departures, result.arrivals, oldFirst,
542 getFirstService(), service, null);
543 }
544
545 public void removedService(ServiceReference reference, Object service) {
546 // A service is leaving
547 // 1 - the service was in the matching set => real departure
548 // 2 - the service was not in the matching set => nothing do do.
549
550 try {
551 m_dependency.acquireWriteLockIfNotHeld();
552 TransformedServiceReference initial = m_matchingReferences.remove(reference);
553 if (initial != null) {
554 // Case 1
555 onDepartureOfAMatchingService(initial, service);
556 }
557 // else case 2.
558 } finally {
559 m_dependency.releaseWriteLockIfHeld();
560 }
561
562 }
563
564 /**
565 * A new filter is set.
566 * We have to recompute the set of matching services.
567 *
568 * @param filter the new filter
569 * @param tracker the tracker
570 */
571 public ChangeSet setFilter(Filter filter, Tracker tracker) {
572 try {
573 m_dependency.acquireWriteLockIfNotHeld();
574 m_filter = filter;
575
576 if (!m_trackingInterceptors.isEmpty()) {
577 ServiceTrackingInterceptor interceptor = m_trackingInterceptors.getLast();
578 if (interceptor != null && interceptor instanceof FilterBasedServiceTrackingInterceptor) {
579 // Remove it first.
580 m_trackingInterceptors.removeLast();
581 }
582 }
583
584 if (m_filter != null) {
585 // Add the new one.
586 ServiceTrackingInterceptor newInterceptor = new FilterBasedServiceTrackingInterceptor(m_filter);
587 m_trackingInterceptors.addLast(newInterceptor);
588 }
589
590 if (tracker == null) {
591 // Tracker closed, no problem
592 return new ChangeSet(Collections.<ServiceReference>emptyList(),
593 Collections.<ServiceReference>emptyList(),
594 Collections.<ServiceReference>emptyList(),
595 null,
596 null,
597 null,
598 null);
599 } else {
600 // The tracker is open, we must recheck all services.
601 ServiceReference oldBest = getFirstService();
602
603 // Recompute the matching services.
604 m_matchingReferences.clear();
Clement Escoffiercbde4802013-06-14 14:36:53 +0000605 final List<ServiceReference> serviceReferencesList = tracker.getServiceReferencesList();
606 if (serviceReferencesList != null) {
607 for (ServiceReference reference : serviceReferencesList) {
608 TransformedServiceReference ref = new TransformedServiceReferenceImpl(reference);
609 ref = accept(ref);
610 if (ref != null) {
611 m_matchingReferences.put(reference, ref);
612 }
Clement Escoffiere050be02013-06-12 11:38:27 +0000613 }
614 }
615
616 // We have the new matching set.
617
618 List<ServiceReference> beforeRanking = getSelectedServices();
619
620 final List<ServiceReference> allServices = getMatchingServices();
621 List<ServiceReference> references;
622 if (allServices.isEmpty()) {
623 references = Collections.emptyList();
624 } else {
625 references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
626 }
627
628 RankingResult result = computeDifferences(beforeRanking, references);
629 m_selectedReferences = result.selected;
630 return new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest, getFirstService(),
631 null, null);
632 }
633 } finally {
634 m_dependency.releaseWriteLockIfHeld();
635 }
636 }
637
638 public boolean isEmpty() {
639 try {
640 m_dependency.acquireReadLockIfNotHeld();
641 return m_selectedReferences.isEmpty();
642 } finally {
643 m_dependency.releaseReadLockIfHeld();
644 }
645 }
646
647 public Comparator<ServiceReference> getComparator() {
648 try {
649 m_dependency.acquireReadLockIfNotHeld();
650 return m_comparator;
651 } finally {
652 m_dependency.releaseReadLockIfHeld();
653 }
654 }
655
656 public void setComparator(Comparator<ServiceReference> cmp) {
657 try {
658 m_dependency.acquireWriteLockIfNotHeld();
659 m_comparator = cmp;
660 // Be aware that this method will release the lock to call the dependency callback.
661 setRankingInterceptor(new ComparatorBasedServiceRankingInterceptor(cmp));
662 } finally {
663 m_dependency.releaseWriteLockIfHeld();
664 }
665 }
666
667 public Filter getFilter() {
668 try {
669 m_dependency.acquireReadLockIfNotHeld();
670 return m_filter;
671 } finally {
672 m_dependency.releaseReadLockIfHeld();
673 }
674 }
675
676 public void setRankingInterceptor(ServiceRankingInterceptor interceptor) {
677 m_dependency.getComponentInstance().getFactory().getLogger().log(Log.INFO, "Dependency " + m_dependency.getId
678 () + " is getting a new ranking interceptor : " + interceptor);
679 ChangeSet changeSet;
680 try {
681 m_dependency.acquireWriteLockIfNotHeld();
682 ServiceReference oldBest = getFirstService();
683 List<ServiceReference> beforeRanking = getSelectedServices();
684 m_rankingInterceptor = interceptor;
685 m_rankingInterceptor.open(m_dependency);
686
687 final List<ServiceReference> allServices = getMatchingServices();
688 List<ServiceReference> references = Collections.emptyList();
689 if (!allServices.isEmpty()) {
690 references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
691 }
692 RankingResult result = computeDifferences(beforeRanking, references);
693 m_selectedReferences = result.selected;
694 changeSet = new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest,
695 getFirstService(), null, null);
696 } finally {
697 m_dependency.releaseWriteLockIfHeld();
698 }
699 // Calling onChange outside of the lock.
700 m_dependency.onChange(changeSet);
701 }
702
703 public void close() {
704 reset();
705 }
706
707 public void invalidateMatchingServices() {
708 ChangeSet changeset;
709 try {
710 m_dependency.acquireWriteLockIfNotHeld();
711 m_matchingReferences.clear();
712 changeset = computeChangesInMatchingServices();
713 } finally {
714 m_dependency.releaseWriteLockIfHeld();
715 }
716 m_dependency.onChange(changeset);
717 }
718
719 public void invalidateSelectedServices() {
720 ChangeSet changeset;
721 try {
722 m_dependency.acquireWriteLockIfNotHeld();
723 ServiceReference oldBest = getFirstService();
724 List<ServiceReference> beforeRanking = getSelectedServices();
725 m_selectedReferences.clear();
726 final List<ServiceReference> allServices = getMatchingServices();
727 List<ServiceReference> references = Collections.emptyList();
728 if (!allServices.isEmpty()) {
729 references = m_rankingInterceptor.getServiceReferences(m_dependency, allServices);
730 }
731 RankingResult result = computeDifferences(beforeRanking, references);
732 m_selectedReferences = result.selected;
733 changeset = new ChangeSet(getSelectedServices(), result.departures, result.arrivals, oldBest,
734 getFirstService(), null, null);
735 } finally {
736 m_dependency.releaseWriteLockIfHeld();
737 }
738
739 m_dependency.onChange(changeset);
740 }
741
742 private class RankingResult {
743 final List<ServiceReference> departures;
744 final List<ServiceReference> arrivals;
745 final List<ServiceReference> selected;
746
747 private RankingResult(List<ServiceReference> departures, List<ServiceReference> arrivals,
748 List<ServiceReference> selected) {
749 this.departures = departures;
750 this.arrivals = arrivals;
751 this.selected = selected;
752 }
753 }
754
755 public class ChangeSet {
756 public final List<ServiceReference> selected;
757 public final List<ServiceReference> departures;
758 public final List<ServiceReference> arrivals;
759 public final ServiceReference oldFirstReference;
760 public final ServiceReference newFirstReference;
761 public final Object service;
762 public final ServiceReference modified;
763
764 public ChangeSet(List<ServiceReference> selectedServices,
765 List<ServiceReference> departures, List<ServiceReference> arrivals,
766 ServiceReference oldFirst, ServiceReference newFirst,
767 Object service, ServiceReference modified) {
768 this.selected = selectedServices;
769 this.departures = departures;
770 this.arrivals = arrivals;
771 this.oldFirstReference = oldFirst;
772 this.newFirstReference = newFirst;
773 this.service = service;
774 this.modified = modified;
775 }
776 }
777}