blob: 5a1f2714ac30407c04c961a9f065f08f5960c355 [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.aries.blueprint.container;
import java.util.*;
import java.util.concurrent.Callable;
import org.apache.aries.blueprint.ExtendedBlueprintContainer;
import org.apache.aries.blueprint.ExtendedReferenceListMetadata;
import org.apache.aries.blueprint.ExtendedServiceReferenceMetadata;
import org.apache.aries.blueprint.di.Recipe;
import org.apache.aries.blueprint.di.CollectionRecipe;
import org.apache.aries.blueprint.utils.DynamicCollection;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.service.blueprint.container.ReifiedType;
import org.osgi.service.blueprint.container.ComponentDefinitionException;
import org.osgi.service.blueprint.container.ServiceUnavailableException;
import org.osgi.service.blueprint.reflect.ReferenceListMetadata;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A recipe to create a managed collection of service references
*
* @version $Rev$, $Date$
*/
public class ReferenceListRecipe extends AbstractServiceReferenceRecipe {
private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceListRecipe.class);
private final ReferenceListMetadata metadata;
private final List<ManagedCollection> collections = new ArrayList<ManagedCollection>();
private final DynamicCollection<ServiceDispatcher> storage = new DynamicCollection<ServiceDispatcher>();
private final List<ServiceDispatcher> unboundDispatchers = new ArrayList<ServiceDispatcher>();
private final Object monitor = new Object();
public ReferenceListRecipe(String name,
ExtendedBlueprintContainer blueprintContainer,
ReferenceListMetadata metadata,
CollectionRecipe listenersRecipe,
List<Recipe> explicitDependencies) {
super(name, blueprintContainer, metadata, listenersRecipe, explicitDependencies);
this.metadata = metadata;
}
@Override
protected Object internalCreate() throws ComponentDefinitionException {
try {
if (explicitDependencies != null) {
for (Recipe recipe : explicitDependencies) {
recipe.create();
}
}
ProvidedObject object = new ProvidedObject();
addPartialObject(object);
// Handle initial references
createListeners();
updateListeners();
return object;
} catch (ComponentDefinitionException t) {
throw t;
} catch (Throwable t) {
throw new ComponentDefinitionException(t);
}
}
protected void retrack() {
List<ServiceReference> refs = getServiceReferences();
if (refs != null) {
for (ServiceReference ref : refs) {
track(ref);
}
}
}
protected void track(ServiceReference reference) {
synchronized (monitor) {
try {
// ServiceReferences may be tracked at multiple points:
// * first after the collection creation in #internalCreate()
// * in #postCreate() after listeners are created
// * after creation time if a new reference shows up
//
// In the first step, listeners are not created, so we add
// the dispatcher to the unboundDispatchers list. In the second
// step, the dispatcher has already been added to the collection
// so we just call the listener.
//
ServiceDispatcher dispatcher = findDispatcher(reference);
if (dispatcher != null) {
if (!unboundDispatchers.remove(dispatcher)) {
return;
}
} else {
dispatcher = new ServiceDispatcher(reference);
Set<Class> interfaces = new HashSet<Class>();
if (metadata.getInterface() != null) {
interfaces.add(loadClass(metadata.getInterface()));
}
if (metadata instanceof ExtendedReferenceListMetadata) {
if (((ExtendedServiceReferenceMetadata) metadata).getRuntimeInterface() != null) {
interfaces.add(((ExtendedServiceReferenceMetadata) metadata).getRuntimeInterface());
}
boolean greedy = (((ExtendedReferenceListMetadata) metadata).getProxyMethod() & ExtendedReferenceListMetadata.PROXY_METHOD_GREEDY) != 0;
if (greedy) {
List<String> ifs = Arrays.asList((String[]) reference.getProperty(Constants.OBJECTCLASS));
interfaces.addAll(loadAllClasses(ifs));
}
}
dispatcher.proxy = createProxy(dispatcher, interfaces);
if (!storage.add(dispatcher)) {
dispatcher.destroy();
return;
}
}
if (listeners != null) {
bind(dispatcher.reference, dispatcher.proxy);
} else {
unboundDispatchers.add(dispatcher);
}
} catch (Throwable t) {
LOGGER.info("Error tracking new service reference", t);
}
}
}
protected void untrack(ServiceReference reference) {
synchronized (monitor) {
ServiceDispatcher dispatcher = findDispatcher(reference);
if (dispatcher != null) {
unbind(dispatcher.reference, dispatcher.proxy);
storage.remove(dispatcher);
dispatcher.destroy();
}
}
}
protected ServiceDispatcher findDispatcher(ServiceReference reference) {
for (ServiceDispatcher dispatcher : storage) {
if (dispatcher.reference == reference) {
return dispatcher;
}
}
return null;
}
protected ManagedCollection getManagedCollection(boolean useReferences) {
for (ManagedCollection col : collections) {
if (col.references == useReferences) {
return col;
}
}
ManagedCollection collection = new ManagedCollection(useReferences, storage);
collections.add(collection);
return collection;
}
/**
* The ServiceDispatcher is used when creating the cglib proxy.
* Thic class is responsible for getting the actual service that will be used.
*/
public class ServiceDispatcher implements Callable<Object> {
public ServiceReference reference;
public Object service;
public Object proxy;
public ServiceDispatcher(ServiceReference reference) throws Exception {
this.reference = reference;
}
public synchronized void destroy() {
if (reference != null) {
reference.getBundle().getBundleContext().ungetService(reference);
reference = null;
service = null;
proxy = null;
}
}
public synchronized Object call() throws Exception {
if (reference == null) {
throw new ServiceUnavailableException("Service is unavailable", getOsgiFilter());
}
if (service == null) {
service = blueprintContainer.getService(reference);
}
return service;
}
}
public class ProvidedObject implements AggregateConverter.Convertible {
public Object convert(ReifiedType type) {
LOGGER.debug("Converting ManagedCollection to {}", type);
if (!type.getRawClass().isAssignableFrom(List.class)) {
throw new ComponentDefinitionException("<reference-list/> can only be converted to a List, not " + type);
}
int memberType = metadata.getMemberType();
boolean useRef = false;
if (type.size() == 1) {
useRef = (type.getActualTypeArgument(0).getRawClass() == ServiceReference.class);
if ( (useRef && memberType == ReferenceListMetadata.USE_SERVICE_OBJECT) ||
(!useRef && memberType == ReferenceListMetadata.USE_SERVICE_REFERENCE)) {
throw new ComponentDefinitionException("The memeber-type specified is incompatible with generic injection type");
}
}
boolean references;
if (memberType == ReferenceListMetadata.USE_SERVICE_REFERENCE) {
references = true;
} else if (memberType == ReferenceListMetadata.USE_SERVICE_OBJECT) {
references = false;
} else {
references = useRef;
}
LOGGER.debug("ManagedCollection references={}", references);
return getManagedCollection(references);
}
}
/**
* Base class for managed collections.
*
* TODO: list iterators should not be supported
* TODO: rework the iteration so that if hasNext() has returned false, it will always return false
* TODO: implement subList()
*/
public static class ManagedCollection extends AbstractCollection implements List, RandomAccess {
protected final DynamicCollection<ServiceDispatcher> dispatchers;
protected boolean references;
public ManagedCollection(boolean references, DynamicCollection<ServiceDispatcher> dispatchers) {
this.references = references;
this.dispatchers = dispatchers;
LOGGER.debug("ManagedCollection references={}", references);
}
public boolean addDispatcher(ServiceDispatcher dispatcher) {
return dispatchers.add(dispatcher);
}
public boolean removeDispatcher(ServiceDispatcher dispatcher) {
return dispatchers.remove(dispatcher);
}
public DynamicCollection<ServiceDispatcher> getDispatchers() {
return dispatchers;
}
public Iterator iterator() {
return new ManagedListIterator(dispatchers.iterator());
}
public int size() {
return dispatchers.size();
}
@Override
public boolean add(Object o) {
throw new UnsupportedOperationException("This collection is read only");
}
@Override
public boolean remove(Object o) {
throw new UnsupportedOperationException("This collection is read only");
}
@Override
public boolean addAll(Collection c) {
throw new UnsupportedOperationException("This collection is read only");
}
@Override
public void clear() {
throw new UnsupportedOperationException("This collection is read only");
}
@Override
public boolean retainAll(Collection c) {
throw new UnsupportedOperationException("This collection is read only");
}
@Override
public boolean removeAll(Collection c) {
throw new UnsupportedOperationException("This collection is read only");
}
public Object get(int index) {
return references ? dispatchers.get(index).reference : dispatchers.get(index).proxy;
}
public int indexOf(Object o) {
if (o == null) {
throw new NullPointerException();
}
ListIterator e = listIterator();
while (e.hasNext()) {
if (o.equals(e.next())) {
return e.previousIndex();
}
}
return -1;
}
public int lastIndexOf(Object o) {
if (o == null) {
throw new NullPointerException();
}
ListIterator e = listIterator(size());
while (e.hasPrevious()) {
if (o.equals(e.previous())) {
return e.nextIndex();
}
}
return -1;
}
public ListIterator listIterator() {
return listIterator(0);
}
public ListIterator listIterator(int index) {
return new ManagedListIterator(dispatchers.iterator(index));
}
public List<ServiceDispatcher> subList(int fromIndex, int toIndex) {
throw new UnsupportedOperationException("Not implemented");
}
public Object set(int index, Object element) {
throw new UnsupportedOperationException("This collection is read only");
}
public void add(int index, Object element) {
throw new UnsupportedOperationException("This collection is read only");
}
public Object remove(int index) {
throw new UnsupportedOperationException("This collection is read only");
}
public boolean addAll(int index, Collection c) {
throw new UnsupportedOperationException("This collection is read only");
}
public class ManagedListIterator implements ListIterator {
protected final ListIterator<ServiceDispatcher> iterator;
public ManagedListIterator(ListIterator<ServiceDispatcher> iterator) {
this.iterator = iterator;
}
public boolean hasNext() {
return iterator.hasNext();
}
public Object next() {
return references ? iterator.next().reference : iterator.next().proxy;
}
public boolean hasPrevious() {
return iterator.hasPrevious();
}
public Object previous() {
return references ? iterator.previous().reference : iterator.previous().proxy;
}
public int nextIndex() {
return iterator.nextIndex();
}
public int previousIndex() {
return iterator.previousIndex();
}
public void remove() {
throw new UnsupportedOperationException("This collection is read only");
}
public void set(Object o) {
throw new UnsupportedOperationException("This collection is read only");
}
public void add(Object o) {
throw new UnsupportedOperationException("This collection is read only");
}
}
}
}