blob: a5339f876e55184984274bc85ad88b69b3975f6a [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.dosgi.topologymanager.util;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
/**
* A {@link ServiceTracker} extension that simplifies its usage.
* <p>
* It enhances {@code ServiceTracker} by adding:
* <ul>
* <li>Multiple event listeners for service add/remove events
* <li>Simpler event callbacks that do not need to deal with getting/ungetting
* services, calling super methods or returning service objects
* <li>Generics support, which means the callback and {@code getList()} methods
* are type-safe and require no casting
* <li>A {@link #getAllServices()} method which returns all currently tracked services;
* Unlike {@link ServiceTracker#getServices()}, if it is called from within a service
* {@link SimpleServiceTrackerListener#added added} event handler, the returned list
* will include the newly added service (this is the source of several bugs when using
* the original {@code getServices()})
* </ul>
*
* @param <T> the service interface type
*/
public class SimpleServiceTracker<T> extends ServiceTracker<T, T> {
// we must use a map with references as keys, so as not to invoke equals remotely on service objects
private ConcurrentMap<ServiceReference<T>, T> services;
private List<SimpleServiceTrackerListener<T>> listeners;
/**
* Create a <code>SimpleServiceTracker</code> on the specified class' name.
* <p>
* Services registered under the specified class' name will be tracked by
* this <code>SimpleServiceTracker</code>.
*
* @param context the {@code BundleContext} against which the tracking is done
* @param clazz the class of the services to be tracked
*/
public SimpleServiceTracker(BundleContext context, Class<T> clazz) {
super(context, clazz.getName(), null);
this.listeners = new CopyOnWriteArrayList<SimpleServiceTrackerListener<T>>();
this.services = new ConcurrentHashMap<ServiceReference<T>, T>();
}
/**
* Adds a listener to be notified of services added or removed.
*
* @param listener the listener to add
*/
public void addListener(SimpleServiceTrackerListener<T> listener) {
listeners.add(listener);
}
@Override
public T addingService(ServiceReference<T> reference) {
T service = (T) super.addingService(reference);
services.put(reference, service);
for (SimpleServiceTrackerListener<T> listener : listeners) {
listener.added(service);
}
return service;
}
@Override
public void removedService(ServiceReference<T> reference, T service) {
services.remove(reference, service);
for (SimpleServiceTrackerListener<T> listener : listeners) {
listener.removed(service);
}
super.removedService(reference, service);
}
@Override
public void close() {
super.close();
services.clear();
}
/**
* Returns all currently tracked services.
* <p>
* Unlike {@link ServiceTracker#getServices()}, if it is called from within a service
* {@link SimpleServiceTrackerListener#added added} event handler, the returned list
* will include the newly added service.
*
* @return all currently tracked services
*/
public List<T> getAllServices() {
return new ArrayList<T>(services.values());
}
}