/**
* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or modify it under
* the terms of the GNU Lesser General Public License as published by the Free
* Software Foundation; either version 2.1 of the License, or (at your option)
* any later version.
*
* This library is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*/
package com.liferay.osgi.service.tracker.collections.internal.map;
import com.liferay.osgi.service.tracker.collections.internal.ServiceTrackerUtil;
import com.liferay.osgi.service.tracker.collections.map.KeyedServiceReferenceServiceTuple;
import com.liferay.osgi.service.tracker.collections.map.ServiceReferenceMapper;
import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerBucket;
import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerBucketFactory;
import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMap;
import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMapListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.apache.felix.utils.log.Logger;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
/**
* @author Carlos Sierra Andrés
*/
public class ServiceTrackerMapImpl<K, SR, TS, R>
implements ServiceTrackerMap<K, R> {
public ServiceTrackerMapImpl(
BundleContext bundleContext, Class<SR> clazz, String filterString,
ServiceReferenceMapper<K, ? super SR> serviceReferenceMapper,
ServiceTrackerCustomizer<SR, TS> serviceTrackerCustomizer,
ServiceTrackerBucketFactory<SR, TS, R> serviceTrackerMapBucketFactory,
ServiceTrackerMapListener<K, TS, R> serviceTrackerMapListener) {
_serviceReferenceMapper = serviceReferenceMapper;
_serviceTrackerCustomizer = serviceTrackerCustomizer;
_serviceTrackerMapBucketFactory = serviceTrackerMapBucketFactory;
_serviceTrackerMapListener = serviceTrackerMapListener;
_serviceTracker = ServiceTrackerUtil.createServiceTracker(
bundleContext, clazz, filterString,
new ServiceReferenceServiceTrackerCustomizer());
_logger = new Logger(bundleContext);
}
@Override
public void close() {
_serviceTracker.close();
}
@Override
public boolean containsKey(K key) {
return _serviceTrackerBuckets.containsKey(key);
}
@Override
public R getService(K key) {
ServiceTrackerBucket<SR, TS, R> serviceTrackerBucket =
_serviceTrackerBuckets.get(key);
if (serviceTrackerBucket == null) {
return null;
}
return serviceTrackerBucket.getContent();
}
@Override
public Set<K> keySet() {
return Collections.unmodifiableSet(_serviceTrackerBuckets.keySet());
}
@Override
public void open() {
_serviceTracker.open();
}
@Override
public Collection<R> values() {
return Collections.unmodifiableCollection(getServices());
}
protected Collection<R> getServices() {
Collection<R> services = new ArrayList<>();
for (ServiceTrackerBucket<SR, TS, R> serviceTrackerBucket :
_serviceTrackerBuckets.values()) {
services.add(serviceTrackerBucket.getContent());
}
return services;
}
private void _removeKeys(
KeyedServiceReferenceServiceTuple<SR, TS, K>
keyedServiceReferenceServiceTuple) {
List<K> emittedKeys =
keyedServiceReferenceServiceTuple.getEmittedKeys();
for (K emittedKey : emittedKeys) {
ServiceTrackerBucket<SR, TS, R> serviceTrackerBucket =
_serviceTrackerBuckets.get(emittedKey);
if (serviceTrackerBucket == null) {
continue;
}
serviceTrackerBucket.remove(keyedServiceReferenceServiceTuple);
if (serviceTrackerBucket.isDisposable()) {
_serviceTrackerBuckets.remove(emittedKey);
}
if (_serviceTrackerMapListener != null) {
try {
_serviceTrackerMapListener.keyRemoved(
ServiceTrackerMapImpl.this, emittedKey,
keyedServiceReferenceServiceTuple.getService(),
serviceTrackerBucket.getContent());
}
catch (Throwable t) {
_logger.log(
Logger.LOG_ERROR,
"Invocation to listener threw exception", t);
}
}
}
emittedKeys.clear();
}
private void _storeKey(
K key,
KeyedServiceReferenceServiceTuple<SR, TS, K>
keyedServiceReferenceServiceTuple) {
ServiceTrackerBucket<SR, TS, R> serviceTrackerBucket =
_serviceTrackerBuckets.get(key);
if (serviceTrackerBucket == null) {
ServiceTrackerBucket<SR, TS, R> newServiceTrackerBucket =
_serviceTrackerMapBucketFactory.create();
serviceTrackerBucket = _serviceTrackerBuckets.putIfAbsent(
key, newServiceTrackerBucket);
if (serviceTrackerBucket == null) {
serviceTrackerBucket = newServiceTrackerBucket;
}
}
serviceTrackerBucket.store(keyedServiceReferenceServiceTuple);
keyedServiceReferenceServiceTuple.addEmittedKey(key);
}
private final Logger _logger;
private final ServiceReferenceMapper<K, ? super SR> _serviceReferenceMapper;
private final ServiceTracker
<SR, KeyedServiceReferenceServiceTuple<SR, TS, K>> _serviceTracker;
private final ConcurrentMap<K, ServiceTrackerBucket<SR, TS, R>>
_serviceTrackerBuckets = new ConcurrentHashMap<>();
private final ServiceTrackerCustomizer<SR, TS> _serviceTrackerCustomizer;
private final ServiceTrackerBucketFactory<SR, TS, R>
_serviceTrackerMapBucketFactory;
private final ServiceTrackerMapListener<K, TS, R>
_serviceTrackerMapListener;
private class DefaultEmitter implements ServiceReferenceMapper.Emitter<K> {
public DefaultEmitter(ServiceReference<SR> serviceReference) {
_serviceReference = serviceReference;
}
@Override
public void emit(K key) {
if (_keyedServiceReferenceServiceTuple == null) {
if (!_invokedServiceTrackerCustomizer) {
TS service = _serviceTrackerCustomizer.addingService(
_serviceReference);
_invokedServiceTrackerCustomizer = true;
if (service == null) {
return;
}
_keyedServiceReferenceServiceTuple =
new KeyedServiceReferenceServiceTuple<>(
_serviceReference, service);
}
else {
return;
}
}
_storeKey(key, _keyedServiceReferenceServiceTuple);
if (_serviceTrackerMapListener != null) {
try {
ServiceTrackerBucket<SR, TS, R> serviceTrackerBucket =
_serviceTrackerBuckets.get(key);
_serviceTrackerMapListener.keyEmitted(
ServiceTrackerMapImpl.this, key,
_keyedServiceReferenceServiceTuple.getService(),
serviceTrackerBucket.getContent());
}
catch (Throwable t) {
_logger.log(
Logger.LOG_ERROR,
"Invocation to listener threw exception", t);
}
}
}
public KeyedServiceReferenceServiceTuple<SR, TS, K>
getServiceReferenceServiceTuple() {
return _keyedServiceReferenceServiceTuple;
}
private boolean _invokedServiceTrackerCustomizer;
private KeyedServiceReferenceServiceTuple<SR, TS, K>
_keyedServiceReferenceServiceTuple;
private final ServiceReference<SR> _serviceReference;
}
private class ServiceReferenceServiceTrackerCustomizer
implements
ServiceTrackerCustomizer
<SR, KeyedServiceReferenceServiceTuple<SR, TS, K>> {
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public KeyedServiceReferenceServiceTuple<SR, TS, K> addingService(
final ServiceReference<SR> serviceReference) {
DefaultEmitter defaultEmitter = new DefaultEmitter(
serviceReference);
_serviceReferenceMapper.map(
(ServiceReference)serviceReference, defaultEmitter);
return defaultEmitter.getServiceReferenceServiceTuple();
}
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public void modifiedService(
ServiceReference<SR> serviceReference,
final KeyedServiceReferenceServiceTuple<SR, TS, K>
keyedServiceReferenceServiceTuple) {
_removeKeys(keyedServiceReferenceServiceTuple);
_serviceTrackerCustomizer.modifiedService(
serviceReference,
keyedServiceReferenceServiceTuple.getService());
_serviceReferenceMapper.map(
(ServiceReference)serviceReference,
new ServiceReferenceMapper.Emitter<K>() {
@Override
public void emit(K key) {
_storeKey(key, keyedServiceReferenceServiceTuple);
}
});
}
@Override
public void removedService(
final ServiceReference<SR> serviceReference,
final KeyedServiceReferenceServiceTuple<SR, TS, K>
keyedServiceReferenceServiceTuple) {
_removeKeys(keyedServiceReferenceServiceTuple);
_serviceTrackerCustomizer.removedService(
serviceReference,
keyedServiceReferenceServiceTuple.getService());
}
}
}