blob: 5930747274b177d509d5b25230533368b019f312 [file] [log] [blame]
/**
* 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.util.Reflection.cast;
import java.lang.annotation.Annotation;
import java.lang.reflect.Member;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.lang.reflect.WildcardType;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import javax.enterprise.inject.Instance;
import javax.enterprise.inject.spi.Annotated;
import javax.enterprise.inject.spi.AnnotatedCallable;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedParameter;
import javax.enterprise.inject.spi.InjectionPoint;
import javax.inject.Named;
import javax.inject.Provider;
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.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cdi.annotations.Greedy;
import org.osgi.service.cdi.annotations.Reference;
import org.osgi.service.cdi.reference.ReferenceServiceObjects;
import org.osgi.service.cdi.runtime.dto.template.MaximumCardinality;
import org.osgi.service.cdi.runtime.dto.template.ReferenceTemplateDTO;
import org.osgi.service.cdi.runtime.dto.template.ReferenceTemplateDTO.Policy;
import org.osgi.service.cdi.runtime.dto.template.ReferenceTemplateDTO.PolicyOption;
import org.osgi.util.converter.TypeReference;
public class ReferenceModel {
public static class Builder {
public ReferenceModel build() {
return new ReferenceModel(_annotated, _member, _qualifiers, _type, _referenceClass);
}
public Builder injectionPoint(InjectionPoint injectionPoint) {
_annotated = injectionPoint.getAnnotated();
_member = injectionPoint.getMember();
_qualifiers = injectionPoint.getQualifiers();
_type = injectionPoint.getType();
if (_annotated instanceof AnnotatedParameter) {
AnnotatedParameter<?> parameter = (AnnotatedParameter<?>)_annotated;
_referenceClass = parameter.getDeclaringCallable().getDeclaringType().getJavaClass();
}
if (_annotated instanceof AnnotatedField) {
AnnotatedField<?> field = (AnnotatedField<?>)_annotated;
_referenceClass = field.getDeclaringType().getJavaClass();
}
return this;
}
private Annotated _annotated;
private Member _member;
private Set<Annotation> _qualifiers;
private Class<?> _referenceClass;
private Type _type;
}
public static enum Scope {
BUNDLE, PROTOTYPE, SINGLETON
}
private ReferenceModel(
Annotated annotated,
Member member,
Set<Annotation> qualifiers,
Type type,
Class<?> referenceClass) {
_annotated = annotated;
_member = member;
_qualifiers = qualifiers;
_injectionPointType = type;
_referenceClass = referenceClass;
calculateServiceType(_injectionPointType);
Reference reference = _annotated.getAnnotation(Reference.class);
if ((reference != null) && (reference.value() != null) && (reference.value() != Object.class)) {
if (!_serviceType.isAssignableFrom(reference.value())) {
throw new IllegalArgumentException(
"The service type specified in @Reference (" + reference.value() +
") is not compatible with the type calculated from the injection point: " +
_serviceType);
}
_serviceType = reference.value();
}
Type rawType = _injectionPointType;
if (rawType instanceof ParameterizedType) {
ParameterizedType pt = cast(_injectionPointType);
rawType = pt.getRawType();
}
_beanClass = cast(rawType);
_name = calculateName(_serviceType, _annotated);
if (_annotated.isAnnotationPresent(Greedy.class)) {
_greedy = true;
}
}
public Annotated getAnnotated() {
return _annotated;
}
public Class<?> getBeanClass() {
return _beanClass;
}
public CollectionType getCollectionType() {
return _collectionType;
}
public Type getInjectionPointType() {
return _injectionPointType;
}
public String getName() {
return _name;
}
public Member getMember() {
return _member;
}
public Set<Annotation> getQualifiers() {
return _qualifiers;
}
public Class<?> getServiceType() {
return _serviceType;
}
public String getTarget() {
return _targetFilter;
}
public Set<Type> getTypes() {
return null; // TODO _types;
}
public boolean dynamic() {
return _dynamic;
}
public boolean optional() {
return _optional;
}
public boolean unary() {
return _multiplicity == MaximumCardinality.ONE;
}
@Override
public String toString() {
if (_string == null) {
//_string = String.format("reference[name='%s', service='%s', scope='%s', target='%s']", _name, _service, _scope, _target);
_string = super.toString();
}
return _string;
}
public static String buildFilter(
Class<?> serviceType,
String target,
Scope 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 == Scope.PROTOTYPE) {
sb.append("(");
sb.append(Constants.SERVICE_SCOPE);
sb.append("=");
sb.append(Constants.SCOPE_PROTOTYPE);
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;
// }
private String calculateName(Class<?> service, Annotated annotated) {
Named named = annotated.getAnnotation(Named.class);
if (named != null) {
if (named.value() == null | named.value().equals("")) {
throw new IllegalArgumentException(
"It's illegal to specify @Name without specifying a value with @Reference: " +
annotated);
}
return named.value();
}
String prefix = _referenceClass.getName() + ".";
if (annotated instanceof AnnotatedParameter) {
AnnotatedParameter<?> parameter = (AnnotatedParameter<?>)annotated;
AnnotatedCallable<?> declaringCallable = parameter.getDeclaringCallable();
if (declaringCallable instanceof AnnotatedConstructor) {
return prefix + "new" + parameter.getPosition();
}
else {
AnnotatedMethod<?> method = (AnnotatedMethod<?>)declaringCallable;
return prefix + method.getJavaMember().getName() + parameter.getPosition();
}
}
else {
AnnotatedField<?> annotatedField = (AnnotatedField<?>)annotated;
return prefix + annotatedField.getJavaMember().getName();
}
}
private void calculateServiceType(Type type) {
if (!(type instanceof ParameterizedType)) {
if (!(type instanceof Class)) {
throw new IllegalArgumentException(
"The service type must not be generic: " + type);
}
else if (Map.class == cast(type)) {
throw new IllegalArgumentException(
"Map must specify a generic type arguments: " + type);
}
else if (Map.Entry.class == cast(type)) {
throw new IllegalArgumentException(
"Map.Entry must specify a generic type arguments: " + type);
}
else if (ReferenceServiceObjects.class == cast(type)) {
throw new IllegalArgumentException(
"ReferenceServiceObjects must specify a generic type argument: " + type);
}
else if (ServiceReference.class == cast(type)) {
throw new IllegalArgumentException(
"ServiceReference must specify a generic type argument: " + type);
}
_serviceType = cast(type);
return;
}
ParameterizedType parameterizedType = cast(type);
Type rawType = parameterizedType.getRawType();
if (Instance.class == cast(rawType)) {
throw new IllegalArgumentException(
"Instance<T> is not supported with @Reference: " + type);
}
if ((!_dynamic) && (Provider.class == cast(rawType))) {
_dynamic = true;
calculateServiceType(parameterizedType.getActualTypeArguments()[0]);
return;
}
if ((!_optional) && (Optional.class == cast(rawType))) {
_optional = true;
calculateServiceType(parameterizedType.getActualTypeArguments()[0]);
return;
}
if ((_multiplicity == MaximumCardinality.ONE) &&
((Collection.class == cast(rawType)) ||
(Iterable.class == cast(rawType)) ||
(List.class == cast(rawType)))) {
_optional = true;
_multiplicity = MaximumCardinality.MANY;
calculateServiceType(parameterizedType.getActualTypeArguments()[0]);
return;
}
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
Type argument = actualTypeArguments[0];
if (Map.class == cast(rawType)) {
if (String.class != cast(argument)) {
throw new IllegalArgumentException(
"Maps of properties must use the form Map<String, (? | Object)>: " + type);
}
argument = actualTypeArguments[1];
if ((Object.class != cast(argument)) &&
(!(argument instanceof WildcardType))) {
throw new IllegalArgumentException(
"Maps of properties must use the form Map<String, (? | Object)>: " + type);
}
_collectionType = CollectionType.PROPERTIES;
Reference reference = _annotated.getAnnotation(Reference.class);
if ((reference == null) || (reference.value() == null)) {
throw new IllegalArgumentException(
"Maps of properties must specify service type with @Reference.value(): " + argument);
}
_serviceType = reference.value();
return;
}
if (Map.Entry.class == cast(rawType)) {
if (!checkKey(argument)) {
throw new IllegalArgumentException(
"Tuples must have a key of type Map<String, (? | Object)>: " + argument);
}
argument = actualTypeArguments[1];
if (!(argument instanceof Class)) {
throw new IllegalArgumentException(
"The service type must not be generic: " + argument);
}
_collectionType = CollectionType.TUPLE;
_serviceType = cast(argument);
return;
}
if (ReferenceServiceObjects.class == cast(rawType)) {
_collectionType = CollectionType.SERVICEOBJECTS;
calculateServiceType(argument);
return;
}
if (ServiceReference.class == cast(rawType)) {
_collectionType = CollectionType.REFERENCE;
calculateServiceType(argument);
return;
}
_serviceType = cast(rawType);
if (_serviceType.getTypeParameters().length > 0) {
throw new IllegalArgumentException(
"Illegal service type: " + 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 Type upwrapCDITypes(Type type) {
// if (type instanceof ParameterizedType) {
// ParameterizedType pType = (ParameterizedType)type;
//
// Type rawType = pType.getRawType();
//
// if (Instance.class == Reflection.cast(rawType) ||
// ServiceEvent.class == Reflection.cast(rawType)) {
//
// type = pType.getActualTypeArguments()[0];
// }
// }
//
// return type;
// }
public ReferenceTemplateDTO toDTO() {
ReferenceTemplateDTO dto = new ReferenceTemplateDTO();
dto.maximumCardinality = _multiplicity;
dto.minimumCardinality = (_multiplicity == MaximumCardinality.ONE) ? (_optional?0:1) : (0);
dto.name = _name;
dto.policy = (_dynamic) ? Policy.DYNAMIC : Policy.STATIC;
dto.policyOption = (_greedy) ? PolicyOption.GREEDY: PolicyOption.RELUCTANT;
dto.targetFilter = _targetFilter;
dto.serviceType = _serviceType.getName();
return dto;
}
private static final String _emptyFilter = "";
private static final TypeReference<Map<String, String>> _mapType = new TypeReference<Map<String, String>>(){};
private final Annotated _annotated;
private Class<?> _beanClass;
private CollectionType _collectionType = CollectionType.SERVICE;
private boolean _dynamic = false;
private boolean _greedy = false;
private final Type _injectionPointType;
private final Member _member;
private MaximumCardinality _multiplicity = MaximumCardinality.ONE;
private String _name;
private boolean _optional = false;
private final Set<Annotation> _qualifiers;
private Class<?> _referenceClass;
private Class<?> _serviceType;
private String _string;
private String _targetFilter = _emptyFilter;
}