You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by ro...@apache.org on 2017/09/05 22:01:15 UTC

svn commit: r1807424 [4/8] - in /aries/trunk/cdi: ./ cdi-extender/ cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/ cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/bean/ cdi-extender/src/main/java/org/apache/aries/...

Added: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/reference/ReferenceModel.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/reference/ReferenceModel.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/reference/ReferenceModel.java (added)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/reference/ReferenceModel.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,864 @@
+/**
+ * Licensed 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.cdi.container.internal.reference;
+
+import static org.apache.aries.cdi.container.internal.model.Model.*;
+import static org.apache.aries.cdi.container.internal.util.Reflection.cast;
+import static java.lang.String.format;
+import static org.apache.aries.cdi.container.internal.model.Constants.CARDINALITY_ATTRIBUTE;
+import static org.apache.aries.cdi.container.internal.model.Constants.CDI10_URI;
+import static org.apache.aries.cdi.container.internal.model.Constants.NAME_ATTRIBUTE;
+import static org.apache.aries.cdi.container.internal.model.Constants.POLICY_ATTRIBUTE;
+import static org.apache.aries.cdi.container.internal.model.Constants.POLICY_OPTION_ATTRIBUTE;
+import static org.apache.aries.cdi.container.internal.model.Constants.SCOPE_ATTRIBUTE;
+import static org.apache.aries.cdi.container.internal.model.Constants.TARGET_ATTRIBUTE;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.WildcardType;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import javax.enterprise.inject.Instance;
+import javax.enterprise.inject.spi.Annotated;
+import javax.enterprise.inject.spi.AnnotatedField;
+import javax.enterprise.inject.spi.AnnotatedParameter;
+
+import org.apache.aries.cdi.container.internal.model.CollectionType;
+import org.apache.aries.cdi.container.internal.util.Conversions;
+import org.apache.aries.cdi.container.internal.util.Maps;
+import org.apache.aries.cdi.container.internal.util.Sets;
+import org.apache.aries.cdi.container.internal.util.Types;
+import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceObjects;
+import org.osgi.framework.ServiceReference;
+import org.osgi.service.cdi.annotations.Reference;
+import org.osgi.service.cdi.annotations.ReferenceCardinality;
+import org.osgi.service.cdi.annotations.ReferencePolicy;
+import org.osgi.service.cdi.annotations.ReferencePolicyOption;
+import org.osgi.service.cdi.annotations.ReferenceScope;
+import org.osgi.service.cdi.annotations.ServiceEvent;
+import org.osgi.util.converter.TypeReference;
+import org.xml.sax.Attributes;
+
+public class ReferenceModel {
+
+	public static class Builder {
+
+		public Builder(Attributes attributes) {
+			_cardinality = _cardinality(attributes);
+			_name = _name(attributes);
+			_option = _option(attributes);
+			_policy = _policy(attributes);
+			_scope = _scope(attributes);
+			_target = _target(attributes);
+		}
+
+		public Builder(Set<Annotation> qualifiers) {
+			_qualifiers = qualifiers;
+			Reference reference = getQualifier(_qualifiers, Reference.class);
+			if (reference != null) {
+				_cardinality = reference.cardinality();
+				_name = reference.name();
+				_option = reference.policyOption();
+				_policy = reference.policy();
+				_scope = reference.scope();
+				_service = reference.service();
+				_target = reference.target();
+			}
+		}
+
+		public ReferenceModel build() {
+			if ((_annotated == null) && (_service == null)) {
+				throw new IllegalArgumentException(
+					"Either injectionPoint or service must be set!");
+			}
+
+			if (_annotated == null) {
+				_annotated = new ReferenceAnnotated(_service);
+			}
+
+			Type type = upwrapCDITypes(_annotated.getBaseType());
+
+			_policy = calculatePolicy(_policy);
+			_option = calculatePolicyOption(_option);
+			_scope = calculateScope(_scope);
+			Multiplicity multiplicity = calculateMultiplicity(_annotated.getBaseType());// we need the pure type to check "Instance"
+			_cardinality = calculateCardinality(_cardinality, multiplicity, type);
+			CollectionType collectionType = calculateCollectionType(type);
+			Class<?> beanClass = calculateBeanClass(type);
+			_service = calculateServiceClass(_service, _cardinality, collectionType, type, _annotated);
+			_name = calculateName(_name, _service, _annotated);
+
+			return new ReferenceModel(_service, _cardinality, _name, _policy, _option, _target, _scope, _qualifiers, beanClass, type, collectionType);
+		}
+
+		public Builder cardinality(ReferenceCardinality cardinality) {
+			_cardinality = cardinality;
+			return this;
+		}
+
+		public Builder annotated(Annotated annotated) {
+			_annotated = annotated;
+			return this;
+		}
+
+		public Builder name(String name) {
+			_name = name;
+			return this;
+		}
+
+		public Builder option(ReferencePolicyOption option) {
+			_option = option;
+			return this;
+		}
+
+		public Builder policy(ReferencePolicy policy) {
+			_policy = policy;
+			return this;
+		}
+
+		public Builder scope(ReferenceScope scope) {
+			_scope = scope;
+			return this;
+		}
+
+		public Builder service(Class<?> service) {
+			_service = service;
+			return this;
+		}
+
+		public Builder target(String target) {
+			_target = target;
+			return this;
+		}
+
+		@SuppressWarnings("unchecked")
+		private static <T extends Annotation> T getQualifier(
+			Set<Annotation> qualifiers, Class<T> clazz) {
+			for (Annotation annotation : qualifiers) {
+				if (clazz.isAssignableFrom(annotation.annotationType())) {
+					return (T)annotation;
+				}
+			}
+			return null;
+		}
+
+		private ReferenceCardinality _cardinality;
+		private Annotated _annotated;
+		private String _name;
+		private ReferencePolicyOption _option;
+		private ReferencePolicy _policy;
+		private Set<Annotation> _qualifiers;
+		private ReferenceScope _scope;
+		private Class<?> _service;
+		private String _target;
+
+	}
+
+	private ReferenceModel(
+		Class<?> service,
+		ReferenceCardinality cardinality,
+		String name,
+		ReferencePolicy policy,
+		ReferencePolicyOption option,
+		String target,
+		ReferenceScope scope,
+		Set<Annotation> qualifiers,
+		Class<?> beanClass,
+		Type injectionPointType,
+		CollectionType collectionType) {
+
+		_service = service;
+		_cardinality = cardinality;
+		_name = name;
+		_policy = policy;
+		_option = option;
+		_target = target;
+		_scope = scope;
+		_qualifiers = new LinkedHashSet<>();
+		if (qualifiers != null) {
+			_qualifiers.addAll(qualifiers);
+		}
+		_beanClass = beanClass;
+		_injectionPointType = injectionPointType;
+		_collectionType = collectionType;
+
+		_types = Sets.immutableHashSet(_injectionPointType, Object.class);
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result + ((_beanClass == null) ? 0 : _beanClass.hashCode());
+		result = prime * result + ((_cardinality == null) ? 0 : _cardinality.hashCode());
+		result = prime * result + ((_name == null) ? 0 : _name.hashCode());
+		result = prime * result + ((_option == null) ? 0 : _option.hashCode());
+		result = prime * result + ((_policy == null) ? 0 : _policy.hashCode());
+		result = prime * result + ((_scope == null) ? 0 : _scope.hashCode());
+		result = prime * result + ((_service == null) ? 0 : _service.hashCode());
+		result = prime * result + ((_target == null) ? 0 : _target.hashCode());
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		ReferenceModel other = (ReferenceModel) obj;
+		if (_beanClass == null) {
+			if (other._beanClass != null)
+				return false;
+		} else if (!_beanClass.equals(other._beanClass))
+			return false;
+		if (_cardinality != other._cardinality)
+			return false;
+		if (_name == null) {
+			if (other._name != null)
+				return false;
+		} else if (!_name.equals(other._name))
+			return false;
+		if (_option != other._option)
+			return false;
+		if (_policy != other._policy)
+			return false;
+		if (_scope != other._scope)
+			return false;
+		if (_service == null) {
+			if (other._service != null)
+				return false;
+		} else if (!_service.equals(other._service))
+			return false;
+		if (_target == null) {
+			if (other._target != null)
+				return false;
+		} else if (!_target.equals(other._target))
+			return false;
+		return true;
+	}
+
+	public boolean found() {
+		return _found.get();
+	}
+
+	public void found(boolean found) {
+		_found.set(found);
+	}
+
+	public Class<?> getBeanClass() {
+		return _beanClass;
+	}
+
+	public ReferenceCardinality getCardinality() {
+		return _cardinality;
+	}
+
+	public CollectionType getCollectionType() {
+		return _collectionType;
+	}
+
+	public Type getInjectionPointType() {
+		return _injectionPointType;
+	}
+
+	public String getName() {
+		return _name;
+	}
+
+	public ReferencePolicy getPolicy() {
+		return _policy;
+	}
+
+	public ReferencePolicyOption getPolicyOption() {
+		return _option;
+	}
+
+	public Set<Annotation> getQualifiers() {
+		return _qualifiers;
+	}
+
+	public ReferenceScope getScope() {
+		return _scope;
+	}
+
+	public Class<?> getServiceClass() {
+		return _service;
+	}
+
+	public String getTarget() {
+		return _target;
+	}
+
+	public Set<Type> getTypes() {
+		return _types;
+	}
+
+	public void setQualifiers(Set<Annotation> qualifiers) {
+		_qualifiers.clear();
+		_qualifiers.addAll(qualifiers);
+	}
+
+	@Override
+	public String toString() {
+		if (_string == null) {
+			_string = String.format("reference[name='%s', service='%s', scope='%s', target='%s']", _name, _service, _scope, _target);
+		}
+		return _string;
+	}
+
+	public static String buildFilter(
+			Class<?> serviceType,
+			String target,
+			ReferenceScope scope,
+			Set<Annotation> qualifiers)
+		throws InvalidSyntaxException {
+
+		StringBuilder sb = new StringBuilder();
+
+		sb.append("(&(");
+		sb.append(Constants.OBJECTCLASS);
+		sb.append("=");
+		sb.append(serviceType.getName());
+		sb.append(")");
+
+		if (scope == ReferenceScope.PROTOTYPE) {
+			sb.append("(");
+			sb.append(Constants.SERVICE_SCOPE);
+			sb.append("=");
+			sb.append(Constants.SCOPE_PROTOTYPE);
+			sb.append(")");
+		}
+		else if (scope == ReferenceScope.SINGLETON) {
+			sb.append("(");
+			sb.append(Constants.SERVICE_SCOPE);
+			sb.append("=");
+			sb.append(Constants.SCOPE_SINGLETON);
+			sb.append(")");
+		}
+		else if (scope == ReferenceScope.BUNDLE) {
+			sb.append("(");
+			sb.append(Constants.SERVICE_SCOPE);
+			sb.append("=");
+			sb.append(Constants.SCOPE_BUNDLE);
+			sb.append(")");
+		}
+
+		String targetFilter = target == null ? "" : target;
+
+		int targetFilterLength = targetFilter.length();
+
+		if (targetFilterLength > 0) {
+			FrameworkUtil.createFilter(targetFilter);
+
+			sb.append(targetFilter);
+		}
+
+		if (qualifiers != null) {
+			for (Annotation qualifier : qualifiers) {
+				Class<? extends Annotation> annotationType = qualifier.annotationType();
+
+				if (annotationType.equals(Reference.class)) {
+					continue;
+				}
+
+				Map<String, String> map = Conversions.convert(qualifier).sourceAs(qualifier.annotationType()).to(_mapType);
+
+				Maps.appendFilter(sb, map);
+			}
+		}
+
+		sb.append(")");
+
+		return sb.toString();
+	}
+
+	private static Class<?> calculateBeanClass(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType pType = (ParameterizedType)type;
+
+			type = pType.getRawType();
+		}
+		else if (type instanceof WildcardType) {
+			throw new IllegalArgumentException(
+				"Cannot use a wildcard as the bean: " + type);
+		}
+
+		return cast(type);
+	}
+
+	private static CollectionType calculateCollectionType(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType parameterizedType = cast(type);
+
+			Type rawType = parameterizedType.getRawType();
+
+			if ((List.class == cast(rawType)) ||
+				Collection.class.isAssignableFrom(cast(rawType))) {
+
+				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+				return calculateCollectionType(actualTypeArguments[0]);
+			}
+			else if (Map.class == cast(rawType)) {
+				Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+				Type first = actualTypeArguments[0];
+				Type second = actualTypeArguments[1];
+
+				if (!(first instanceof ParameterizedType) &&
+					String.class.isAssignableFrom(cast(first))) {
+
+					if ((!(second instanceof ParameterizedType) && (second == Object.class)) ||
+						(second instanceof WildcardType)) {
+
+						return CollectionType.PROPERTIES;
+					}
+				}
+			}
+			else if (Map.Entry.class == cast(rawType)) {
+				return CollectionType.TUPLE;
+			}
+			else if (ServiceObjects.class == cast(rawType)) {
+				return CollectionType.SERVICEOBJECTS;
+			}
+			else if (ServiceReference.class == cast(rawType)) {
+				return CollectionType.REFERENCE;
+			}
+		}
+		else if (Map.Entry.class == cast(type)) {
+			return CollectionType.TUPLE;
+		}
+		else if (ServiceObjects.class == cast(type)) {
+			return CollectionType.SERVICEOBJECTS;
+		}
+		else if (ServiceReference.class == cast(type)) {
+			return CollectionType.REFERENCE;
+		}
+
+		return CollectionType.SERVICE;
+	}
+
+	private static ReferenceCardinality calculateCardinality(
+		ReferenceCardinality cardinality, Multiplicity multiplicity, Type type) {
+
+		if ((multiplicity == Multiplicity.UNARY) &&
+			((cardinality == ReferenceCardinality.AT_LEAST_ONE) || (cardinality == ReferenceCardinality.MULTIPLE))) {
+
+			throw new IllegalArgumentException(
+				format(
+					"Unary injection point type %s cannot be defined by multiple cardinality %s",
+					type, cardinality));
+		}
+		else if ((multiplicity == Multiplicity.MULTIPLE) &&
+				((cardinality == ReferenceCardinality.OPTIONAL) || (cardinality == ReferenceCardinality.MANDATORY))) {
+
+			throw new IllegalArgumentException(
+				format(
+					"Multiple injection point type %s cannot be defined by unary cardinality %s",
+					type, cardinality));
+		}
+
+		if ((cardinality == null) || (cardinality == ReferenceCardinality.DEFAULT)) {
+			switch(multiplicity) {
+				case MULTIPLE:
+					return ReferenceCardinality.MULTIPLE;
+				case UNARY:
+					return ReferenceCardinality.MANDATORY;
+			}
+		}
+
+		return cardinality;
+	}
+
+	private static Multiplicity calculateMultiplicity(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType parameterizedType = cast(type);
+
+			Type rawType = parameterizedType.getRawType();
+
+			if ((Instance.class == cast(rawType)) ||
+				Collection.class.isAssignableFrom(cast(rawType)) ||
+				ServiceEvent.class == cast(rawType)) {
+
+				return Multiplicity.MULTIPLE;
+			}
+		}
+
+		return Multiplicity.UNARY;
+	}
+
+	public static String calculateName(String name, Class<?> service, Annotated annotated) {
+		if ((name != null) && (name.length() > 0)) {
+			return name;
+		}
+
+		if (annotated != null) {
+			if (annotated instanceof AnnotatedParameter) {
+				AnnotatedParameter<?> annotatedParameter = (AnnotatedParameter<?>)annotated;
+
+				return Types.getName(service) + annotatedParameter.getPosition();
+			}
+			if (annotated instanceof AnnotatedField) {
+				AnnotatedField<?> annotatedField = (AnnotatedField<?>)annotated;
+
+				return annotatedField.getJavaMember().getName();
+			}
+		}
+
+		return Types.getName(service);
+	}
+
+	private static ReferencePolicy calculatePolicy(ReferencePolicy policy) {
+		if ((policy == null) || (policy == ReferencePolicy.DEFAULT)) {
+			return ReferencePolicy.STATIC;
+		}
+
+		return policy;
+	}
+
+	private static ReferencePolicyOption calculatePolicyOption(ReferencePolicyOption option) {
+		if ((option == null) || (option == ReferencePolicyOption.DEFAULT)) {
+			return ReferencePolicyOption.RELUCTANT;
+		}
+
+		return option;
+	}
+
+	private static ReferenceScope calculateScope(ReferenceScope scope) {
+		if ((scope == null) || (scope == ReferenceScope.DEFAULT)) {
+			return ReferenceScope.DEFAULT;
+		}
+
+		return scope;
+	}
+
+	private static Class<?> calculateServiceClass(Type injectionPointType) {
+		Type type = injectionPointType;
+
+		if (!(type instanceof ParameterizedType)) {
+			return cast(type);
+		}
+
+		ParameterizedType parameterizedType = cast(type);
+
+		Type rawType = parameterizedType.getRawType();
+
+		if ((List.class == cast(rawType)) ||
+			Collection.class.isAssignableFrom(cast(rawType))) {
+
+			Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+			type = actualTypeArguments[0];
+
+			if (type instanceof ParameterizedType) {
+				parameterizedType = (ParameterizedType)type;
+
+				rawType = parameterizedType.getRawType();
+			}
+			else if ((type instanceof WildcardType) ||
+					Map.Entry.class.isAssignableFrom(cast(type))) {
+
+				return null;
+			}
+			else {
+				rawType = type;
+			}
+		}
+
+		if (!Map.Entry.class.isAssignableFrom(cast(rawType)) &&
+			!ServiceObjects.class.isAssignableFrom(cast(rawType)) &&
+			!ServiceReference.class.isAssignableFrom(cast(rawType))) {
+
+			return cast(rawType);
+		}
+
+		Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
+
+		Type argument = actualTypeArguments[0];
+
+		if (Map.Entry.class.isAssignableFrom(cast(rawType))) {
+			if (!checkKey(argument)) {
+				throw new IllegalArgumentException(
+					"Tuples must have a key of type Map<String, [? or Object]>: " + argument);
+			}
+
+			argument = actualTypeArguments[1];
+		}
+
+		if (argument instanceof ParameterizedType) {
+			ParameterizedType parameterizedType1 = cast(argument);
+
+			return cast(parameterizedType1.getRawType());
+		}
+		else if (argument instanceof WildcardType) {
+			WildcardType wildcardType = (WildcardType)argument;
+
+			if ((wildcardType.getUpperBounds().length == 1)) {
+				argument = wildcardType.getUpperBounds()[0];
+
+				if (Object.class.equals(argument)) {
+					return null;
+				}
+				else if (argument instanceof Class) {
+					return cast(argument);
+				}
+			}
+
+			throw new IllegalArgumentException(
+				"@Reference cannot use nested parameterized types or multiple upper bounds: " + injectionPointType);
+		}
+
+		return cast(argument);
+	}
+
+	private static Class<?> calculateServiceClass(
+		Class<?> service, ReferenceCardinality cardinality, CollectionType collectionType, Type injectionPointType, Annotated annotated) {
+
+		Class<?> calculatedServiceClass = calculateServiceClass(injectionPointType);
+
+		if ((service == null) || (service == Object.class)) {
+			if (calculatedServiceClass == null) {
+				throw new IllegalArgumentException(
+					"Could not determine the service type from @Reference on annotated " +
+						annotated);
+			}
+
+			switch(collectionType) {
+				case PROPERTIES:
+					if (calculatedServiceClass == Map.class) {
+						throw new IllegalArgumentException(
+							"A @Reference cannot bind service properties to a Map<String, Object> without " +
+								"specifying the @Reference.service property: " + annotated);
+					}
+					break;
+				case REFERENCE:
+					if (calculatedServiceClass == ServiceReference.class) {
+						throw new IllegalArgumentException(
+							"A @Reference cannot bind a ServiceReference without specifying either the " +
+								"@Reference.service property or a generic type argument (e.g. ServiceReference<Foo>: " +
+									annotated);
+					}
+					break;
+				case SERVICEOBJECTS:
+					if	(calculatedServiceClass == ServiceObjects.class) {
+						throw new IllegalArgumentException(
+							"A @Reference cannot bind a ServiceObjects without specifying either the " +
+								"@Reference.service property or a generic type argument (e.g. ServiceObjects<Foo>: " +
+									annotated);
+					}
+					break;
+				case TUPLE:
+					if (calculatedServiceClass == Map.Entry.class) {
+						throw new IllegalArgumentException(
+							"A @Reference cannot bind a Map.Entry without specifying either the " +
+								"@Reference.service property or a generic type argument (e.g. Map.Entry<Map<String, Object>, Foo>: " +
+									annotated);
+					}
+					break;
+				default:
+			}
+
+			return calculatedServiceClass;
+		}
+
+		switch(collectionType) {
+			case PROPERTIES:
+				if (Map.class.isAssignableFrom(calculatedServiceClass)) {
+					return service;
+				}
+				break;
+			case REFERENCE:
+				if ((calculatedServiceClass == null) ||
+					ServiceReference.class.isAssignableFrom(calculatedServiceClass)) {
+					return service;
+				}
+				break;
+			case SERVICEOBJECTS:
+				if ((calculatedServiceClass == null) ||
+					ServiceObjects.class.isAssignableFrom(calculatedServiceClass)) {
+					return service;
+				}
+				break;
+			case TUPLE:
+				if ((calculatedServiceClass != null) &&
+					Map.Entry.class.isAssignableFrom(calculatedServiceClass)) {
+
+					if (!checkKey(calculatedServiceClass)) {
+						throw new IllegalArgumentException(
+							"Tuples must have a key of type Map<String, [? or Object]>: " + calculatedServiceClass);
+					}
+
+					return service;
+				}
+				else if ((calculatedServiceClass == null) ||
+					calculatedServiceClass.isAssignableFrom(service)) {
+
+					return service;
+				}
+				break;
+			case SERVICE:
+				if (((calculatedServiceClass == null) &&
+						((cardinality == ReferenceCardinality.MULTIPLE) ||
+						(cardinality == ReferenceCardinality.AT_LEAST_ONE))) ||
+					((calculatedServiceClass != null) &&
+						calculatedServiceClass.isAssignableFrom(service))) {
+					return service;
+				}
+		}
+
+		throw new IllegalArgumentException(
+			"@Reference.service " + service + " is not compatible with annotated " + annotated);
+	}
+
+	// check the key type to make sure it complies with Map<String, ?> OR Map<String, Object>
+	private static boolean checkKey(Type mapEntryType) {
+		if (!(mapEntryType instanceof ParameterizedType)) {
+			return false;
+		}
+
+		ParameterizedType parameterizedKeyType = (ParameterizedType)mapEntryType;
+
+		if ((!Map.class.isAssignableFrom(cast(parameterizedKeyType.getRawType()))) ||
+			(!parameterizedKeyType.getActualTypeArguments()[0].equals(String.class))) {
+
+			return false;
+		}
+
+		Type valueType = parameterizedKeyType.getActualTypeArguments()[1];
+
+		if ((!valueType.equals(Object.class) &&
+			(
+				(!(valueType instanceof WildcardType)) ||
+				(((WildcardType)valueType).getUpperBounds().length != 1) ||
+				(!((WildcardType)valueType).getUpperBounds()[0].equals(Object.class))))) {
+
+			return false;
+		}
+
+		return true;
+	}
+
+	private static ReferenceCardinality _cardinality(Attributes attributes) {
+		return ReferenceCardinality.get(
+			getValue(CDI10_URI, CARDINALITY_ATTRIBUTE, attributes, ReferenceCardinality.DEFAULT.toString()));
+	}
+
+	private static String _name(Attributes attributes) {
+		return getValue(CDI10_URI, NAME_ATTRIBUTE, attributes);
+	}
+
+	private static ReferencePolicyOption _option(Attributes attributes) {
+		return ReferencePolicyOption.get(
+			getValue(CDI10_URI, POLICY_OPTION_ATTRIBUTE, attributes, ReferencePolicyOption.DEFAULT.toString()));
+	}
+
+	private static ReferencePolicy _policy(Attributes attributes) {
+		return ReferencePolicy.get(
+			getValue(CDI10_URI, POLICY_ATTRIBUTE, attributes, ReferencePolicy.DEFAULT.toString()));
+	}
+
+	private static ReferenceScope _scope(Attributes attributes) {
+		return ReferenceScope.get(getValue(
+			CDI10_URI, SCOPE_ATTRIBUTE, attributes, ReferenceScope.DEFAULT.toString()));
+	}
+
+	private static String _target(Attributes attributes) {
+		return getValue(CDI10_URI, TARGET_ATTRIBUTE, attributes);
+	}
+
+	private static Type upwrapCDITypes(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType pType = (ParameterizedType)type;
+
+			Type rawType = pType.getRawType();
+
+			if (Instance.class == cast(rawType) ||
+				ServiceEvent.class == cast(rawType)) {
+
+				type = pType.getActualTypeArguments()[0];
+			}
+		}
+
+		return type;
+	}
+
+	private static final TypeReference<Map<String, String>> _mapType = new TypeReference<Map<String, String>>(){};
+
+	private final Class<?> _beanClass;
+	private final ReferenceCardinality _cardinality;
+	private final CollectionType _collectionType;
+	private final AtomicBoolean _found = new AtomicBoolean();
+	private final Type _injectionPointType;
+	private final String _name;
+	private final ReferencePolicyOption _option;
+	private final ReferencePolicy _policy;
+	private final Set<Annotation> _qualifiers;
+	private final ReferenceScope _scope;
+	private final Class<?> _service;
+	private String _string;
+	private final String _target;
+	private final Set<Type> _types;
+
+	private static class ReferenceAnnotated implements Annotated {
+
+		public ReferenceAnnotated(Class<?> service) {
+			_service = service;
+		}
+
+		@Override
+		public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
+			return null;
+		}
+
+		@Override
+		public Set<Annotation> getAnnotations() {
+			return Collections.emptySet();
+		}
+
+		@Override
+		public Type getBaseType() {
+			return _service;
+		}
+
+		@Override
+		public Set<Type> getTypeClosure() {
+			return Sets.hashSet(_service);
+		}
+
+		@Override
+		public boolean isAnnotationPresent(Class<? extends Annotation> annotationType) {
+			return false;
+		}
+
+		private final Class<?> _service;
+
+	}
+
+}

Added: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/service/ServiceDeclaration.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/service/ServiceDeclaration.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/service/ServiceDeclaration.java (added)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/service/ServiceDeclaration.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,173 @@
+/**
+ * Licensed 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.cdi.container.internal.service;
+
+import static org.apache.aries.cdi.container.internal.util.Reflection.cast;
+
+import java.util.Arrays;
+import java.util.Dictionary;
+import java.util.stream.Collectors;
+
+import javax.enterprise.context.spi.CreationalContext;
+import javax.enterprise.inject.spi.Bean;
+
+import org.apache.aries.cdi.container.internal.component.ComponentModel;
+import org.apache.aries.cdi.container.internal.component.ComponentProperties;
+import org.apache.aries.cdi.container.internal.container.ContainerState;
+import org.apache.aries.cdi.container.internal.util.Types;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.PrototypeServiceFactory;
+import org.osgi.framework.ServiceFactory;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.cdi.annotations.ServiceScope;
+
+public class ServiceDeclaration {
+
+	public ServiceDeclaration(
+		ContainerState containerState,
+		ComponentModel componentModel,
+		Bean<?> bean,
+		CreationalContext<?> creationalContext) {
+
+		_containerState = containerState;
+		_bean = bean;
+		_creationalContext = creationalContext;
+
+		_componentModel = componentModel;
+
+		Object instance = null;
+
+		if (_componentModel.getServiceScope() == ServiceScope.SINGLETON) {
+			instance = new SingletonScopeWrapper();
+		}
+		else if (_componentModel.getServiceScope() == ServiceScope.BUNDLE) {
+			instance = new BundleScopeWrapper();
+		}
+		else if (_componentModel.getServiceScope() == ServiceScope.PROTOTYPE) {
+			instance = new PrototypeScopeWrapper();
+		}
+
+		_instance = instance;
+	}
+
+	public String[] getClassNames() {
+		return Arrays.stream(
+			Types.types(_componentModel, _componentModel.getBeanClass(), _containerState.classLoader())
+		).map(
+			c -> c.getName()
+		).collect(
+			Collectors.toList()
+		).toArray(
+			new String[0]
+		);
+	}
+
+	public String getName() {
+		return _componentModel.getName();
+	}
+
+	public ServiceScope getScope() {
+		return _componentModel.getServiceScope();
+	}
+
+	public Object getServiceInstance() {
+		return _instance;
+	}
+
+	public Dictionary<String, ?> getServiceProperties() {
+		return new ComponentProperties().bean(
+			_bean
+		).componentModel(
+			_componentModel
+		).containerState(
+			_containerState
+		).build();
+	}
+
+	private final Bean<?> _bean;
+	private final ComponentModel _componentModel;
+	private final ContainerState _containerState;
+	private final CreationalContext<?> _creationalContext;
+	private final Object _instance;
+
+	@SuppressWarnings({"rawtypes"})
+	private class BundleScopeWrapper implements ServiceFactory {
+
+		@Override
+		public Object getService(Bundle bundle, ServiceRegistration registration) {
+			return _bean.create(cast(_creationalContext));
+		}
+
+		@Override
+		public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+			_bean.destroy(cast(service), cast(_creationalContext));
+		}
+
+	}
+
+	@SuppressWarnings({"rawtypes"})
+	private class PrototypeScopeWrapper implements PrototypeServiceFactory {
+
+		@Override
+		public Object getService(Bundle bundle, ServiceRegistration registration) {
+			return _bean.create(cast(_creationalContext));
+		}
+
+		@Override
+		public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+			_bean.destroy(cast(service), cast(_creationalContext));
+		}
+
+	}
+
+	@SuppressWarnings({"rawtypes"})
+	private class SingletonScopeWrapper implements ServiceFactory {
+
+		@Override
+		public Object getService(Bundle bundle, ServiceRegistration registration) {
+			if (_instance == null) {
+				_instance = _bean.create(cast(_creationalContext));
+			}
+			return _instance;
+		}
+
+		@Override
+		public void ungetService(Bundle bundle, ServiceRegistration registration, Object service) {
+		}
+
+		private Object _instance;
+
+	}
+
+//	private class ManagedServiceFactoryWrapper implements ManagedServiceFactory {
+//
+//		@Override
+//		public String getName() {
+//			return _component.name();
+//		}
+//
+//		@Override
+//		public void updated(String pid, Dictionary<String, ?> properties) throws ConfigurationException {
+//		}
+//
+//		@Override
+//		public void deleted(String pid) {
+//		}
+//
+//		private final Map<String, Object> _instances = new ConcurrentHashMap<>();
+//
+//	}
+
+}

Modified: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Conversions.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Conversions.java?rev=1807424&r1=1807423&r2=1807424&view=diff
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Conversions.java (original)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Conversions.java Tue Sep  5 22:01:11 2017
@@ -18,6 +18,7 @@ import java.util.Arrays;
 
 import org.osgi.util.converter.Converter;
 import org.osgi.util.converter.ConverterBuilder;
+import org.osgi.util.converter.Converting;
 import org.osgi.util.converter.StandardConverter;
 import org.osgi.util.converter.TypeRule;
 
@@ -27,8 +28,8 @@ public class Conversions {
 		return INSTANCE._converter.convert(object).defaultValue("").to(String.class);
 	}
 
-	public static Converter c() {
-		return INSTANCE._converter;
+	public static Converting convert(Object object) {
+		return INSTANCE._converter.convert(object);
 	}
 
 	private Conversions() {

Modified: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Maps.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Maps.java?rev=1807424&r1=1807423&r2=1807424&view=diff
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Maps.java (original)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Maps.java Tue Sep  5 22:01:11 2017
@@ -70,20 +70,21 @@ public class Maps {
 		map(map, key, type, value);
 	}
 
+	@SuppressWarnings("unchecked")
 	static void map(Map<String, Object> map, String key, String type, String value) {
 		PropertyType propertyType = PropertyType.find(type);
 
 		Object object = map.get(key);
 
 		if (object == null) {
-			Object valueObject = Conversions.c().convert(value).to(propertyType.getType());
+			Object valueObject = Conversions.convert(value).to(propertyType.getType());
 
 			map.put(key, valueObject);
 
 			return;
 		}
 
-		Object valueObject = Conversions.c().convert(value).to(propertyType.componentType());
+		Object valueObject = Conversions.convert(value).to(propertyType.componentType());
 
 		if (propertyType.isRaw()) {
 			if (!object.getClass().isArray()) {
@@ -101,10 +102,12 @@ public class Maps {
 			}
 		}
 		else if (propertyType.isList()) {
+			@SuppressWarnings("rawtypes")
 			List list = Collections.checkedList((List)object, propertyType.componentType());
 			list.add(valueObject);
 		}
 		else if (propertyType.isSet()) {
+			@SuppressWarnings("rawtypes")
 			Set set = Collections.checkedSet((Set)object, propertyType.componentType());
 			set.add(valueObject);
 		}

Modified: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Strings.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Strings.java?rev=1807424&r1=1807423&r2=1807424&view=diff
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Strings.java (original)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Strings.java Tue Sep  5 22:01:11 2017
@@ -19,6 +19,8 @@ import java.util.regex.Pattern;
 
 public class Strings {
 
+	public static final String[] EMPTY_ARRAY = new String[0];
+
 	private Strings() {
 		// no instances
 	}

Added: aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Types.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Types.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Types.java (added)
+++ aries/trunk/cdi/cdi-extender/src/main/java/org/apache/aries/cdi/container/internal/util/Types.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,134 @@
+/**
+ * Licensed 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.cdi.container.internal.util;
+
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.enterprise.inject.spi.InjectionPoint;
+
+import org.apache.aries.cdi.container.internal.component.ComponentModel;
+import org.osgi.service.cdi.annotations.ServiceScope;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class Types {
+
+	public static String getName(InjectionPoint injectionPoint) {
+		return getName(injectionPoint.getType());
+	}
+
+	public static String getName(Type type) {
+		if (type instanceof ParameterizedType) {
+			ParameterizedType pt = (ParameterizedType)type;
+
+			Type rawType = pt.getRawType();
+
+			if (rawType instanceof Class) {
+				Class<?> clazz = (Class<?>)rawType;
+
+				return clazz.getSimpleName();
+			}
+			else {
+				return rawType.getTypeName();
+			}
+		}
+		else if (type instanceof GenericArrayType) {
+			GenericArrayType gat = (GenericArrayType)type;
+
+			Type genericComponentType = gat.getGenericComponentType();
+
+			if (genericComponentType instanceof ParameterizedType) {
+				ParameterizedType pt = (ParameterizedType)genericComponentType;
+
+				Type rawType = pt.getRawType();
+
+				if (rawType instanceof Class) {
+					Class<?> clazz = (Class<?>)rawType;
+
+					return clazz.getSimpleName();
+				}
+				else {
+					return rawType.getTypeName();
+				}
+			}
+			else if (genericComponentType instanceof Class) {
+				Class<?> clazz = (Class<?>)genericComponentType;
+
+				return clazz.getSimpleName();
+			}
+			else {
+				return genericComponentType.getTypeName();
+			}
+		}
+		else if (type instanceof Class) {
+			Class<?> clazz = (Class<?>)type;
+
+			String simpleName = clazz.getSimpleName();
+
+			char lowerCase = Character.toLowerCase(simpleName.charAt(0));
+
+			return lowerCase + simpleName.substring(1, simpleName.length());
+		}
+
+		return type.getTypeName();
+	}
+
+	public static Class<?>[] types(
+		ComponentModel componentModel, Class<?> beanClass, ClassLoader classLoader) {
+
+		List<Class<?>> classes = new ArrayList<>();
+
+		if ((componentModel.getServiceScope() == ServiceScope.DEFAULT) ||
+			(componentModel.getServiceScope() == ServiceScope.NONE)) {
+
+			return new Class<?>[0];
+		}
+		else if (!componentModel.getProvides().isEmpty()) {
+			for (String provide : componentModel.getProvides()) {
+				try {
+					classes.add(classLoader.loadClass(provide));
+				}
+				catch (ReflectiveOperationException roe) {
+					if (_log.isWarnEnabled()) {
+						_log.warn(
+							"CDIe - component {} cannot load provided type {}. Skipping!",
+							componentModel.getBeanClass(), provide, roe);
+					}
+				}
+			}
+		}
+		else {
+			Class<?>[] interfaces = beanClass.getInterfaces();
+
+			if (interfaces.length > 0) {
+				for (Class<?> iface : interfaces) {
+					classes.add(iface);
+				}
+			}
+			else {
+				classes.add(beanClass);
+			}
+		}
+
+		return classes.toArray(new Class[0]);
+	}
+
+	public static final Logger _log = LoggerFactory.getLogger(Types.class);
+
+}
\ No newline at end of file

Modified: aries/trunk/cdi/cdi-extender/src/main/resources/META-INF/cdi.xsd
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/main/resources/META-INF/cdi.xsd?rev=1807424&r1=1807423&r2=1807424&view=diff
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/main/resources/META-INF/cdi.xsd (original)
+++ aries/trunk/cdi/cdi-extender/src/main/resources/META-INF/cdi.xsd Tue Sep  5 22:01:11 2017
@@ -29,46 +29,82 @@
 			the OSGi CDI Extender. OSGi Bean description documents
 			may be embedded in other XML documents. The OSGi CDI Extender
 			will process all XML documents listed in the osgi.beans
-			attribute of the CDI extender requirement. XML documents containing
-			OSGi Bean descriptions may contain one or more bean, configuration,
-			and/or reference elements embedded in a larger document. Use of
-			the namespace for bean descriptions is mandatory. The attributes
-			and subelements of bean, configuration, and/or reference elements
-			are always unqualified.
+			attribute of the CDI extender requirement. CDI Beans named by the
+			descriptor are described as components. XML documents containing
+			OSGi Bean descriptions may contain one or more component elements.
+			Use of the namespace for bean descriptions is mandatory. The
+			attributes and subelements of component elements are always
+			unqualified.
 		</documentation>
 	</annotation>
-	<element name="bean" type="cdi:Tbean" />
-	<complexType name="Tbean">
+	<element name="component" type="cdi:Tcomponent" />
+	<complexType name="Tcomponent">
 		<sequence>
-			<element name="service" maxOccurs="1" minOccurs="0"
-				type="cdi:Tservice" />
-			<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
-				processContents="lax" />
+			<annotation>
+				<documentation xml:lang="en">
+					Implementations of CDI Support must not require component
+					descriptions to specify the sub-elements of the component
+					element in the order as required by the schema. CDI Support
+					implementations must allow other orderings since
+					arbitrary orderings do not affect the meaning of the
+					component description. Only the relative ordering of
+					property elements have meaning.
+				</documentation>
+			</annotation>
+			<element name="configuration" maxOccurs="unbounded" minOccurs="0" type="cdi:Tconfiguration" />
+			<element name="property" maxOccurs="unbounded" minOccurs="0" type="cdi:Tproperty" />
+			<element name="provide" maxOccurs="unbounded" minOccurs="0" type="cdi:Tprovide" />
+			<element name="reference" maxOccurs="unbounded" minOccurs="0" type="cdi:Treference" />
+			<any namespace="##other" maxOccurs="unbounded" minOccurs="0" processContents="lax" />
 		</sequence>
 		<attribute name="class" type="token" use="required" />
+		<attribute name="name" type="token" use="optional">
+			<annotation>
+				<documentation xml:lang="en">
+					The default value of this attribute is the value of
+					the class attribute. If multiple component elements use the same
+					value for the class attribute, then using the default value
+					for this attribute will result in duplicate names.
+					In this case, this attribute must be specified with
+					a unique value.
+				</documentation>
+			</annotation>
+		</attribute>
+		<attribute name="service-scope" default="none" type="cdi:Tservice_scope" use="optional" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<element name="configuration" type="cdi:Tconfiguration" />
 	<complexType name="Tconfiguration">
 		<sequence>
-			<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
-				processContents="lax" />
+			<any namespace="##any" processContents="lax"
+				minOccurs="0" maxOccurs="unbounded" />
 		</sequence>
-		<attribute name="beanClass" type="token" use="required" />
-		<attribute name="pid" type="token" use="required" />
-		<attribute name="required" type="boolean" default="true" use="optional" />
+		<attribute name="configuration-policy" default="optional" type="cdi:Tconfiguration-policy" use="optional" />
+		<attribute name="configuration-pid" use="optional">
+			<annotation>
+				<documentation xml:lang="en">
+					The default value is the name attribute of the component.
+				</documentation>
+			</annotation>
+			<simpleType>
+				<restriction>
+					<simpleType>
+						<list itemType="token" />
+					</simpleType>
+					<minLength value="1" />
+				</restriction>
+			</simpleType>
+		</attribute>
+		<attribute name="type" type="token" use="required" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<element name="reference" type="cdi:Treference" />
-	<complexType name="Treference">
+	<complexType name="Tmulti-value">
 		<sequence>
-			<any namespace="##any" processContents="lax"
-				minOccurs="0" maxOccurs="unbounded" />
+		<element name="value" minOccurs="0" maxOccurs="unbounded" type="cdi:Tvalue"/>
+		<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+			processContents="lax" />
 		</sequence>
-		<attribute name="beanClass" type="token" use="required" />
-		<attribute name="target" type="token" use="required" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
@@ -79,52 +115,92 @@
 				<element name="list" type="cdi:Tmulti-value"/>
 				<element name="set" type="cdi:Tmulti-value"/>
 			</choice>
-			<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
-				processContents="lax" />
+			<any namespace="##other" minOccurs="0" maxOccurs="unbounded" processContents="lax" />
 		</sequence>
 		<attribute name="name" type="string" use="required" />
 		<attribute name="value" type="string" use="optional" />
-		<attribute name="value-type" type="cdi:Tvalue-types" default="String" use="optional" />
+		<attribute name="value-type" default="String" type="cdi:Tvalue-types" use="optional" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<complexType name="Tmulti-value">
+	<complexType name="Tprovide">
 		<sequence>
-		<element name="value" minOccurs="0" maxOccurs="unbounded" type="cdi:Tvalue"/>
-		<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
-			processContents="lax" />
+			<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
 		</sequence>
+		<attribute name="interface" type="token" use="required" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<complexType name="Tvalue" mixed="true">
+	<complexType name="Treference">
 		<sequence>
-		<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
-			processContents="lax" />
+			<any namespace="##any" processContents="lax" minOccurs="0" maxOccurs="unbounded" />
 		</sequence>
+		<attribute name="cardinality" default="1..1" type="cdi:Tcardinality" use="optional" />
+		<attribute name="name" type="token" use="optional" />
+		<attribute name="policy" default="static" type="cdi:Tpolicy" use="optional" />
+		<attribute name="policy-option" default="reluctant" type="cdi:Tpolicy-option" use="optional" />
+		<attribute name="scope" default="bundle" type="cdi:Treference_scope" use="optional" />
+		<attribute name="service" type="token" use="required" />
+		<attribute name="target" type="string" use="optional" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<complexType name="Tprovide">
+	<complexType name="Tvalue" mixed="true">
 		<sequence>
-			<any namespace="##any" processContents="lax"
-				minOccurs="0" maxOccurs="unbounded" />
+		<any namespace="##other" minOccurs="0" maxOccurs="unbounded"
+			processContents="lax" />
 		</sequence>
-		<attribute name="interface" type="token" use="required" />
 		<anyAttribute processContents="lax" />
 	</complexType>
 
-	<complexType name="Tservice">
-		<sequence>
-			<element name="property" type="cdi:Tproperty"
-				maxOccurs="unbounded" minOccurs="0" />
-			<element name="provide" type="cdi:Tprovide"
-				minOccurs="1" maxOccurs="unbounded" />
-			<any namespace="##other" processContents="lax"
-				minOccurs="0" maxOccurs="unbounded" />
-		</sequence>
-		<anyAttribute processContents="lax" />
-	</complexType>
+	<simpleType name="Tcardinality">
+		<restriction base="string">
+			<enumeration value="0..1" />
+			<enumeration value="0..n" />
+			<enumeration value="1..1" />
+			<enumeration value="1..n" />
+		</restriction>
+	</simpleType>
+
+	<simpleType name="Tconfiguration-policy">
+		<restriction base="string">
+			<enumeration value="factory" />
+			<enumeration value="ignore" />
+			<enumeration value="optional" />
+			<enumeration value="require" />
+		</restriction>
+	</simpleType>
+
+	<simpleType name="Tpolicy">
+		<restriction base="string">
+			<enumeration value="dynamic" />
+			<enumeration value="static" />
+		</restriction>
+	</simpleType>
+
+	<simpleType name="Tpolicy-option">
+		<restriction base="string">
+			<enumeration value="greedy" />
+			<enumeration value="reluctant" />
+		</restriction>
+	</simpleType>
+
+	<simpleType name="Treference_scope">
+		<restriction base="string">
+			<enumeration value="bundle" />
+			<enumeration value="prototype" />
+			<enumeration value="prototype_required" />
+		</restriction>
+	</simpleType>
+
+	<simpleType name="Tservice_scope">
+		<restriction base="string">
+			<enumeration value="bundle" />
+			<enumeration value="none" />
+			<enumeration value="prototype" />
+			<enumeration value="singleton" />
+		</restriction>
+	</simpleType>
 
 	<!-- Specifies the data type of a property or of the elements in a multi-value
 			property. Numerical and boolean values are trimmed before they are processed.
@@ -133,23 +209,23 @@
 			type of property it will automatically be boxed. -->
 	<simpleType name="Tvalue-types">
 		<restriction base="string">
-			<enumeration value="String" />
-			<enumeration value="long" />
-			<enumeration value="Long" />
+			<enumeration value="boolean" />
+			<enumeration value="Boolean" />
+			<enumeration value="byte" />
+			<enumeration value="Byte" />
+			<enumeration value="char" />
+			<enumeration value="Character" />
 			<enumeration value="double" />
 			<enumeration value="Double" />
 			<enumeration value="float" />
 			<enumeration value="Float" />
 			<enumeration value="int" />
 			<enumeration value="Integer" />
-			<enumeration value="byte" />
-			<enumeration value="Byte" />
-			<enumeration value="char" />
-			<enumeration value="Character" />
-			<enumeration value="boolean" />
-			<enumeration value="Boolean" />
+			<enumeration value="long" />
+			<enumeration value="Long" />
 			<enumeration value="short" />
 			<enumeration value="Short" />
+			<enumeration value="String" />
 		</restriction>
 	</simpleType>
 

Added: aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/component/ComponentExtension_Ctor_static_reluctant_Test.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/component/ComponentExtension_Ctor_static_reluctant_Test.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/component/ComponentExtension_Ctor_static_reluctant_Test.java (added)
+++ aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/component/ComponentExtension_Ctor_static_reluctant_Test.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,318 @@
+/**
+ * Licensed 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.cdi.container.internal.component;
+
+import static org.apache.aries.cdi.container.test.TestUtil.*;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.aries.cdi.container.internal.model.BeansModel;
+import org.apache.aries.cdi.container.internal.reference.ReferenceModel;
+import org.apache.aries.cdi.container.test.MockCdiContainer;
+import org.apache.aries.cdi.container.test.MockCdiContainerAndComponents;
+import org.apache.aries.cdi.container.test.beans.Bar;
+import org.apache.aries.cdi.container.test.beans.CtorArrayListFoo;
+import org.apache.aries.cdi.container.test.beans.CtorFoo;
+import org.apache.aries.cdi.container.test.beans.CtorFooBar;
+import org.apache.aries.cdi.container.test.beans.CtorFooFoo;
+import org.apache.aries.cdi.container.test.beans.CtorFooFooNamed;
+import org.apache.aries.cdi.container.test.beans.CtorFooOptional;
+import org.apache.aries.cdi.container.test.beans.CtorListFoo;
+import org.apache.aries.cdi.container.test.beans.Foo;
+import org.apache.aries.cdi.container.test.beans.CtorCollectionFoo;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.osgi.service.cdi.annotations.ReferenceCardinality;
+import org.osgi.service.cdi.annotations.ReferencePolicy;
+import org.osgi.service.cdi.annotations.ReferencePolicyOption;
+
+public class ComponentExtension_Ctor_static_reluctant_Test {
+
+	@Test
+	public void test_CtorArrayListFoo() throws Exception {
+		try (MockCdiContainerAndComponents container =
+				new MockCdiContainerAndComponents(
+					"test", CtorArrayListFoo.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(1, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(ArrayList.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MULTIPLE, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorCollectionFoo() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorCollectionFoo.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(1, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(Collection.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MULTIPLE, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorFooFoo() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorFooFoo.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(2, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorFoo() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorFoo.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(1, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorListFoo() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorListFoo.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(1, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(List.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MULTIPLE, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorFooBar() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorFooBar.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			List<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(2, references.size());
+//
+//			ReferenceModel referenceModel = references.get(0);
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+
+//			referenceModel = references.get(1);
+//
+//			Assert.assertEquals(Bar.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("Bar1", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Bar.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorFooFooNamed() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorFooFooNamed.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			Collection<ReferenceModel> references = sort(container.getTrackedReferences(), (c1, c2) -> c1.toString().compareTo(c2.toString()));
+//
+//			Assert.assertEquals(2, references.size());
+//
+//			Iterator<ReferenceModel> referenceIterator = references.iterator();
+//
+//			ReferenceModel referenceModel = referenceIterator.next();
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("foo_a", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+//
+//			referenceModel = referenceIterator.next();
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.MANDATORY, referenceModel.getCardinality());
+//			Assert.assertEquals("foo_b", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+	@Test
+	public void test_CtorFooOptional() throws Exception {
+		try (MockCdiContainerAndComponents container = new MockCdiContainerAndComponents(
+				"test", CtorFooOptional.class.getName())) {
+
+			BeansModel beansModel = container.containerState().beansModel();
+
+			Collection<ComponentModel> models = sort(beansModel.getComponentModels());
+
+			Assert.assertEquals(1, models.size());
+
+//			Collection<ReferenceModel> references = container.getTrackedReferences();
+//
+//			Assert.assertEquals(1, references.size());
+//
+//			Iterator<ReferenceModel> referenceIterator = references.iterator();
+//
+//			ReferenceModel referenceModel = referenceIterator.next();
+//
+//			Assert.assertEquals(Foo.class, referenceModel.getBeanClass());
+//			Assert.assertEquals(
+//				ReferenceCardinality.OPTIONAL, referenceModel.getCardinality());
+//			Assert.assertEquals("Foo0", referenceModel.getName());
+//			Assert.assertEquals(ReferencePolicy.STATIC, referenceModel.getPolicy());
+//			Assert.assertEquals(
+//				ReferencePolicyOption.RELUCTANT, referenceModel.getPolicyOption());
+//			Assert.assertNull(referenceModel.getScope());
+//			Assert.assertEquals(Foo.class, referenceModel.getServiceClass());
+//			Assert.assertEquals("", referenceModel.getTarget());
+		}
+	}
+
+}
\ No newline at end of file

Added: aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Ignore.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Ignore.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Ignore.java (added)
+++ aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Ignore.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,355 @@
+/**
+ * Licensed 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.cdi.container.internal.configuration;
+
+import static org.osgi.framework.Constants.SERVICE_PID;
+import static org.apache.aries.cdi.container.test.TestUtil.*;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.service.cdi.annotations.ConfigurationPolicy;
+
+public class ConfigurationCallbackTest_Ignore {
+
+	@Test
+	public void test_emptyAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		callback.added(properties);
+	}
+
+	@Test
+	public void test_emptyUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		callback.updated(properties);
+	}
+
+	@Test
+	public void test_nullAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(null);
+	}
+
+	@Test
+	public void test_nullUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.updated(null);
+	}
+
+	@Test
+	public void test_addAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(properties);
+	}
+
+	@Test
+	public void test_addAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test
+	public void test_addAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(properties);
+	}
+
+	@Test
+	public void test_removeAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test
+	public void test_removeAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+	}
+
+	@Test
+	public void test_removeAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test
+	public void test_removeBeforeAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+	}
+
+	@Test
+	public void test_updateAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test
+	public void test_updateAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.updated(properties);
+	}
+
+	@Test
+	public void test_updateAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fee");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test
+	public void test_updateBeforeAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.updated(properties);
+	}
+
+	private ConfigurationPolicy POLICY = ConfigurationPolicy.IGNORE;
+
+}

Added: aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Optional.java
URL: http://svn.apache.org/viewvc/aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Optional.java?rev=1807424&view=auto
==============================================================================
--- aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Optional.java (added)
+++ aries/trunk/cdi/cdi-extender/src/test/java/org/apache/aries/cdi/container/internal/configuration/ConfigurationCallbackTest_Optional.java Tue Sep  5 22:01:11 2017
@@ -0,0 +1,362 @@
+/**
+ * Licensed 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.cdi.container.internal.configuration;
+
+import static org.osgi.framework.Constants.SERVICE_PID;
+import static org.apache.aries.cdi.container.test.TestUtil.*;
+
+import java.util.Dictionary;
+import java.util.Hashtable;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.osgi.service.cdi.annotations.ConfigurationPolicy;
+
+public class ConfigurationCallbackTest_Optional {
+
+	@Test(expected = IllegalArgumentException.class)
+	public void test_emptyAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		callback.added(properties);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void test_emptyUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+
+		callback.updated(properties);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void test_nullAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(null);
+	}
+
+	@Test(expected = IllegalArgumentException.class)
+	public void test_nullUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.updated(null);
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_addAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.added(properties);
+	}
+
+	@Test
+	public void test_addAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_addAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.added(properties);
+	}
+
+	@Test
+	public void test_removeAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_removeAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+	}
+
+	@Test
+	public void test_removeAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("foo", callback.properties().get(SERVICE_PID));
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("fum", callback.properties().get(SERVICE_PID));
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_removeBeforeAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.removed();
+	}
+
+	@Test
+	public void test_updateAfterAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("foo", callback.properties().get(SERVICE_PID));
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("fum", callback.properties().get(SERVICE_PID));
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_updateAfterRemove() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+
+		callback.removed();
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		callback.updated(properties);
+	}
+
+	@Test
+	public void test_updateAfterUpdate() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.added(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("foo", callback.properties().get(SERVICE_PID));
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fum");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("fum", callback.properties().get(SERVICE_PID));
+
+		properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "fee");
+
+		callback.updated(properties);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertFalse(callback.properties().isEmpty());
+		Assert.assertEquals("fee", callback.properties().get(SERVICE_PID));
+	}
+
+	@Test(expected = IllegalStateException.class)
+	public void test_updateBeforeAdd() throws Exception {
+		ConfigurationCallback callback = getCallback(POLICY);
+
+		Assert.assertTrue(callback.resolved());
+		Assert.assertNotNull(callback.properties());
+		Assert.assertTrue(callback.properties().isEmpty());
+
+		Dictionary<String, Object> properties = new Hashtable<>();
+		properties.put(SERVICE_PID, "foo");
+
+		callback.updated(properties);
+	}
+
+	private ConfigurationPolicy POLICY = ConfigurationPolicy.OPTIONAL;
+
+}