/** * 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.container; import static org.apache.aries.cdi.container.internal.util.Reflection.cast; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; import java.util.Comparator; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentSkipListSet; import javax.enterprise.inject.Instance; import javax.enterprise.inject.spi.InjectionPoint; import org.apache.aries.cdi.container.internal.util.Conversions; import org.apache.aries.cdi.container.internal.util.Maps; import org.jboss.weld.manager.BeanManagerImpl; import org.osgi.framework.Constants; import org.osgi.framework.Filter; import org.osgi.framework.FrameworkUtil; import org.osgi.framework.InvalidSyntaxException; import org.osgi.framework.ServiceReference; import org.osgi.service.cdi.annotations.Reference; import org.osgi.service.cdi.annotations.ReferenceScope; import org.osgi.util.converter.TypeReference; public class ReferenceDependency { public ReferenceDependency( BeanManagerImpl beanManagerImpl, Reference reference, InjectionPoint injectionPoint) throws InvalidSyntaxException { _beanManagerImpl = beanManagerImpl; _reference = reference; _injectionPoint = injectionPoint; _bindType = getBindType(_injectionPoint.getType()); _minCardinality = getMinCardinality(_injectionPoint); _serviceClass = getServiceType(); _string = buildFilter(_serviceClass, _injectionPoint.getQualifiers()); _filter = FrameworkUtil.createFilter(_string); } public Class<?> getBeanClass() { if (_bindType == BindType.SERVICE_REFERENCE) { return ServiceReference.class; } else if (_bindType == BindType.SERVICE_PROPERTIES) { return Map.class; } return _serviceClass; } public BindType getBindType() { return _bindType; } public InjectionPoint getInjectionPoint() { return _injectionPoint; } public int getMinCardinality() { return _minCardinality; } public BeanManagerImpl getManager() { return _beanManagerImpl; } public Reference getReference() { return _reference; } public Set<ServiceReference<?>> getMatchingReferences() { return _matchingReferences; } public Type getInjectionPointType() { Type type = _injectionPoint.getType(); if ((type instanceof ParameterizedType)) { ParameterizedType pType = (ParameterizedType)type; if (Instance.class.isAssignableFrom(cast(pType.getRawType()))) { type = pType.getActualTypeArguments()[0]; } } return type; } public boolean isResolved() { return (_matchingReferences.size() >= _minCardinality); } public boolean matches(ServiceReference<?> reference) { return _filter.match(reference); } public void resolve(ServiceReference<?> reference) { _matchingReferences.add(reference); } public void unresolve(ServiceReference<?> reference) { _matchingReferences.remove(reference); } @Override public String toString() { return _string; } private String buildFilter(Class<?> serviceType, Set<Annotation> qualifiers) throws InvalidSyntaxException { StringBuilder sb = new StringBuilder(); sb.append("(&("); sb.append(Constants.OBJECTCLASS); sb.append("="); sb.append(serviceType.getName()); sb.append(")"); // TODO add Bundle scope? if (_reference.scope() == ReferenceScope.PROTOTYPE) { sb.append("("); sb.append(Constants.SERVICE_SCOPE); sb.append("="); sb.append(Constants.SCOPE_PROTOTYPE); sb.append(")"); } else if (_reference.scope() == ReferenceScope.SINGLETON) { sb.append("("); sb.append(Constants.SERVICE_SCOPE); sb.append("="); sb.append(Constants.SCOPE_SINGLETON); sb.append(")"); } String targetFilter = _reference.target(); int targetFilterLength = targetFilter.length(); if (targetFilterLength > 0) { FrameworkUtil.createFilter(targetFilter); sb.append(targetFilter); } for (Annotation qualifier : qualifiers) { Class<? extends Annotation> annotationType = qualifier.annotationType(); if (annotationType.equals(Reference.class)) { continue; } Map<String, String> map = Conversions.c().convert(qualifier).sourceAs(qualifier.annotationType()).to(_mapType); Maps.appendFilter(sb, map); } sb.append(")"); return sb.toString(); } private BindType getBindType(Type type) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = cast(type); Type rawType = parameterizedType.getRawType(); if (Instance.class.isAssignableFrom(cast(rawType))) { _instance = true; Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); return getBindType(actualTypeArguments[0]); } else if (Map.class.isAssignableFrom(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 BindType.SERVICE_PROPERTIES; } } return BindType.SERVICE; } else if (ServiceReference.class.isAssignableFrom(cast(rawType))) { return BindType.SERVICE_REFERENCE; } return BindType.SERVICE; } else if (ServiceReference.class.isAssignableFrom(cast(type))) { return BindType.SERVICE_REFERENCE; } return BindType.SERVICE; } private int getMinCardinality(InjectionPoint injectionPoint) { int value = 1; if (_instance) { value = 0; } return value; } private Class<?> getServiceType() { if (_reference.service() != Object.class) { return _reference.service(); } Type type = _injectionPoint.getType(); if (_bindType == BindType.SERVICE_PROPERTIES) { throw new IllegalArgumentException( "A @Reference cannot bind service properties to a Map<String, Object> without " + "specifying the @Reference.service property: " + _injectionPoint); } else if ((_bindType == BindType.SERVICE_REFERENCE) && !(type instanceof ParameterizedType)) { 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>: " + _injectionPoint); } if (!(type instanceof ParameterizedType)) { return cast(type); } ParameterizedType parameterizedType = cast(type); Type rawType = parameterizedType.getRawType(); if (Instance.class.isAssignableFrom(cast(rawType))) { Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); type = actualTypeArguments[0]; if (type instanceof ParameterizedType) { parameterizedType = (ParameterizedType)type; rawType = parameterizedType.getRawType(); } else { rawType = type; } } if (!ServiceReference.class.isAssignableFrom(cast(rawType))) { return cast(rawType); } Type[] actualTypeArguments = parameterizedType.getActualTypeArguments(); Type first = actualTypeArguments[0]; if (first instanceof ParameterizedType) { ParameterizedType parameterizedType1 = cast(first); return cast(parameterizedType1.getRawType()); } return cast(first); } private static final TypeReference<Map<String, String>> _mapType = new TypeReference<Map<String, String>>(){}; private final BeanManagerImpl _beanManagerImpl; private final BindType _bindType; private final int _minCardinality; private final Filter _filter; private final InjectionPoint _injectionPoint; private final Reference _reference; private final Set<ServiceReference<?>> _matchingReferences = new ConcurrentSkipListSet<>(Comparator.reverseOrder()); private final Class<?> _serviceClass; private final String _string; private boolean _instance = false; }