/**
* 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.registry.collections.internal;
import com.liferay.registry.Filter;
import com.liferay.registry.Registry;
import com.liferay.registry.RegistryUtil;
import com.liferay.registry.ServiceReference;
import com.liferay.registry.ServiceRegistration;
import com.liferay.registry.ServiceTracker;
import com.liferay.registry.ServiceTrackerCustomizer;
import com.liferay.registry.collections.ServiceRegistrationMap;
import com.liferay.registry.collections.ServiceRegistrationMapImpl;
import com.liferay.registry.collections.ServiceTrackerList;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @author Raymond Augé
*/
public class ServiceTrackerCollectionImpl<S> implements ServiceTrackerList<S> {
public ServiceTrackerCollectionImpl(
Class<S> clazz, Filter filter,
ServiceTrackerCustomizer<S, S> serviceTrackerCustomizer,
Map<String, Object> properties) {
_clazz = clazz;
_filter = filter;
_properties = Collections.unmodifiableMap(properties);
Registry registry = RegistryUtil.getRegistry();
if (filter != null) {
filter = _getFilter(filter, _clazz);
_serviceTracker = registry.trackServices(
filter,
new DefaultServiceTrackerCustomizer(serviceTrackerCustomizer));
}
else {
_serviceTracker = registry.trackServices(
clazz,
new DefaultServiceTrackerCustomizer(serviceTrackerCustomizer));
}
}
@Override
public void add(int index, S service) {
throw new UnsupportedOperationException();
}
@Override
public boolean add(S service) {
if (service == null) {
throw new IllegalArgumentException("Service is null");
}
if ((_filter != null) && !_filter.matches(_properties)) {
throw new IllegalStateException();
}
Map<String, Object> properties = new HashMap<>(_properties);
Registry registry = RegistryUtil.getRegistry();
ServiceRegistration<S> serviceRegistration = registry.registerService(
_clazz, service, properties);
_serviceRegistrations.put(service, serviceRegistration);
return true;
}
@Override
public boolean add(S service, Map<String, Object> properties) {
if (service == null) {
throw new IllegalArgumentException("Service is null");
}
properties = new HashMap<>(properties);
properties.putAll(_properties);
if ((_filter != null) && !_filter.matches(properties)) {
throw new IllegalArgumentException(
"Filter does not match properties " + properties);
}
Registry registry = RegistryUtil.getRegistry();
ServiceRegistration<S> serviceRegistration = registry.registerService(
_clazz, service, properties);
_serviceRegistrations.put(service, serviceRegistration);
return true;
}
@Override
public boolean addAll(Collection<? extends S> services) {
boolean modified = false;
for (S service : services) {
if (add(service)) {
modified = true;
}
}
return modified;
}
@Override
public boolean addAll(int index, Collection<? extends S> services) {
throw new UnsupportedOperationException();
}
@Override
public void clear() {
Set<Map.Entry<S, ServiceRegistration<S>>> set =
_serviceRegistrations.entrySet();
Iterator<Entry<S, ServiceRegistration<S>>> iterator = set.iterator();
while (iterator.hasNext()) {
Entry<S, ServiceRegistration<S>> entry = iterator.next();
ServiceRegistration<S> serviceRegistration = entry.getValue();
serviceRegistration.unregister();
iterator.remove();
}
}
@Override
public void close() {
clear();
_serviceTracker.close();
}
@Override
public boolean contains(Object service) {
return _services.contains(service);
}
@Override
public boolean containsAll(Collection<?> services) {
throw new UnsupportedOperationException();
}
@Override
public S get(int index) {
EntryWrapper entryWrapper = _services.get(index);
return entryWrapper._service;
}
@Override
public int indexOf(Object service) {
return _services.indexOf(service);
}
@Override
public boolean isEmpty() {
return _services.isEmpty();
}
@Override
public Iterator<S> iterator() {
return new ServiceTrackerIterator(_services.listIterator());
}
@Override
public int lastIndexOf(Object service) {
return _services.lastIndexOf(service);
}
@Override
public ListIterator<S> listIterator() {
return new ServiceTrackerIterator(_services.listIterator());
}
@Override
public ListIterator<S> listIterator(int index) {
return new ServiceTrackerIterator(_services.listIterator(index));
}
@Override
public void open() {
_serviceTracker.open();
}
@Override
public S remove(int index) {
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object service) {
ServiceRegistration<S> serviceRegistration =
_serviceRegistrations.remove(service);
if (serviceRegistration == null) {
return false;
}
serviceRegistration.unregister();
return true;
}
@Override
public boolean removeAll(Collection<?> services) {
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> services) {
throw new UnsupportedOperationException();
}
@Override
public S set(int index, S service) {
throw new UnsupportedOperationException();
}
@Override
public int size() {
return _services.size();
}
@Override
public List<S> subList(int fromIndex, int toIndex) {
List<S> list = new ArrayList<>();
List<EntryWrapper> subList = _services.subList(fromIndex, toIndex);
for (EntryWrapper entryWrapper : subList) {
list.add(entryWrapper._service);
}
return list;
}
@Override
public Object[] toArray() {
return toArray(new Object[0]);
}
@Override
public <T> T[] toArray(T[] services) {
if (services.length < _services.size()) {
Class<?> clazz = services.getClass();
services = (T[])Array.newInstance(
clazz.getComponentType(), _services.size());
}
for (int i = 0; i < _services.size(); i++) {
EntryWrapper entryWrapper = _services.get(i);
services[i] = (T)entryWrapper._service;
}
if (services.length > _services.size()) {
services[_services.size()] = null;
}
return services;
}
private Filter _getFilter(Filter filter, Class<S> clazz) {
Map<String, Object> properties =
Collections.<String, Object>singletonMap(
"objectClass", clazz.getName());
if (filter.matches(properties)) {
return filter;
}
Registry registry = RegistryUtil.getRegistry();
StringBuilder sb = new StringBuilder(5);
sb.append("(&(objectClass=");
sb.append(clazz.getName());
sb.append(")");
sb.append(filter.toString());
sb.append(")");
return registry.getFilter(sb.toString());
}
private final Class<S> _clazz;
private final Filter _filter;
private final Map<String, Object> _properties;
private final ServiceRegistrationMap<S> _serviceRegistrations =
new ServiceRegistrationMapImpl<>();
private final List<EntryWrapper> _services = new CopyOnWriteArrayList<>();
private final ServiceTracker<S, S> _serviceTracker;
private class DefaultServiceTrackerCustomizer
implements ServiceTrackerCustomizer<S, S> {
public DefaultServiceTrackerCustomizer(
ServiceTrackerCustomizer<S, S> serviceTrackerCustomizer) {
_serviceTrackerCustomizer = serviceTrackerCustomizer;
}
@Override
public S addingService(ServiceReference<S> serviceReference) {
S service = null;
if (_serviceTrackerCustomizer != null) {
service = _serviceTrackerCustomizer.addingService(
serviceReference);
}
else {
Registry registry = RegistryUtil.getRegistry();
service = registry.getService(serviceReference);
}
_update(serviceReference, service, false);
return service;
}
@Override
public void modifiedService(
ServiceReference<S> serviceReference, S service) {
if (_serviceTrackerCustomizer != null) {
_serviceTrackerCustomizer.modifiedService(
serviceReference, service);
}
_update(serviceReference, service, false);
}
@Override
public void removedService(
ServiceReference<S> serviceReference, S service) {
if (_serviceTrackerCustomizer != null) {
_serviceTrackerCustomizer.removedService(
serviceReference, service);
}
_update(serviceReference, service, true);
Registry registry = RegistryUtil.getRegistry();
registry.ungetService(serviceReference);
}
private void _update(
ServiceReference<S> serviceReference, S service, boolean remove) {
if (service == null) {
return;
}
EntryWrapper entryWrapper = new EntryWrapper(
serviceReference, service);
synchronized (_services) {
int index = Collections.binarySearch(_services, entryWrapper);
if (remove) {
if (index >= 0) {
_services.remove(index);
}
}
else if (index < 0) {
_services.add((-index) - 1, entryWrapper);
}
}
}
private final ServiceTrackerCustomizer<S, S> _serviceTrackerCustomizer;
}
private class EntryWrapper implements Comparable<EntryWrapper> {
public EntryWrapper(ServiceReference<S> serviceReference, S service) {
_serviceReference = serviceReference;
_service = service;
}
@Override
public int compareTo(EntryWrapper entryWrapper) {
// The order is deliberately reversed
return entryWrapper._serviceReference.compareTo(_serviceReference);
}
private final S _service;
private final ServiceReference<S> _serviceReference;
}
private class ServiceTrackerIterator implements ListIterator<S> {
public ServiceTrackerIterator(ListIterator<EntryWrapper> listIterator) {
_listIterator = listIterator;
}
@Override
public void add(S service) {
throw new UnsupportedOperationException();
}
@Override
public boolean hasNext() {
return _listIterator.hasNext();
}
@Override
public boolean hasPrevious() {
return _listIterator.hasPrevious();
}
@Override
public S next() {
EntryWrapper entryWrapper = _listIterator.next();
return entryWrapper._service;
}
@Override
public int nextIndex() {
return _listIterator.nextIndex();
}
@Override
public S previous() {
EntryWrapper entryWrapper = _listIterator.previous();
return entryWrapper._service;
}
@Override
public int previousIndex() {
return _listIterator.previousIndex();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
@Override
public void set(S service) {
throw new UnsupportedOperationException();
}
private final ListIterator<EntryWrapper> _listIterator;
}
}