Return-Path: Delivered-To: apmail-aries-commits-archive@www.apache.org Received: (qmail 67777 invoked from network); 27 Feb 2011 20:51:44 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 27 Feb 2011 20:51:44 -0000 Received: (qmail 7198 invoked by uid 500); 27 Feb 2011 20:51:44 -0000 Delivered-To: apmail-aries-commits-archive@aries.apache.org Received: (qmail 7123 invoked by uid 500); 27 Feb 2011 20:51:43 -0000 Mailing-List: contact commits-help@aries.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@aries.apache.org Delivered-To: mailing list commits@aries.apache.org Received: (qmail 7106 invoked by uid 99); 27 Feb 2011 20:51:43 -0000 Received: from athena.apache.org (HELO athena.apache.org) (140.211.11.136) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Feb 2011 20:51:43 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=5.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Sun, 27 Feb 2011 20:51:40 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id E8C802388C36; Sun, 27 Feb 2011 20:50:56 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r1075143 [13/23] - in /aries/tags/blueprint-0.2.1: ./ blueprint-annotation-api/ blueprint-annotation-api/src/ blueprint-annotation-api/src/main/ blueprint-annotation-api/src/main/java/ blueprint-annotation-api/src/main/java/org/ blueprint-a... Date: Sun, 27 Feb 2011 20:50:51 -0000 To: commits@aries.apache.org From: zoe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20110227205056.E8C802388C36@eris.apache.org> Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/RecipeBuilder.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,361 @@ +/* + * 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.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import org.apache.aries.blueprint.ComponentDefinitionRegistry; +import org.apache.aries.blueprint.ExtendedBeanMetadata; +import org.apache.aries.blueprint.ExtendedBlueprintContainer; +import org.apache.aries.blueprint.PassThroughMetadata; +import org.apache.aries.blueprint.di.ArrayRecipe; +import org.apache.aries.blueprint.di.CollectionRecipe; +import org.apache.aries.blueprint.di.ComponentFactoryRecipe; +import org.apache.aries.blueprint.di.DependentComponentFactoryRecipe; +import org.apache.aries.blueprint.di.IdRefRecipe; +import org.apache.aries.blueprint.di.MapRecipe; +import org.apache.aries.blueprint.di.PassThroughRecipe; +import org.apache.aries.blueprint.di.Recipe; +import org.apache.aries.blueprint.di.RefRecipe; +import org.apache.aries.blueprint.di.ValueRecipe; +import org.apache.aries.blueprint.ext.ComponentFactoryMetadata; +import org.apache.aries.blueprint.ext.DependentComponentFactoryMetadata; +import org.apache.aries.blueprint.mutable.MutableMapMetadata; +import org.apache.aries.blueprint.reflect.MetadataUtil; +import org.osgi.service.blueprint.reflect.BeanArgument; +import org.osgi.service.blueprint.reflect.BeanMetadata; +import org.osgi.service.blueprint.reflect.BeanProperty; +import org.osgi.service.blueprint.reflect.CollectionMetadata; +import org.osgi.service.blueprint.reflect.ComponentMetadata; +import org.osgi.service.blueprint.reflect.IdRefMetadata; +import org.osgi.service.blueprint.reflect.MapEntry; +import org.osgi.service.blueprint.reflect.MapMetadata; +import org.osgi.service.blueprint.reflect.Metadata; +import org.osgi.service.blueprint.reflect.NullMetadata; +import org.osgi.service.blueprint.reflect.PropsMetadata; +import org.osgi.service.blueprint.reflect.RefMetadata; +import org.osgi.service.blueprint.reflect.ReferenceListMetadata; +import org.osgi.service.blueprint.reflect.ReferenceListener; +import org.osgi.service.blueprint.reflect.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.RegistrationListener; +import org.osgi.service.blueprint.reflect.ServiceMetadata; +import org.osgi.service.blueprint.reflect.ValueMetadata; + +/** + * TODO: javadoc + * + * @version $Rev: 985150 $, $Date: 2010-08-13 11:20:09 +0100 (Fri, 13 Aug 2010) $ + */ +public class RecipeBuilder { + + private final Set names = new HashSet(); + private final ExtendedBlueprintContainer blueprintContainer; + private final ComponentDefinitionRegistry registry; + private final IdSpace recipeIdSpace; + + public RecipeBuilder(ExtendedBlueprintContainer blueprintContainer, IdSpace recipeIdSpace) { + this.recipeIdSpace = recipeIdSpace; + this.blueprintContainer = blueprintContainer; + this.registry = blueprintContainer.getComponentDefinitionRegistry(); + } + + public BlueprintRepository createRepository() { + BlueprintRepository repository = new BlueprintRepository(blueprintContainer); + // Create component recipes + for (String name : registry.getComponentDefinitionNames()) { + ComponentMetadata component = registry.getComponentDefinition(name); + Recipe recipe = createRecipe(component); + repository.putRecipe(recipe.getName(), recipe); + } + repository.validate(); + return repository; + } + + public Recipe createRecipe(ComponentMetadata component) { + + // Custom components should be handled before built-in ones + // in case we have a custom component that also implements a built-in metadata + + if (component instanceof DependentComponentFactoryMetadata) { + return createDependentComponentFactoryMetadata((DependentComponentFactoryMetadata) component); + } else if (component instanceof ComponentFactoryMetadata) { + return createComponentFactoryMetadata((ComponentFactoryMetadata) component); + } else if (component instanceof BeanMetadata) { + return createBeanRecipe((BeanMetadata) component); + } else if (component instanceof ServiceMetadata) { + return createServiceRecipe((ServiceMetadata) component); + } else if (component instanceof ReferenceMetadata) { + return createReferenceRecipe((ReferenceMetadata) component); + } else if (component instanceof ReferenceListMetadata) { + return createReferenceListRecipe((ReferenceListMetadata) component); + } else if (component instanceof PassThroughMetadata) { + return createPassThroughRecipe((PassThroughMetadata) component); + } else { + throw new IllegalStateException("Unsupported component type " + component.getClass()); + } + } + + private Recipe createComponentFactoryMetadata(ComponentFactoryMetadata metadata) { + return new ComponentFactoryRecipe( + metadata.getId(), metadata, blueprintContainer, getDependencies(metadata)); + } + + private Recipe createDependentComponentFactoryMetadata(DependentComponentFactoryMetadata metadata) { + return new DependentComponentFactoryRecipe( + metadata.getId(), metadata, blueprintContainer, getDependencies(metadata)); + } + + private List getDependencies(ComponentMetadata metadata) { + List deps = new ArrayList(); + for (String name : metadata.getDependsOn()) { + deps.add(new RefRecipe(getName(null), name)); + } + return deps; + } + + private Recipe createPassThroughRecipe(PassThroughMetadata passThroughMetadata) { + return new PassThroughRecipe(getName(passThroughMetadata.getId()), + passThroughMetadata.getObject()); + } + + private Recipe createReferenceListRecipe(ReferenceListMetadata metadata) { + CollectionRecipe listenersRecipe = null; + if (metadata.getReferenceListeners() != null) { + listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class); + for (ReferenceListener listener : metadata.getReferenceListeners()) { + listenersRecipe.add(createRecipe(listener)); + } + } + ReferenceListRecipe recipe = new ReferenceListRecipe(getName(metadata.getId()), + blueprintContainer, + metadata, + listenersRecipe, + getDependencies(metadata)); + return recipe; + } + + private ReferenceRecipe createReferenceRecipe(ReferenceMetadata metadata) { + CollectionRecipe listenersRecipe = null; + if (metadata.getReferenceListeners() != null) { + listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class); + for (ReferenceListener listener : metadata.getReferenceListeners()) { + listenersRecipe.add(createRecipe(listener)); + } + } + ReferenceRecipe recipe = new ReferenceRecipe(getName(metadata.getId()), + blueprintContainer, + metadata, + listenersRecipe, + getDependencies(metadata)); + return recipe; + } + + private Recipe createServiceRecipe(ServiceMetadata serviceExport) { + CollectionRecipe listenersRecipe = new CollectionRecipe(getName(null), ArrayList.class); + if (serviceExport.getRegistrationListeners() != null) { + for (RegistrationListener listener : serviceExport.getRegistrationListeners()) { + listenersRecipe.add(createRecipe(listener)); + } + } + ServiceRecipe recipe = new ServiceRecipe(getName(serviceExport.getId()), + blueprintContainer, + serviceExport, + getValue(serviceExport.getServiceComponent(), null), + listenersRecipe, + getServicePropertiesRecipe(serviceExport), + getDependencies(serviceExport)); + return recipe; + } + + protected MapRecipe getServicePropertiesRecipe(ServiceMetadata metadata) { + List properties = metadata.getServiceProperties(); + if (properties != null) { + MutableMapMetadata map = MetadataUtil.createMetadata(MutableMapMetadata.class); + for (MapEntry e : properties) { + map.addEntry(e); + } + return createMapRecipe(map); + } else { + return null; + } + } + + private Object getBeanClass(BeanMetadata beanMetadata) { + if (beanMetadata instanceof ExtendedBeanMetadata) { + ExtendedBeanMetadata extBeanMetadata = (ExtendedBeanMetadata) beanMetadata; + if (extBeanMetadata.getRuntimeClass() != null) { + return extBeanMetadata.getRuntimeClass(); + } + } + return beanMetadata.getClassName(); + } + + private boolean allowsFieldInjection(BeanMetadata beanMetadata) { + if (beanMetadata instanceof ExtendedBeanMetadata) { + return ((ExtendedBeanMetadata) beanMetadata).getFieldInjection(); + } + return false; + } + + private BeanRecipe createBeanRecipe(BeanMetadata beanMetadata) { + BeanRecipe recipe = new BeanRecipe( + getName(beanMetadata.getId()), + blueprintContainer, + getBeanClass(beanMetadata), + allowsFieldInjection(beanMetadata)); + // Create refs for explicit dependencies + recipe.setExplicitDependencies(getDependencies(beanMetadata)); + recipe.setPrototype(MetadataUtil.isPrototypeScope(beanMetadata)); + recipe.setInitMethod(beanMetadata.getInitMethod()); + recipe.setDestroyMethod(beanMetadata.getDestroyMethod()); + recipe.setInterceptorLookupKey(beanMetadata); + List beanArguments = beanMetadata.getArguments(); + if (beanArguments != null && !beanArguments.isEmpty()) { + boolean hasIndex = (beanArguments.get(0).getIndex() >= 0); + if (hasIndex) { + List beanArgumentsCopy = new ArrayList(beanArguments); + Collections.sort(beanArgumentsCopy, MetadataUtil.BEAN_COMPARATOR); + beanArguments = beanArgumentsCopy; + } + List arguments = new ArrayList(); + List argTypes = new ArrayList(); + for (BeanArgument argument : beanArguments) { + Recipe value = getValue(argument.getValue(), null); + arguments.add(value); + argTypes.add(argument.getValueType()); + } + recipe.setArguments(arguments); + recipe.setArgTypes(argTypes); + recipe.setReorderArguments(!hasIndex); + } + recipe.setFactoryMethod(beanMetadata.getFactoryMethod()); + if (beanMetadata.getFactoryComponent() != null) { + recipe.setFactoryComponent(getValue(beanMetadata.getFactoryComponent(), null)); + } + for (BeanProperty property : beanMetadata.getProperties()) { + Recipe value = getValue(property.getValue(), null); + recipe.setProperty(property.getName(), value); + } + return recipe; + } + + private Recipe createRecipe(RegistrationListener listener) { + BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, ServiceListener.class, false); + recipe.setProperty("listener", getValue(listener.getListenerComponent(), null)); + if (listener.getRegistrationMethod() != null) { + recipe.setProperty("registerMethod", listener.getRegistrationMethod()); + } + if (listener.getUnregistrationMethod() != null) { + recipe.setProperty("unregisterMethod", listener.getUnregistrationMethod()); + } + recipe.setProperty("blueprintContainer", blueprintContainer); + return recipe; + } + + private Recipe createRecipe(ReferenceListener listener) { + BeanRecipe recipe = new BeanRecipe(getName(null), blueprintContainer, AbstractServiceReferenceRecipe.Listener.class, false); + recipe.setProperty("listener", getValue(listener.getListenerComponent(), null)); + recipe.setProperty("metadata", listener); + recipe.setProperty("blueprintContainer", blueprintContainer); + return recipe; + } + + private Recipe getValue(Metadata v, Object groupingType) { + if (v instanceof NullMetadata) { + return null; + } else if (v instanceof ComponentMetadata) { + return createRecipe((ComponentMetadata) v); + } else if (v instanceof ValueMetadata) { + ValueMetadata stringValue = (ValueMetadata) v; + Object type = stringValue.getType(); + type = (type == null) ? groupingType : type; + ValueRecipe vr = new ValueRecipe(getName(null), stringValue, type); + return vr; + } else if (v instanceof RefMetadata) { + // TODO: make it work with property-placeholders? + String componentName = ((RefMetadata) v).getComponentId(); + RefRecipe rr = new RefRecipe(getName(null), componentName); + return rr; + } else if (v instanceof CollectionMetadata) { + CollectionMetadata collectionMetadata = (CollectionMetadata) v; + Class cl = collectionMetadata.getCollectionClass(); + Object type = collectionMetadata.getValueType(); + if (cl == Object[].class) { + ArrayRecipe ar = new ArrayRecipe(getName(null), type); + for (Metadata lv : collectionMetadata.getValues()) { + ar.add(getValue(lv, type)); + } + return ar; + } else { + CollectionRecipe cr = new CollectionRecipe(getName(null), cl != null ? cl : ArrayList.class); + for (Metadata lv : collectionMetadata.getValues()) { + cr.add(getValue(lv, type)); + } + return cr; + } + } else if (v instanceof MapMetadata) { + return createMapRecipe((MapMetadata) v); + } else if (v instanceof PropsMetadata) { + PropsMetadata mapValue = (PropsMetadata) v; + MapRecipe mr = new MapRecipe(getName(null), Properties.class); + for (MapEntry entry : mapValue.getEntries()) { + Recipe key = getValue(entry.getKey(), String.class); + Recipe val = getValue(entry.getValue(), String.class); + mr.put(key, val); + } + return mr; + } else if (v instanceof IdRefMetadata) { + // TODO: make it work with property-placeholders? + String componentName = ((IdRefMetadata) v).getComponentId(); + IdRefRecipe rnr = new IdRefRecipe(getName(null), componentName); + return rnr; + } else { + throw new IllegalStateException("Unsupported value: " + v.getClass().getName()); + } + } + + private MapRecipe createMapRecipe(MapMetadata mapValue) { + String keyType = mapValue.getKeyType(); + String valueType = mapValue.getValueType(); + MapRecipe mr = new MapRecipe(getName(null), HashMap.class); + for (MapEntry entry : mapValue.getEntries()) { + Recipe key = getValue(entry.getKey(), keyType); + Recipe val = getValue(entry.getValue(), valueType); + mr.put(key, val); + } + return mr; + } + + private String getName(String name) { + if (name == null) { + do { + name = "#recipe-" + recipeIdSpace.nextId(); + } while (names.contains(name) || registry.containsComponentDefinition(name)); + } + names.add(name); + return name; + } + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceListRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceListRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceListRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceListRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,415 @@ +/* + * 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: 950985 $, $Date: 2010-06-03 14:19:22 +0100 (Thu, 03 Jun 2010) $ + */ +public class ReferenceListRecipe extends AbstractServiceReferenceRecipe { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceListRecipe.class); + + private final ReferenceListMetadata metadata; + private final List collections = new ArrayList(); + private final DynamicCollection storage = new DynamicCollection(); + private final List unboundDispatchers = new ArrayList(); + private final Object monitor = new Object(); + + public ReferenceListRecipe(String name, + ExtendedBlueprintContainer blueprintContainer, + ReferenceListMetadata metadata, + CollectionRecipe listenersRecipe, + List 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 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 interfaces = new HashSet(); + 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 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 { + + 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(" 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 dispatchers; + protected boolean references; + + public ManagedCollection(boolean references, DynamicCollection 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 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 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 iterator; + + public ManagedListIterator(ListIterator 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"); + } + } + + } + + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ReferenceRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,227 @@ +/* + * 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.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.concurrent.Callable; + +import org.apache.aries.blueprint.ExtendedBlueprintContainer; +import org.apache.aries.blueprint.ExtendedServiceReferenceMetadata; +import org.apache.aries.blueprint.di.Recipe; +import org.apache.aries.blueprint.di.CollectionRecipe; +import org.osgi.framework.ServiceReference; +import org.osgi.service.blueprint.container.BlueprintEvent; +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.ReferenceMetadata; +import org.osgi.service.blueprint.reflect.ServiceReferenceMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A recipe to create an unary OSGi service reference. + * + * TODO: check synchronization / thread safety + * + * TODO: looks there is a potential problem if the service is unregistered between a call + * to ServiceDispatcher#loadObject() and when the actual invocation finish + * + * @version $Rev: 950985 $, $Date: 2010-06-03 14:19:22 +0100 (Thu, 03 Jun 2010) $ + */ +public class ReferenceRecipe extends AbstractServiceReferenceRecipe { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReferenceRecipe.class); + + private final ReferenceMetadata metadata; + private Object proxy; + + private volatile ServiceReference trackedServiceReference; + private volatile Object trackedService; + private final Object monitor = new Object(); + + public ReferenceRecipe(String name, + ExtendedBlueprintContainer blueprintContainer, + ReferenceMetadata metadata, + CollectionRecipe listenersRecipe, + List 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(); + } + } + // Create the proxy + Set interfaces = new HashSet(); + if (this.metadata.getInterface() != null) { + interfaces.add(loadClass(this.metadata.getInterface())); + } + if (this.metadata instanceof ExtendedServiceReferenceMetadata && ((ExtendedServiceReferenceMetadata) this.metadata).getRuntimeInterface() != null) { + interfaces.add(((ExtendedServiceReferenceMetadata) this.metadata).getRuntimeInterface()); + } + + proxy = createProxy(new ServiceDispatcher(), interfaces); + + // Add partially created proxy to the context + ServiceProxyWrapper wrapper = new ServiceProxyWrapper(); + + addPartialObject(wrapper); + + // Handle initial references + createListeners(); + updateListeners(); + + // Return a ServiceProxy that can injection of references or proxies can be done correctly + return wrapper; + } catch (ComponentDefinitionException e) { + throw e; + } catch (Throwable t) { + throw new ComponentDefinitionException(t); + } + } + + protected void doStop() { + synchronized (monitor) { + unbind(); + monitor.notifyAll(); + } + } + + protected void retrack() { + ServiceReference ref = getBestServiceReference(); + if (ref != null) { + bind(ref); + } else { + unbind(); + } + } + + protected void track(ServiceReference ref) { + // TODO: make this behavior configurable through a custom attribute + // TODO: policy = sticky | replace + synchronized (monitor) { + if (trackedServiceReference == null) { + retrack(); + } + } + } + + protected void untrack(ServiceReference ref) { + // TODO: make this behavior configurable through a custom attribute + // TODO: policy = sticky | replace + synchronized (monitor) { + if (trackedServiceReference == ref) { + retrack(); + } + } + } + + private void bind(ServiceReference ref) { + LOGGER.debug("Binding reference {} to {}", getName(), ref); + synchronized (monitor) { + if (trackedServiceReference != null) { + blueprintContainer.getBundleContext().ungetService(trackedServiceReference); + } + trackedServiceReference = ref; + trackedService = null; + monitor.notifyAll(); + bind(trackedServiceReference, proxy); + } + } + + private void unbind() { + LOGGER.debug("Unbinding reference {}", getName()); + synchronized (monitor) { + if (trackedServiceReference != null) { + unbind(trackedServiceReference, proxy); + blueprintContainer.getBundleContext().ungetService(trackedServiceReference); + trackedServiceReference = null; + trackedService = null; + monitor.notifyAll(); + } + } + } + + private Object getService() throws InterruptedException { + synchronized (monitor) { + if (isStarted() && trackedServiceReference == null && metadata.getTimeout() > 0 + && metadata.getAvailability() == ServiceReferenceMetadata.AVAILABILITY_MANDATORY) { + blueprintContainer.getEventDispatcher().blueprintEvent(new BlueprintEvent(BlueprintEvent.WAITING, blueprintContainer.getBundleContext().getBundle(), blueprintContainer.getExtenderBundle(), new String[] { getOsgiFilter() })); + monitor.wait(metadata.getTimeout()); + } + if (trackedServiceReference == null) { + if (isStarted()) { + LOGGER.info("Timeout expired when waiting for OSGi service {}", getOsgiFilter()); + throw new ServiceUnavailableException("Timeout expired when waiting for OSGi service", getOsgiFilter()); + } else { + throw new ServiceUnavailableException("The Blueprint container is being or has been destroyed", getOsgiFilter()); + } + } + if (trackedService == null) { + trackedService = blueprintContainer.getService(trackedServiceReference); + } + if (trackedService == null) { + throw new IllegalStateException("getService() returned null for " + trackedServiceReference); + } + return trackedService; + } + } + + private ServiceReference getServiceReference() throws InterruptedException { + synchronized (monitor) { + if (!optional) { + getService(); + } + return trackedServiceReference; + } + } + + public class ServiceDispatcher implements Callable { + + public Object call() throws Exception { + return getService(); + } + + } + + public class ServiceProxyWrapper implements AggregateConverter.Convertible { + + public Object convert(ReifiedType type) throws Exception { + if (type.getRawClass() == ServiceReference.class) { + return getServiceReference(); + } else if (type.getRawClass().isInstance(proxy)) { + return proxy; + } else { + throw new ComponentDefinitionException("Unable to convert to " + type); + } + } + + } + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/SatisfiableRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/SatisfiableRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/SatisfiableRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/SatisfiableRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,52 @@ +/* + * 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 org.apache.aries.blueprint.di.Recipe; + +/** + * Interface used to describe an object which can satisfy a constraint or not. + * + * If the state of the object changes, registered SatisfactionListener objects + * will be notified of the change. + * + * @version $Rev: 896324 $, $Date: 2010-01-06 06:05:04 +0000 (Wed, 06 Jan 2010) $ + */ +public interface SatisfiableRecipe extends Recipe { + + /** + * A listener that will be notified when the constraint satisfaction changes. + * + * @version $Rev: 896324 $, $Date: 2010-01-06 06:05:04 +0000 (Wed, 06 Jan 2010) $ + */ + public interface SatisfactionListener { + + void notifySatisfaction(SatisfiableRecipe satisfiable); + + } + + void start(SatisfactionListener listener); + + void stop(); + + boolean isSatisfied(); + + String getOsgiFilter(); + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceListener.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceListener.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceListener.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceListener.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,107 @@ +/** + * 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.lang.reflect.Method; +import java.util.List; +import java.util.Map; + +import org.apache.aries.blueprint.ExtendedBlueprintContainer; +import org.apache.aries.blueprint.utils.ReflectionUtils; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ServiceListener { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceListener.class); + + private Object listener; + private String registerMethod; + private String unregisterMethod; + private ExtendedBlueprintContainer blueprintContainer; + + private List registerMethods; + private List unregisterMethods; + private boolean initialized = false; + + public void setListener(Object listener) { + this.listener = listener; + } + + public void setRegisterMethod(String method) { + this.registerMethod = method; + } + + public void setUnregisterMethod(String method) { + this.unregisterMethod = method; + } + + public void setBlueprintContainer(ExtendedBlueprintContainer blueprintContainer) { + this.blueprintContainer = blueprintContainer; + } + + public void register(Object service, Map properties) { + init(service); + invokeMethod(registerMethods, service, properties); + } + + public void unregister(Object service, Map properties) { + init(service); + invokeMethod(unregisterMethods, service, properties); + } + + private synchronized void init(Object service) { + if (initialized) { + return; + } + Class[] paramTypes = new Class[] { service != null ? service.getClass() : null, Map.class }; + Class listenerClass = listener.getClass(); + + if (registerMethod != null) { + registerMethods = ReflectionUtils.findCompatibleMethods(listenerClass, registerMethod, paramTypes); + if (registerMethods.size() == 0) { + throw new ComponentDefinitionException("No matching methods found for listener registration method: " + registerMethod); + } + LOGGER.debug("Found register methods: {}", registerMethods); + } + if (unregisterMethod != null) { + unregisterMethods = ReflectionUtils.findCompatibleMethods(listenerClass, unregisterMethod, paramTypes); + if (unregisterMethods.size() == 0) { + throw new ComponentDefinitionException("No matching methods found for listener unregistration method: " + unregisterMethod); + } + LOGGER.debug("Found unregister methods: {}", unregisterMethods); + } + initialized = true; + } + + private void invokeMethod(List methods, Object service, Map properties) { + if (methods == null || methods.isEmpty()) { + return; + } + for (Method method : methods) { + try { + ReflectionUtils.invoke(blueprintContainer.getAccessControlContext(), + method, listener, service, properties); + } catch (Exception e) { + LOGGER.error("Error calling listener method " + method, e); + } + } + } + +} + Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/container/ServiceRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,408 @@ +/** + * 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.ArrayList; +import java.util.Collections; +import java.util.Dictionary; +import java.util.HashSet; +import java.util.Hashtable; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; + +import org.apache.aries.blueprint.BlueprintConstants; +import org.apache.aries.blueprint.ExtendedBlueprintContainer; +import org.apache.aries.blueprint.ServiceProcessor; +import org.apache.aries.blueprint.di.AbstractRecipe; +import org.apache.aries.blueprint.di.CollectionRecipe; +import org.apache.aries.blueprint.di.MapRecipe; +import org.apache.aries.blueprint.di.Recipe; +import org.apache.aries.blueprint.di.Repository; +import org.apache.aries.blueprint.utils.JavaUtils; +import org.apache.aries.blueprint.utils.ReflectionUtils; +import org.osgi.framework.Bundle; +import org.osgi.framework.Constants; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceReference; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.reflect.RefMetadata; +import org.osgi.service.blueprint.reflect.ServiceMetadata; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * A Recipe to export services into the OSGi registry. + * + * @version $Rev: 1071718 $, $Date: 2011-02-17 17:35:28 +0000 (Thu, 17 Feb 2011) $ + */ +public class ServiceRecipe extends AbstractRecipe { + + private static final Logger LOGGER = LoggerFactory.getLogger(ServiceRecipe.class); + + private final ExtendedBlueprintContainer blueprintContainer; + private final ServiceMetadata metadata; + private final Recipe serviceRecipe; + private final CollectionRecipe listenersRecipe; + private final MapRecipe propertiesRecipe; + private final List explicitDependencies; + + private Map properties; + private final AtomicBoolean registered = new AtomicBoolean(); + private final AtomicReference registration = new AtomicReference(); + private Map registrationProperties; + private List listeners; + private volatile Object service; + + public ServiceRecipe(String name, + ExtendedBlueprintContainer blueprintContainer, + ServiceMetadata metadata, + Recipe serviceRecipe, + CollectionRecipe listenersRecipe, + MapRecipe propertiesRecipe, + List explicitDependencies) { + super(name); + this.prototype = false; + this.blueprintContainer = blueprintContainer; + this.metadata = metadata; + this.serviceRecipe = serviceRecipe; + this.listenersRecipe = listenersRecipe; + this.propertiesRecipe = propertiesRecipe; + this.explicitDependencies = explicitDependencies; + } + + public Recipe getServiceRecipe() { + return serviceRecipe; + } + + public CollectionRecipe getListenersRecipe() { + return listenersRecipe; + } + + @Override + public List getConstructorDependencies() { + List recipes = new ArrayList(); + if (explicitDependencies != null) { + recipes.addAll(explicitDependencies); + } + return recipes; + } + + public List getDependencies() { + List recipes = new ArrayList(); + if (serviceRecipe != null) { + recipes.add(serviceRecipe); + } + if (listenersRecipe != null) { + recipes.add(listenersRecipe); + } + if (propertiesRecipe != null) { + recipes.add(propertiesRecipe); + } + recipes.addAll(getConstructorDependencies()); + return recipes; + } + + protected Object internalCreate() throws ComponentDefinitionException { + ServiceRegistrationProxy proxy = new ServiceRegistrationProxy(); + addPartialObject(proxy); + internalGetService(null, null); // null bundle means we don't want to retrieve the actual service when used with a ServiceFactory + return proxy; + } + + public boolean isRegistered() { + return registered.get(); + } + + public void register() { + int state = blueprintContainer.getBundleContext().getBundle().getState(); + if (state != Bundle.ACTIVE && state != Bundle.STARTING) { + return; + } + if (registered.compareAndSet(false, true)) { + createExplicitDependencies(); + + Hashtable props = new Hashtable(); + if (properties == null) { + properties = (Map) createRecipe(propertiesRecipe); + } + props.putAll(properties); + if (metadata.getRanking() == 0) { + props.remove(Constants.SERVICE_RANKING); + } else { + props.put(Constants.SERVICE_RANKING, metadata.getRanking()); + } + String componentName = getComponentName(); + if (componentName != null) { + props.put(BlueprintConstants.COMPONENT_NAME_PROPERTY, componentName); + } else { + props.remove(BlueprintConstants.COMPONENT_NAME_PROPERTY); + } + for (ServiceProcessor processor : blueprintContainer.getProcessors(ServiceProcessor.class)) { + processor.updateProperties(new PropertiesUpdater(), props); + } + + registrationProperties = props; + + Set classes = getClasses(); + String[] classArray = classes.toArray(new String[classes.size()]); + + LOGGER.debug("Registering service {} with interfaces {} and properties {}", + new Object[] { name, classes, props }); + + registration.set(blueprintContainer.registerService(classArray, new TriggerServiceFactory(), props)); + } + } + + public void unregister() { + if (registered.compareAndSet(true, false)) { + LOGGER.debug("Unregistering service {}", name); + // This method needs to allow reentrance, so if we need to make sure the registration is + // set to null before actually unregistering the service + ServiceRegistration reg = registration.get(); + if (listeners != null) { + LOGGER.debug("Calling listeners for service unregistration"); + for (ServiceListener listener : listeners) { + listener.unregister(service, registrationProperties); + } + } + if (reg != null) { + reg.unregister(); + } + + registration.compareAndSet(reg, null); + } + } + + protected ServiceReference getReference() { + ServiceRegistration reg = registration.get(); + if (reg == null) { + throw new IllegalStateException("Service is not registered"); + } else { + return reg.getReference(); + } + } + + protected void setProperties(Dictionary props) { + ServiceRegistration reg = registration.get(); + if (reg == null) { + throw new IllegalStateException("Service is not registered"); + } else { + reg.setProperties(props); + // TODO: set serviceProperties? convert somehow? should listeners be notified of this? + } + } + + + protected Object internalGetService() { + return internalGetService(blueprintContainer.getBundleContext().getBundle(), null); + } + + /** + * Create the service object. + * We need to synchronize the access to the repository, + * but not on this ServiceRecipe instance to avoid deadlock. + * When using internalCreate(), no other lock but the on the repository + * should be held. + * + * @param bundle + * @param registration + * @return + */ + private Object internalGetService(Bundle bundle, ServiceRegistration registration) { + LOGGER.debug("Retrieving service for bundle {} and service registration {}", bundle, registration); + if (this.service == null) { + synchronized (blueprintContainer.getRepository().getInstanceLock()) { + if (this.service == null) { + createService(); + } + } + } + + Object service = this.service; + // We need the real service ... + if (bundle != null) { + if (service instanceof ServiceFactory) { + service = ((ServiceFactory) service).getService(bundle, registration); + } + if (service == null) { + throw new IllegalStateException("service is null"); + } + // Check if the service actually implement all the requested interfaces + validateClasses(service); + // We're not really interested in the service, but perform some sanity checks nonetheless + } else { + if (!(service instanceof ServiceFactory)) { + // Check if the service actually implement all the requested interfaces + validateClasses(service); + } + } + + return service; + } + + private void createService() { + try { + LOGGER.debug("Creating service instance"); + service = createRecipe(serviceRecipe); + LOGGER.debug("Service created: {}", service); + // When the service is first requested, we need to create listeners and call them + if (listeners == null) { + LOGGER.debug("Creating listeners"); + if (listenersRecipe != null) { + listeners = (List) createRecipe(listenersRecipe); + } else { + listeners = Collections.emptyList(); + } + LOGGER.debug("Listeners created: {}", listeners); + if (registered.get()) { + LOGGER.debug("Calling listeners for initial service registration"); + for (ServiceListener listener : listeners) { + listener.register(service, registrationProperties); + } + } else { + LOGGER.debug("Calling listeners for initial service unregistration"); + for (ServiceListener listener : listeners) { + listener.unregister(service, registrationProperties); + } + } + } + } catch (RuntimeException e) { + LOGGER.error("Error retrieving service from " + this, e); + throw e; + } + } + + private void validateClasses(Object service) { + // Check if the service actually implement all the requested interfaces + if (metadata.getAutoExport() == ServiceMetadata.AUTO_EXPORT_DISABLED) { + Set allClasses = new HashSet(); + ReflectionUtils.getSuperClasses(allClasses, service.getClass()); + ReflectionUtils.getImplementedInterfaces(allClasses, service.getClass()); + Set classes = getClasses(); + classes.removeAll(allClasses); + if (!classes.isEmpty()) { + throw new ComponentDefinitionException("The service implementation does not implement the required interfaces: " + classes); + } + } + } + + public Object getService(Bundle bundle, ServiceRegistration registration) { + /** getService() can get called before registerService() returns with the registration object. + * So we need to set the registration object in case registration listeners call + * getServiceReference(). + */ + this.registration.compareAndSet(null, registration); + return internalGetService(bundle, registration); + } + + public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { + if (this.service instanceof ServiceFactory) { + ((ServiceFactory) this.service).ungetService(bundle, registration, service); + } + } + + private Set getClasses() { + Set classes; + switch (metadata.getAutoExport()) { + case ServiceMetadata.AUTO_EXPORT_INTERFACES: + classes = ReflectionUtils.getImplementedInterfaces(new HashSet(), internalGetService().getClass()); + break; + case ServiceMetadata.AUTO_EXPORT_CLASS_HIERARCHY: + classes = ReflectionUtils.getSuperClasses(new HashSet(), internalGetService().getClass()); + break; + case ServiceMetadata.AUTO_EXPORT_ALL_CLASSES: + classes = ReflectionUtils.getSuperClasses(new HashSet(), internalGetService().getClass()); + classes = ReflectionUtils.getImplementedInterfaces(classes, internalGetService().getClass()); + break; + default: + classes = new HashSet(metadata.getInterfaces()); + break; + } + return classes; + } + + private void createExplicitDependencies() { + if (explicitDependencies != null) { + for (Recipe recipe : explicitDependencies) { + createRecipe(recipe); + } + } + } + + private Object createRecipe(Recipe recipe) { + String name = recipe.getName(); + Repository repo = blueprintContainer.getRepository(); + if (repo.getRecipe(name) != recipe) { + repo.putRecipe(name, recipe); + } + return repo.create(name); + } + + private String getComponentName() { + if (metadata.getServiceComponent() instanceof RefMetadata) { + RefMetadata ref = (RefMetadata) metadata.getServiceComponent(); + return ref.getComponentId(); + } else { + return null; + } + } + + private class TriggerServiceFactory implements ServiceFactory { + + public Object getService(Bundle bundle, ServiceRegistration registration) { + return ServiceRecipe.this.getService(bundle, registration); + } + + public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) { + ServiceRecipe.this.ungetService(bundle, registration, service); + } + + } + + private class ServiceRegistrationProxy implements ServiceRegistration { + + public ServiceReference getReference() { + return ServiceRecipe.this.getReference(); + } + + public void setProperties(Dictionary properties) { + ServiceRecipe.this.setProperties(properties); + } + + public void unregister() { + throw new UnsupportedOperationException(); + } + } + + private class PropertiesUpdater implements ServiceProcessor.ServicePropertiesUpdater { + + public String getId() { + return metadata.getId(); + } + + public void updateProperties(Dictionary properties) { + Hashtable table = JavaUtils.getProperties(ServiceRecipe.this.getReference()); + JavaUtils.copy(table, properties); + ServiceRecipe.this.setProperties(table); + } + } + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/AbstractRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/AbstractRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/AbstractRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/AbstractRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,130 @@ +/** + * + * 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.di; + +import java.lang.reflect.Type; +import java.util.Collections; +import java.util.List; + +import org.apache.aries.blueprint.container.GenericType; +import org.osgi.service.blueprint.container.ReifiedType; +import org.osgi.service.blueprint.container.ComponentDefinitionException; + +public abstract class AbstractRecipe implements Recipe { + + protected final String name; + protected boolean prototype = true; + + protected AbstractRecipe(String name) { + if (name == null) throw new NullPointerException("name is null"); + this.name = name; + } + + public String getName() { + return name; + } + + public boolean isPrototype() { + return prototype; + } + + public void setPrototype(boolean prototype) { + this.prototype = prototype; + } + + public final Object create() throws ComponentDefinitionException { + // Ensure a container has been set + ExecutionContext context = ExecutionContext.Holder.getContext(); + + synchronized (context.getInstanceLock()) { + // if this recipe has already been executed in this container, return the currently registered value + Object obj = context.getPartialObject(name); + if (obj != null) { + return obj; + } + + // execute the recipe + context.push(this); + try { + obj = internalCreate(); + if (!prototype) { + context.addFullObject(name, obj); + } + return obj; + } finally { + Recipe popped = context.pop(); + if (popped != this) { + //noinspection ThrowFromFinallyBlock + throw new IllegalStateException("Internal Error: recipe stack is corrupt:" + + " Expected " + this + " to be popped of the stack but was " + popped); + } + } + } + } + + protected abstract Object internalCreate() throws ComponentDefinitionException; + + protected void addPartialObject(Object obj) { + if (!prototype) { + ExecutionContext.Holder.getContext().addPartialObject(name, obj); + } + } + + protected Object convert(Object obj, ReifiedType type) throws Exception { + return ExecutionContext.Holder.getContext().convert(obj, type); + } + + protected Object convert(Object obj, Type type) throws Exception { + return ExecutionContext.Holder.getContext().convert(obj, new GenericType(type)); + } + + protected Class loadClass(String className) { + ReifiedType t = loadType(className, null); + return t != null ? t.getRawClass() : null; + } + + protected ReifiedType loadType(String typeName) { + return loadType(typeName, null); + } + + protected ReifiedType loadType(String typeName, ClassLoader fromClassLoader) { + if (typeName == null) { + return null; + } + try { + return GenericType.parse(typeName, fromClassLoader != null ? fromClassLoader : ExecutionContext.Holder.getContext()); + } catch (ClassNotFoundException e) { + throw new ComponentDefinitionException("Unable to load class " + typeName + " from recipe " + this, e); + } + } + + public void destroy(Object instance) { + } + + public List getConstructorDependencies() { + return Collections.emptyList(); + } + + public String toString() { + return getClass().getSimpleName() + "[" + + "name='" + name + '\'' + + ']'; + + } + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ArrayRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ArrayRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ArrayRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ArrayRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,92 @@ +/** + * 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.di; + +import java.lang.reflect.Array; +import java.util.ArrayList; +import java.util.List; + +import org.osgi.service.blueprint.container.ComponentDefinitionException; +import org.osgi.service.blueprint.container.ReifiedType; + +/** + * @version $Rev: 820271 $ $Date: 2009-09-30 14:58:29 +0100 (Wed, 30 Sep 2009) $ + */ +public class ArrayRecipe extends AbstractRecipe { + + private final List list; + private final Object type; + + public ArrayRecipe(String name, Object type) { + super(name); + this.type = type; + this.list = new ArrayList(); + } + + public List getDependencies() { + List nestedRecipes = new ArrayList(list.size()); + for (Recipe recipe : list) { + if (recipe != null) { + nestedRecipes.add(recipe); + } + } + return nestedRecipes; + } + + protected Object internalCreate() throws ComponentDefinitionException { + ReifiedType type; + if (this.type instanceof Class) { + type = new ReifiedType((Class) this.type); + } else if (this.type instanceof String) { + type = loadType((String) this.type); + } else { + type = new ReifiedType(Object.class); + } + + // create array instance + Object array; + try { + array = Array.newInstance(type.getRawClass(), list.size()); + } catch (Exception e) { + throw new ComponentDefinitionException("Error while creating array instance: " + type); + } + + int index = 0; + for (Recipe recipe : list) { + Object value; + if (recipe != null) { + try { + value = convert(recipe.create(), type); + } catch (Exception e) { + throw new ComponentDefinitionException("Unable to convert value " + recipe + " to type " + type, e); + } + } else { + value = null; + } + + Array.set(array, index, value); + index++; + } + + return array; + } + + public void add(Recipe value) { + list.add(value); + } + +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CircularDependencyException.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CircularDependencyException.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CircularDependencyException.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CircularDependencyException.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,50 @@ +/** + * + * 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.di; + +import java.util.List; + +import org.osgi.service.blueprint.container.ComponentDefinitionException; + +public class CircularDependencyException extends ComponentDefinitionException { + private final List circularDependency; + + public CircularDependencyException(List circularDependency) { + super(circularDependency.toString()); + this.circularDependency = circularDependency; + } + + public CircularDependencyException(String message, List circularDependency) { + super(message + ": " + circularDependency); + this.circularDependency = circularDependency; + } + + public CircularDependencyException(String message, Throwable cause, List circularDependency) { + super(message + ": " + circularDependency, cause); + this.circularDependency = circularDependency; + } + + public CircularDependencyException(Throwable cause, List circularDependency) { + super(circularDependency.toString(), cause); + this.circularDependency = circularDependency; + } + + public List getCircularDependency() { + return circularDependency; + } +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CollectionRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CollectionRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CollectionRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/CollectionRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,111 @@ +/** + * 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.di; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Queue; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import org.apache.aries.blueprint.utils.ReflectionUtils; +import org.osgi.service.blueprint.container.ComponentDefinitionException; + +/** + * @version $Rev: 896324 $ $Date: 2010-01-06 06:05:04 +0000 (Wed, 06 Jan 2010) $ + */ +public class CollectionRecipe extends AbstractRecipe { + + private final List list; + private final Class typeClass; + + public CollectionRecipe(String name, Class type) { + super(name); + if (type == null) throw new NullPointerException("type is null"); + this.typeClass = type; + this.list = new ArrayList(); + } + + public List getDependencies() { + List nestedRecipes = new ArrayList(list.size()); + for (Recipe recipe : list) { + if (recipe != null) { + nestedRecipes.add(recipe); + } + } + return nestedRecipes; + } + + protected Object internalCreate() throws ComponentDefinitionException { + Class type = getCollection(typeClass); + + if (!ReflectionUtils.hasDefaultConstructor(type)) { + throw new ComponentDefinitionException("Type does not have a default constructor " + type.getName()); + } + + // create collection instance + Object o; + try { + o = type.newInstance(); + } catch (Exception e) { + throw new ComponentDefinitionException("Error while creating collection instance: " + type.getName()); + } + if (!(o instanceof Collection)) { + throw new ComponentDefinitionException("Specified collection type does not implement the Collection interface: " + type.getName()); + } + Collection instance = (Collection) o; + + for (Recipe recipe : list) { + Object value; + if (recipe != null) { + try { + value = recipe.create(); + } catch (Exception e) { + throw new ComponentDefinitionException("Unable to convert value " + recipe + " to type " + type, e); + } + } else { + value = null; + } + instance.add(value); + } + return instance; + } + + public void add(Recipe value) { + list.add(value); + } + + public static Class getCollection(Class type) { + if (ReflectionUtils.hasDefaultConstructor(type)) { + return type; + } else if (SortedSet.class.isAssignableFrom(type)) { + return TreeSet.class; + } else if (Set.class.isAssignableFrom(type)) { + return LinkedHashSet.class; + } else if (List.class.isAssignableFrom(type)) { + return ArrayList.class; + } else if (Queue.class.isAssignableFrom(type)) { + return LinkedList.class; + } else { + return ArrayList.class; + } + } +} Added: aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ComponentFactoryRecipe.java URL: http://svn.apache.org/viewvc/aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ComponentFactoryRecipe.java?rev=1075143&view=auto ============================================================================== --- aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ComponentFactoryRecipe.java (added) +++ aries/tags/blueprint-0.2.1/blueprint-core/src/main/java/org/apache/aries/blueprint/di/ComponentFactoryRecipe.java Sun Feb 27 20:50:38 2011 @@ -0,0 +1,63 @@ +/* + * 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.di; + +import java.util.List; + +import org.apache.aries.blueprint.ExtendedBlueprintContainer; +import org.apache.aries.blueprint.ext.ComponentFactoryMetadata; +import org.osgi.service.blueprint.container.ComponentDefinitionException; + +/** + * Pass-through recipe that allows custom bean manager (represented by a ComponentFactoryMetadata instance) + * to fit into the container lifecycle. + * + * @param + */ +public class ComponentFactoryRecipe extends AbstractRecipe { + private T metadata; + private List dependencies; + + public ComponentFactoryRecipe(String name, T metadata, + ExtendedBlueprintContainer container, List dependencies) { + super(name); + this.metadata = metadata; + this.dependencies = dependencies; + metadata.init(container); + } + + @Override + protected Object internalCreate() throws ComponentDefinitionException { + return metadata.create(); + } + + public List getDependencies() { + return dependencies; + } + + @Override + public void destroy(Object instance) { + metadata.destroy(instance); + } + + protected T getMetadata() { + return metadata; + } + +}