| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.felix.scr.impl.inject; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.InvocationTargetException; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.util.Map; |
| |
| import org.apache.felix.scr.impl.helper.ReadOnlyDictionary; |
| import org.apache.felix.scr.impl.inject.internal.Annotations; |
| import org.apache.felix.scr.impl.inject.internal.ClassUtils; |
| import org.apache.felix.scr.impl.logger.ComponentLogger; |
| import org.apache.felix.scr.impl.metadata.ReferenceMetadata; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.ServiceReference; |
| import org.osgi.service.log.LogService; |
| |
| /** |
| * Utility methods for handling references and activation |
| */ |
| public class ValueUtils { |
| |
| /** |
| * The value type of the field, activation field or constructor parameter |
| */ |
| public enum ValueType |
| { |
| ignore, |
| componentContext, // field activation, constructor |
| bundleContext, // field activation, constructor |
| config_map, // field activation, constructor |
| config_annotation, // field activation, constructor |
| ref_logger, // reference (field, constructor, method) |
| ref_formatterLogger, // reference (field, constructor, method) |
| ref_serviceReference, // reference (field, constructor, method) |
| ref_serviceObjects, // reference (field, constructor, method) |
| ref_serviceType, // reference (field, constructor, method) |
| ref_map, // reference (field, constructor, method) |
| ref_tuple // reference (field, constructor ??) // TDODO |
| } |
| |
| /** Empty array. */ |
| public static final ValueType[] EMPTY_VALUE_TYPES = new ValueType[0]; |
| |
| /** |
| * Get the value type for the parameter class. |
| * This method is used for field activation and constructor injection. |
| * |
| * @param typeClass The class of the parameter |
| * @return The value type |
| */ |
| public static ValueType getValueType( final Class<?> typeClass ) |
| { |
| if ( typeClass == ClassUtils.COMPONENT_CONTEXT_CLASS ) |
| { |
| return ValueType.componentContext; |
| } |
| else if ( typeClass == ClassUtils.BUNDLE_CONTEXT_CLASS ) |
| { |
| return ValueType.bundleContext; |
| } |
| else if ( typeClass == ClassUtils.MAP_CLASS ) |
| { |
| return ValueType.config_map; |
| } |
| else if ( typeClass.isAnnotation() ) |
| { |
| return ValueType.config_annotation; |
| } |
| return ValueType.ignore; |
| } |
| |
| /** |
| * Get the value type of the reference for a field/constructor argument |
| * |
| * @param componentClass The component class declaring the reference |
| * @param metadata The reference metadata |
| * @param typeClass The type of the field/parameter |
| * @param f The optional field. If {@code null} this is a constructor reference |
| * @param logger The logger |
| * @return The value type for the field. If invalid, {@code ValueType#ignore} |
| */ |
| public static ValueType getReferenceValueType( |
| final Class<?> componentClass, |
| final ReferenceMetadata metadata, |
| final Class<?> typeClass, |
| final Field field, |
| final ComponentLogger logger ) |
| { |
| final Class<?> referenceType = ClassUtils.getClassFromComponentClassLoader( |
| componentClass, metadata.getInterface(), logger); |
| |
| ValueType valueType = ValueType.ignore; |
| |
| // unary reference |
| if ( !metadata.isMultiple() ) |
| { |
| // service interface or supertype |
| if ( typeClass.isAssignableFrom(referenceType) ) |
| { |
| valueType = ValueType.ref_serviceType; |
| } |
| // service reference |
| else if ( typeClass == ClassUtils.SERVICE_REFERENCE_CLASS ) |
| { |
| valueType = ValueType.ref_serviceReference; |
| } |
| // components service object |
| else if ( typeClass == ClassUtils.COMPONENTS_SERVICE_OBJECTS_CLASS ) |
| { |
| valueType = ValueType.ref_serviceObjects; |
| } |
| // map (properties) |
| else if ( typeClass == ClassUtils.MAP_CLASS ) |
| { |
| valueType = ValueType.ref_map; |
| } |
| // tuple (map.entry) |
| else if ( typeClass == ClassUtils.MAP_ENTRY_CLASS ) |
| { |
| valueType = ValueType.ref_tuple; |
| } |
| // 1.4: Logger - reference needs to be of type LoggerFactory |
| else if ( typeClass.getName().equals(ClassUtils.LOGGER_CLASS) && metadata.getInterface().equals(ClassUtils.LOGGER_FACTORY_CLASS) ) |
| { |
| return ValueType.ref_logger; |
| } |
| // 1.4: FormatterLogger - reference needs to be of type LoggerFactory |
| else if ( typeClass.getName().equals(ClassUtils.FORMATTER_LOGGER_CLASS) && metadata.getInterface().equals(ClassUtils.LOGGER_FACTORY_CLASS) ) |
| { |
| return ValueType.ref_formatterLogger; |
| } |
| else |
| { |
| if ( field != null ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} has unsupported type {2}", null, |
| metadata.getField(), componentClass, typeClass.getName() ); |
| } |
| else |
| { |
| logger.log( LogService.LOG_ERROR, "Constructor argument {0} in class {1} has unsupported type {2}", null, |
| metadata.getParameterIndex(), componentClass, typeClass.getName() ); |
| } |
| valueType = ValueType.ignore; |
| } |
| |
| // if the field is dynamic, it has to be volatile (field is ignored, case logged) (112.3.8.1) |
| if ( field != null && !metadata.isStatic() && !Modifier.isVolatile(field.getModifiers()) ) { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} must be declared volatile to handle a dynamic reference", null, |
| metadata.getField(), componentClass ); |
| valueType = ValueType.ignore; |
| } |
| |
| // the field must not be final (field is ignored, case logged) (112.3.8.1) |
| if ( field != null && Modifier.isFinal(field.getModifiers()) ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} must not be declared as final", null, |
| metadata.getField(), componentClass ); |
| valueType = ValueType.ignore; |
| } |
| } |
| else |
| { |
| String colType = field != null ? metadata.getFieldCollectionType() : metadata.getParameterCollectionType(); |
| if ( ReferenceMetadata.FIELD_VALUE_TYPE_SERVICE.equals(colType) ) |
| { |
| valueType = ValueType.ref_serviceType; |
| } |
| else if ( ReferenceMetadata.FIELD_VALUE_TYPE_REFERENCE.equals(colType) ) |
| { |
| valueType = ValueType.ref_serviceReference; |
| } |
| else if ( ReferenceMetadata.FIELD_VALUE_TYPE_SERVICEOBJECTS.equals(colType) ) |
| { |
| valueType = ValueType.ref_serviceObjects; |
| } |
| else if ( ReferenceMetadata.FIELD_VALUE_TYPE_PROPERTIES.equals(colType) ) |
| { |
| valueType = ValueType.ref_map; |
| } |
| else if ( ReferenceMetadata.FIELD_VALUE_TYPE_TUPLE.equals(colType) ) |
| { |
| valueType = ValueType.ref_tuple; |
| } |
| |
| // multiple cardinality, field type must be collection or subtype |
| if ( !ClassUtils.COLLECTION_CLASS.isAssignableFrom(typeClass) ) |
| { |
| if ( field != null ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} has unsupported type {2}", null, |
| metadata.getField(), componentClass, typeClass.getName() ); |
| } |
| else |
| { |
| logger.log( LogService.LOG_ERROR, "Constructor argument {0} in class {1} has unsupported type {2}", null, |
| metadata.getParameterIndex(), componentClass, typeClass.getName() ); |
| } |
| valueType = ValueType.ignore; |
| } |
| |
| // additional checks for replace strategy: |
| if ( metadata.isReplace() && field != null ) |
| { |
| // if the field is dynamic wit has to be volatile (field is ignored, case logged) (112.3.8.1) |
| if ( !metadata.isStatic() && !Modifier.isVolatile(field.getModifiers()) ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} must be declared volatile to handle a dynamic reference", null, |
| metadata.getField(), componentClass ); |
| valueType = ValueType.ignore; |
| } |
| |
| // replace strategy: field must not be final (field is ignored, case logged) (112.3.8.1) |
| // only collection and list allowed |
| if ( typeClass != ClassUtils.LIST_CLASS && typeClass != ClassUtils.COLLECTION_CLASS ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} has unsupported type {2}."+ |
| " It must be one of java.util.Collection or java.util.List.", null, |
| metadata.getField(), componentClass, typeClass.getName() ); |
| valueType = ValueType.ignore; |
| |
| } |
| if ( Modifier.isFinal(field.getModifiers()) ) |
| { |
| logger.log( LogService.LOG_ERROR, "Field {0} in class {1} must not be declared as final", null, |
| metadata.getField(), componentClass ); |
| valueType = ValueType.ignore; |
| } |
| } |
| } |
| // static references only allowed for replace strategy |
| if ( field != null && metadata.isStatic() && !metadata.isReplace() ) |
| { |
| logger.log( LogService.LOG_ERROR, "Update strategy for field {0} in class {1} only allowed for non static field references.", null, |
| metadata.getField(), componentClass ); |
| valueType = ValueType.ignore; |
| } |
| return valueType; |
| } |
| |
| /** |
| * Get the value for the value type |
| * @param componentType The class of the component |
| * @param type The value type |
| * @param targetType Optional target type, only required for type {@code ValueType#config_annotation}. |
| * @param componentContext The component context |
| * @param refPair The ref pair |
| * @return The value or {@code null}. |
| */ |
| @SuppressWarnings("unchecked") |
| public static Object getValue( |
| final String componentType, |
| final ValueType type, |
| final Class<?> targetType, |
| final ScrComponentContext componentContext, |
| final RefPair<?, ?> refPair) |
| { |
| final Object value; |
| switch ( type ) |
| { |
| case ignore : value = null; |
| break; |
| case componentContext : value = componentContext; |
| break; |
| case bundleContext : value = componentContext.getBundleContext(); |
| break; |
| case config_map : // note: getProperties() returns a ReadOnlyDictionary which is a Map |
| value = componentContext.getProperties(); |
| break; |
| case config_annotation : value = Annotations.toObject(targetType, |
| (Map<String, Object>) componentContext.getProperties(), |
| componentContext.getBundleContext().getBundle(), componentContext.getComponentMetadata().isConfigureWithInterfaces()); |
| break; |
| case ref_serviceType : value = refPair.getServiceObject(componentContext); |
| break; |
| case ref_serviceReference : value = refPair.getRef(); |
| break; |
| case ref_serviceObjects : value = componentContext.getComponentServiceObjectsHelper().getServiceObjects(refPair.getRef()); |
| break; |
| case ref_map : value = new ReadOnlyDictionary( refPair.getRef() ); |
| break; |
| case ref_tuple : final Object tupleKey = new ReadOnlyDictionary( refPair.getRef() ); |
| final Object tupleValue = refPair.getServiceObject(componentContext); |
| value = new MapEntryImpl(tupleKey, tupleValue, refPair.getRef()); |
| break; |
| case ref_logger : |
| case ref_formatterLogger : value = getLogger(componentType, targetType, componentContext, refPair); |
| break; |
| default: value = null; |
| } |
| return value; |
| } |
| |
| private static Object getLogger(String componentType, |
| final Class<?> targetType, |
| final ScrComponentContext componentContext, |
| final RefPair<?, ?> refPair ) |
| { |
| final Object factory = refPair.getServiceObject(componentContext); |
| if ( factory != null ) |
| { |
| Exception error = null; |
| try { |
| final Method m = factory.getClass().getMethod("getLogger", new Class[] {Bundle.class, String.class, Class.class}); |
| // FELIX-5905 |
| m.setAccessible(true); |
| return m.invoke(factory, new Object[] {componentContext.getBundleContext().getBundle(), componentType, targetType}); |
| } catch (final NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) { |
| error = e; |
| } |
| componentContext.getLogger().log( LogService.LOG_ERROR, "Unexpected error while trying to get logger.", null, error ); |
| } |
| return null; |
| } |
| |
| /** |
| * Comparable map entry using the service reference to compare. |
| */ |
| @SuppressWarnings("rawtypes") |
| private static final class MapEntryImpl implements Map.Entry, Comparable<Map.Entry<?, ?>> |
| { |
| |
| private final Object key; |
| private final Object value; |
| private final ServiceReference<?> ref; |
| |
| public MapEntryImpl(final Object key, |
| final Object value, |
| final ServiceReference<?> ref) |
| { |
| this.key = key; |
| this.value = value; |
| this.ref = ref; |
| } |
| |
| @Override |
| public Object getKey() |
| { |
| return this.key; |
| } |
| |
| @Override |
| public Object getValue() |
| { |
| return this.value; |
| } |
| |
| @Override |
| public Object setValue(final Object value) |
| { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public int compareTo(final Map.Entry<?, ?> o) |
| { |
| if ( o == null ) |
| { |
| return 1; |
| } |
| if ( o instanceof MapEntryImpl ) |
| { |
| final MapEntryImpl other = (MapEntryImpl)o; |
| return ref.compareTo(other.ref); |
| |
| } |
| return new Integer(this.hashCode()).compareTo(o.hashCode()); |
| } |
| } |
| } |