/*
* 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.Modifier;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import org.apache.felix.scr.impl.helper.InitReferenceMethod;
import org.apache.felix.scr.impl.helper.MethodResult;
import org.apache.felix.scr.impl.helper.ReadOnlyDictionary;
import org.apache.felix.scr.impl.helper.ReferenceMethod;
import org.apache.felix.scr.impl.helper.SimpleLogger;
import org.apache.felix.scr.impl.manager.ComponentContextImpl;
import org.apache.felix.scr.impl.manager.RefPair;
import org.apache.felix.scr.impl.metadata.ReferenceMetadata;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
/**
* Handler for field references
*/
public class FieldHandler
{
private enum ParamType {
serviceReference,
serviceObjects,
serviceType,
map,
tuple,
ignore
}
/** The reference metadata. */
private final ReferenceMetadata metadata;
/** The component class. */
private final Class<?> componentClass;
/** The field used for the injection. */
private volatile Field field;
/** Value type. */
private volatile ParamType valueType;
/** State handling. */
private volatile State state;
/**
* Create a new field handler
* @param fieldName name of the field
* @param componentClass component class
* @param referenceClassName service class name
*/
public FieldHandler( final ReferenceMetadata metadata,
final Class<?> componentClass)
{
this.metadata = metadata;
this.componentClass = componentClass;
this.state = NotResolved.INSTANCE;
}
/**
* Set the field.
* If the field is found, the state transitions to resolved, if the field is
* {@code null} the state transitions to not found.
* @param f The field or {@code null}.
* @param logger The logger
*/
synchronized private void setField( final FieldInfo f, final SimpleLogger logger )
{
this.field = f.getField();
this.valueType = f.getType();
if ( f != null )
{
state = Resolved.INSTANCE;
logger.log( LogService.LOG_DEBUG, "Found field: {0}",
new Object[] { field }, null );
}
else
{
state = NotFound.INSTANCE;
logger.log(LogService.LOG_ERROR, "Field [{0}] not found; Component will fail",
new Object[] { this.metadata.getField() }, null);
}
}
/**
* Finds the field named in the {@link #fieldName} field in the given
* <code>targetClass</code>. If the target class has no acceptable method
* the class hierarchy is traversed until a field is found or the root
* of the class hierarchy is reached without finding a field.
*
* @return The requested field or <code>null</code> if no acceptable field
* can be found in the target class or any super class.
* @throws InvocationTargetException If an unexpected Throwable is caught
* trying to find the requested field.
* @param logger
*/
private Field findField( final SimpleLogger logger )
throws InvocationTargetException
{
final Class<?> targetClass = this.componentClass;
final ClassLoader targetClasslLoader = targetClass.getClassLoader();
final String targetPackage = getPackageName( targetClass );
Class<?> theClass = targetClass;
boolean acceptPrivate = true;
boolean acceptPackage = true;
while (true)
{
if ( logger.isLogEnabled( LogService.LOG_DEBUG ) )
{
logger.log( LogService.LOG_DEBUG,
"Locating field " + this.metadata.getField() + " in class " + theClass.getName(), null );
}
try
{
final Field field = getField( theClass, acceptPrivate, acceptPackage, logger );
if ( field != null )
{
return field;
}
}
catch ( SuitableMethodNotAccessibleException ex )
{
// log and return null
logger.log( LogService.LOG_ERROR,
"findField: Suitable but non-accessible field {0} found in class {1}, subclass of {2}", new Object[]
{ this.metadata.getField(), theClass.getName(), targetClass.getName() }, null );
break;
}
// if we get here, we have no field, so check the super class
theClass = theClass.getSuperclass();
if ( theClass == null )
{
break;
}
// super class field check ignores private fields and accepts
// package fields only if in the same package and package
// fields are (still) allowed
acceptPackage &= targetClasslLoader == theClass.getClassLoader()
&& targetPackage.equals( getPackageName( theClass ) );
// private fields will not be accepted any more in super classes
acceptPrivate = false;
}
// nothing found after all these years ...
return null;
}
/**
* Finds the field named in the {@link #fieldName} field in the given
* <code>targetClass</code>. If the target class has no acceptable field
* the class hierarchy is traversed until a field is found or the root
* of the class hierarchy is reached without finding a field.
*
*
* @param targetClass The class in which to look for the method
* @param acceptPrivate <code>true</code> if private fields should be
* considered.
* @param acceptPackage <code>true</code> if package private fields should
* be considered.
* @param logger
* @return The requested field or <code>null</code> if no acceptable field
* can be found in the target class or any super class.
* @throws InvocationTargetException If an unexpected Throwable is caught
* trying to find the requested field.
*/
private Field getField( final Class<?> clazz,
final boolean acceptPrivate,
final boolean acceptPackage,
final SimpleLogger logger )
throws SuitableMethodNotAccessibleException, InvocationTargetException
{
try
{
// find the declared field in this class
final Field field = clazz.getDeclaredField( this.metadata.getField() );
// accept public and protected fields only and ensure accessibility
if ( accept( field, acceptPrivate, acceptPackage ) )
{
return field;
}
// the method would fit the requirements but is not acceptable
throw new SuitableMethodNotAccessibleException();
}
catch ( NoSuchFieldException nsfe )
{
// thrown if no field is declared with the given name and
// parameters
if ( logger.isLogEnabled( LogService.LOG_DEBUG ) )
{
logger.log( LogService.LOG_DEBUG, "Declared Field {0}.{1} not found", new Object[]
{ clazz.getName(), this.metadata.getField() }, null );
}
}
catch ( NoClassDefFoundError cdfe )
{
// may be thrown if a method would be found but the signature
// contains throws declaration for an exception which cannot
// be loaded
if ( logger.isLogEnabled( LogService.LOG_WARNING ) )
{
StringBuffer buf = new StringBuffer();
buf.append( "Failure loooking up field " ).append( this.metadata.getField() );
buf.append( " in class class " ).append( clazz.getName() ).append( ". Assuming no such field." );
logger.log( LogService.LOG_WARNING, buf.toString(), cdfe );
}
}
catch ( SuitableMethodNotAccessibleException e)
{
throw e;
}
catch ( Throwable throwable )
{
// unexpected problem accessing the field, don't let everything
// blow up in this situation, just throw a declared exception
throw new InvocationTargetException( throwable, "Unexpected problem trying to get field " + this.metadata.getField() );
}
// caught and ignored exception, assume no field and continue search
return null;
}
/**
* Validate the field, type etc.
* @param f The field
* @param logger The logger
* @return The FieldInfo, the field info will have a type of {@link ParamType#ignore}
* if the field is not valid.
*/
private FieldInfo validateField( final Field f, final SimpleLogger logger )
{
if (f == null)
{
return new FieldInfo(f, ParamType.ignore);
}
final Class<?> fieldType = f.getType();
final Class<?> referenceType = ClassUtils.getClassFromComponentClassLoader(
this.componentClass, metadata.getInterface(), logger);
// ignore static fields
if ( Modifier.isStatic(f.getModifiers()))
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must not be static", new Object[]
{metadata.getField(), this.componentClass}, null );
return new FieldInfo(f, ParamType.ignore);
}
ParamType type = null;
// unary reference
if ( !metadata.isMultiple() )
{
if ( fieldType.isAssignableFrom(referenceType) )
{
type = ParamType.serviceType;
}
else if ( fieldType == ClassUtils.SERVICE_REFERENCE_CLASS )
{
type = ParamType.serviceReference;
}
else if ( fieldType == ClassUtils.COMPONENTS_SERVICE_OBJECTS_CLASS )
{
type = ParamType.serviceObjects;
}
else if ( fieldType == ClassUtils.MAP_CLASS )
{
type = ParamType.map;
}
else if ( fieldType == ClassUtils.MAP_ENTRY_CLASS )
{
type = ParamType.tuple;
}
else
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} has unsupported type {2}", new Object[]
{metadata.getField(), this.componentClass, fieldType.getName()}, null );
type = ParamType.ignore;
}
// if the field is dynamic, it has to be volatile (field is ignored, case logged) (112.3.8.1)
if ( !metadata.isStatic() && !Modifier.isVolatile(f.getModifiers()) ) {
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must be declared volatile to handle a dynamic reference", new Object[]
{metadata.getField(), this.componentClass}, null );
type = ParamType.ignore;
}
// the field must not be final (field is ignored, case logged) (112.3.8.1)
if ( Modifier.isFinal(f.getModifiers()) )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must not be declared as final", new Object[]
{metadata.getField(), this.componentClass}, null );
type = ParamType.ignore;
}
}
else
{
if ( ReferenceMetadata.FIELD_VALUE_TYPE_SERVICE.equals(metadata.getFieldCollectionType()) )
{
type = ParamType.serviceType;
}
else if ( ReferenceMetadata.FIELD_VALUE_TYPE_REFERENCE.equals(metadata.getFieldCollectionType()) )
{
type = ParamType.serviceReference;
}
else if ( ReferenceMetadata.FIELD_VALUE_TYPE_SERVICEOBJECTS.equals(metadata.getFieldCollectionType()) )
{
type = ParamType.serviceObjects;
}
else if ( ReferenceMetadata.FIELD_VALUE_TYPE_PROPERTIES.equals(metadata.getFieldCollectionType()) )
{
type = ParamType.map;
}
else if ( ReferenceMetadata.FIELD_VALUE_TYPE_TUPLE.equals(metadata.getFieldCollectionType()) )
{
type = ParamType.tuple;
}
// multiple cardinality, field type must be collection or subtype
if ( !ClassUtils.COLLECTION_CLASS.isAssignableFrom(fieldType) )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} has unsupported type {2}", new Object[]
{metadata.getField(), this.componentClass, fieldType.getName()}, null );
type = ParamType.ignore;
}
// additional checks for replace strategy:
if ( metadata.isReplace() )
{
// if the field is dynamic wit has to be volatile (field is ignored, case logged) (112.3.8.1)
if ( !metadata.isStatic() && !Modifier.isVolatile(f.getModifiers()) )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must be declared volatile to handle a dynamic reference", new Object[]
{metadata.getField(), this.componentClass}, null );
type = ParamType.ignore;
}
// replace strategy: field must not be final (field is ignored, case logged) (112.3.8.1)
// only collection and list allowed
if ( fieldType != ClassUtils.LIST_CLASS && fieldType != ClassUtils.COLLECTION_CLASS )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} has unsupported type {2}."+
" It must be one of java.util.Collection or java.util.List.",
new Object[] {metadata.getField(), this.componentClass, fieldType.getName()}, null );
type = ParamType.ignore;
}
if ( Modifier.isFinal(f.getModifiers()) )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must not be declared as final", new Object[]
{metadata.getField(), this.componentClass}, null );
type = ParamType.ignore;
}
}
}
// static references only allowed for replace strategy
if ( metadata.isStatic() && !metadata.isReplace() )
{
logger.log( LogService.LOG_ERROR, "Update strategy for field {0} in component {1} only allowed for non static field references.", new Object[]
{metadata.getField(), this.componentClass}, null );
type = ParamType.ignore;
}
return new FieldInfo(f, type);
}
private enum METHOD_TYPE
{
BIND,
UNBIND,
UPDATED
};
@SuppressWarnings("rawtypes")
private 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());
}
}
private Object getValue(final ComponentContextImpl key,
final RefPair<?, ?> refPair)
{
final Object obj;
switch ( this.valueType )
{
case serviceType : obj = refPair.getServiceObject(key); break;
case serviceReference : obj = refPair.getRef(); break;
case serviceObjects : obj = key.getComponentServiceObjectsHelper().getServiceObjects(refPair.getRef()); break;
case map : obj = new ReadOnlyDictionary( refPair.getRef() ); break;
case tuple : final Object tupleKey = new ReadOnlyDictionary( refPair.getRef() );
final Object tupleValue = refPair.getServiceObject(key);
obj = new MapEntryImpl(tupleKey, tupleValue, refPair.getRef());
break;
default: obj = null;
}
return obj;
}
private boolean initField(final Object componentInstance,
final SimpleLogger logger )
{
if ( valueType == ParamType.ignore )
{
return true;
}
try
{
if ( metadata.isMultiple() )
{
if ( metadata.isReplace() )
{
this.setFieldValue(componentInstance, new CopyOnWriteArrayList<Object>());
}
else
{
final Class<?> fieldType = this.field.getType();
// update strategy: if DS implementation provides collection implementation
// only list and collection are allowed, field must not be final
final Object providedImpl = this.getFieldValue(componentInstance);
if ( providedImpl == null)
{
if ( Modifier.isFinal(this.field.getModifiers()) )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} must not be declared as final", new Object[]
{metadata.getField(), this.componentClass}, null );
valueType = ParamType.ignore;
return true;
}
if ( fieldType != ClassUtils.LIST_CLASS && fieldType != ClassUtils.COLLECTION_CLASS )
{
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} has unsupported type {2}."+
" It must be one of java.util.Collection or java.util.List.",
new Object[] {metadata.getField(), this.componentClass, fieldType.getName()}, null );
valueType = ParamType.ignore;
return true;
}
if ( fieldType == ClassUtils.LIST_CLASS )
{
this.setFieldValue(componentInstance, new CopyOnWriteArrayList<Object>());
}
else
{
this.setFieldValue(componentInstance, new CopyOnWriteArraySet<Object>());
}
}
}
}
else
{
// only optional field need initialization
if ( metadata.isOptional() )
{
// null the field if optional and unary
this.setFieldValue(componentInstance, null);
}
}
}
catch ( final InvocationTargetException ite)
{
valueType = ParamType.ignore;
logger.log( LogService.LOG_ERROR, "Field {0} in component {1} can't be initialized.",
new Object[] {metadata.getField(), this.componentClass}, ite );
return false;
}
return true;
}
private Collection<Object> getReplaceCollection(final BindParameters bp)
{
final List<Object> objects = new ArrayList<Object>();
for(final Object val : bp.getComponentContext().getBoundValues(metadata.getName()).values())
{
objects.add(val);
}
return objects;
}
private MethodResult updateField(final METHOD_TYPE mType,
final Object componentInstance,
final BindParameters bp,
final SimpleLogger logger )
throws InvocationTargetException
{
final ComponentContextImpl key = bp.getComponentContext();
final RefPair<?, ?> refPair = bp.getRefPair();
if ( !this.metadata.isMultiple() )
{
// unary references
// unbind needs only be done, if reference is dynamic and optional
if ( mType == METHOD_TYPE.UNBIND )
{
if ( this.metadata.isOptional() && !this.metadata.isStatic() )
{
// we only reset if it was previously set with this value
if ( bp.getComponentContext().getBoundValues(metadata.getName()).size() == 1 )
{
this.setFieldValue(componentInstance, null);
}
}
bp.getComponentContext().getBoundValues(metadata.getName()).remove(refPair);
}
// updated needs only be done, if the value type is map or tuple
// If it's a dynamic reference, the value can be updated
// for a static reference we need a reactivation
else if ( mType == METHOD_TYPE.UPDATED )
{
if ( this.valueType == ParamType.map || this.valueType == ParamType.tuple )
{
if ( this.metadata.isStatic() )
{
return MethodResult.REACTIVATE;
}
final Object obj = getValue(key, refPair);
this.setFieldValue(componentInstance, obj);
bp.getComponentContext().getBoundValues(metadata.getName()).put(refPair, obj);
}
}
// bind needs always be done
else
{
final Object obj = getValue(key, refPair);
this.setFieldValue(componentInstance, obj);
bp.getComponentContext().getBoundValues(metadata.getName()).put(refPair, obj);
}
}
else
{
// multiple references
// bind: replace or update the field
if ( mType == METHOD_TYPE.BIND )
{
final Object obj = getValue(key, refPair);
bp.getComponentContext().getBoundValues(metadata.getName()).put(refPair, obj);
if ( metadata.isReplace() )
{
this.setFieldValue(componentInstance, getReplaceCollection(bp));
}
else
{
@SuppressWarnings("unchecked")
final Collection<Object> col = (Collection<Object>)this.getFieldValue(componentInstance);
col.add(obj);
}
}
// unbind needs only be done, if reference is dynamic
else if ( mType == METHOD_TYPE.UNBIND)
{
if ( !metadata.isStatic() )
{
final Object obj = bp.getComponentContext().getBoundValues(metadata.getName()).remove(refPair);
if ( metadata.isReplace() )
{
this.setFieldValue(componentInstance, getReplaceCollection(bp));
}
else
{
@SuppressWarnings("unchecked")
final Collection<Object> col = (Collection<Object>)this.getFieldValue(componentInstance);
col.remove(obj);
}
}
}
// updated needs only be done, if the value type is map or tuple
else if ( mType == METHOD_TYPE.UPDATED)
{
if ( this.valueType == ParamType.map || this.valueType == ParamType.tuple )
{
if ( !this.metadata.isStatic() )
{
final Object obj = getValue(key, refPair);
final Object oldObj = bp.getComponentContext().getBoundValues(metadata.getName()).put(refPair, obj);
if ( metadata.isReplace() )
{
this.setFieldValue(componentInstance, getReplaceCollection(bp));
}
else
{
@SuppressWarnings("unchecked")
final Collection<Object> col = (Collection<Object>)this.getFieldValue(componentInstance);
col.add(obj);
col.remove(oldObj);
}
}
else
{
// if it's static we need to reactivate
return MethodResult.REACTIVATE;
}
}
}
}
return MethodResult.VOID;
}
private void setFieldValue(final Object componentInstance, final Object value)
throws InvocationTargetException
{
try
{
field.set(componentInstance, value);
}
catch ( final IllegalArgumentException iae )
{
throw new InvocationTargetException(iae);
}
catch ( final IllegalAccessException iae )
{
throw new InvocationTargetException(iae);
}
}
private Object getFieldValue(final Object componentInstance)
throws InvocationTargetException
{
try
{
return field.get(componentInstance);
}
catch ( final IllegalArgumentException iae )
{
throw new InvocationTargetException(iae);
}
catch ( final IllegalAccessException iae )
{
throw new InvocationTargetException(iae);
}
}
/**
* Returns <code>true</code> if the field is acceptable to be returned from the
* {@link #getField(Class, String, boolean, boolean, SimpleLogger)} and also
* makes the field accessible.
* <p>
* This method returns <code>true</code> if the field:
* <ul>
* <li>Is not static</li>
* <li>Is public or protected</li>
* <li>Is private and <code>acceptPrivate</code> is <code>true</code></li>
* <li>Is package private and <code>acceptPackage</code> is <code>true</code></li>
* </ul>
* <p>
*
* @param field The field to check
* @param acceptPrivate Whether a private field is acceptable
* @param acceptPackage Whether a package private field is acceptable
* @return whether the field is acceptable
*/
private static boolean accept( final Field field,
final boolean acceptPrivate,
final boolean acceptPackage )
{
// check modifiers now
final int mod = field.getModifiers();
// no static fields
if ( Modifier.isStatic( mod ) )
{
return true;
}
// accept public and protected fields
if ( Modifier.isPublic( mod ) || Modifier.isProtected( mod ) )
{
setAccessible( field );
return true;
}
// accept private if accepted
if ( Modifier.isPrivate( mod ) )
{
if ( acceptPrivate )
{
setAccessible( field );
return true;
}
return false;
}
// accept default (package)
if ( acceptPackage )
{
setAccessible( field );
return true;
}
// else don't accept
return false;
}
private static void setAccessible(final Field field)
{
AccessController.doPrivileged( new PrivilegedAction<Object>()
{
@Override
public Object run()
{
field.setAccessible( true );
return null;
}
} );
}
/**
* Returns the name of the package to which the class belongs or an
* empty string if the class is in the default package.
*/
public static String getPackageName( Class<?> clazz )
{
String name = clazz.getName();
int dot = name.lastIndexOf( '.' );
return ( dot > 0 ) ? name.substring( 0, dot ) : "";
}
/**
* Internal state interface.
*/
private static interface State
{
MethodResult invoke( final FieldHandler handler,
final METHOD_TYPE mType,
final Object componentInstance,
final BindParameters rawParameter,
final SimpleLogger logger )
throws InvocationTargetException;
boolean fieldExists( final FieldHandler handler, final SimpleLogger logger);
}
/**
* Initial state.
*/
private static class NotResolved implements State
{
private static final State INSTANCE = new NotResolved();
private void resolve( final FieldHandler handler, final SimpleLogger logger )
{
logger.log( LogService.LOG_DEBUG, "getting field: {0}", new Object[]
{handler.metadata.getField()}, null );
// resolve the field
FieldInfo fieldInfo = null;
try
{
fieldInfo = handler.validateField(handler.findField(logger), logger);
}
catch ( final InvocationTargetException ex )
{
logger.log( LogService.LOG_WARNING, "{0} cannot be found", new Object[]
{handler.metadata.getField()}, ex.getTargetException() );
fieldInfo = new FieldInfo(null, ParamType.ignore);
}
handler.setField(fieldInfo, logger );
}
@Override
public MethodResult invoke( final FieldHandler handler,
final METHOD_TYPE mType,
final Object componentInstance,
final BindParameters rawParameter,
SimpleLogger logger )
throws InvocationTargetException
{
resolve( handler, logger );
return handler.state.invoke( handler, mType, componentInstance, rawParameter, logger );
}
@Override
public boolean fieldExists( final FieldHandler handler, final SimpleLogger logger)
{
resolve( handler, logger );
return handler.state.fieldExists( handler, logger );
}
}
/**
* Final state of field couldn't be found or errors occured.
*/
private static class NotFound implements State
{
private static final State INSTANCE = new NotFound();
@Override
public MethodResult invoke( final FieldHandler handler,
final METHOD_TYPE mType,
final Object componentInstance,
final BindParameters rawParameter,
final SimpleLogger logger )
{
logger.log( LogService.LOG_ERROR, "Field [{0}] not found", new Object[]
{ handler.metadata.getField() }, null );
return null;
}
@Override
public boolean fieldExists( final FieldHandler handler, final SimpleLogger logger)
{
return false;
}
}
/**
* Final state of field could be found and is valid.
*/
private static class Resolved implements State
{
private static final State INSTANCE = new Resolved();
@Override
public MethodResult invoke( final FieldHandler handler,
final METHOD_TYPE mType,
final Object componentInstance,
final BindParameters rawParameter,
final SimpleLogger logger )
throws InvocationTargetException
{
return handler.updateField( mType, componentInstance, rawParameter, logger );
}
@Override
public boolean fieldExists( final FieldHandler handler, final SimpleLogger logger)
{
return true;
}
}
public boolean fieldExists( SimpleLogger logger )
{
return this.state.fieldExists( this, logger );
}
public static final class ReferenceMethodImpl
implements ReferenceMethod
{
private final METHOD_TYPE methodType;
private final FieldHandler handler;
public ReferenceMethodImpl(final METHOD_TYPE mt, final FieldHandler handler)
{
this.methodType = mt;
this.handler = handler;
}
@Override
public MethodResult invoke(Object componentInstance,
ComponentContextImpl<?> componentContext,
RefPair<?, ?> refPair,
MethodResult methodCallFailureResult,
SimpleLogger logger) {
return invoke(componentInstance,
new BindParameters(componentContext, refPair),
methodCallFailureResult,
logger);
}
public MethodResult invoke(final Object componentInstance,
final BindParameters rawParameter,
final MethodResult methodCallFailureResult,
final SimpleLogger logger)
{
if ( handler.valueType == ParamType.ignore )
{
return MethodResult.VOID;
}
try
{
return handler.state.invoke( handler,
methodType,
componentInstance,
rawParameter,
logger );
}
catch ( final InvocationTargetException ite )
{
logger.log( LogService.LOG_ERROR, "The {0} field has thrown an exception", new Object[]
{ handler.metadata.getField() }, ite.getCause() );
}
return methodCallFailureResult;
}
@Override
public <S, T> boolean getServiceObject(final ComponentContextImpl<S> key,
final RefPair<S, T> refPair,
final BundleContext context,
final SimpleLogger logger)
{
if ( methodType != METHOD_TYPE.UNBIND )
{
//??? this resolves which we need.... better way?
if ( refPair.getServiceObject(key) == null
&& handler.fieldExists( logger )
&& (handler.valueType == ParamType.serviceType || handler.valueType == ParamType.tuple ) )
{
return refPair.getServiceObject(key, context, logger);
}
}
return true;
}
}
public ReferenceMethod getBind()
{
return new ReferenceMethodImpl(METHOD_TYPE.BIND, this);
}
public ReferenceMethod getUnbind()
{
return new ReferenceMethodImpl(METHOD_TYPE.UNBIND, this);
}
public ReferenceMethod getUpdated()
{
return new ReferenceMethodImpl(METHOD_TYPE.UPDATED, this);
}
public InitReferenceMethod getInit()
{
if ( valueType == ParamType.ignore )
{
return null;
}
return new InitReferenceMethod()
{
@Override
public boolean init(final Object componentInstance, final SimpleLogger logger)
{
if ( fieldExists( logger ) )
{
return initField(componentInstance, logger);
}
return false;
}
};
}
private static final class FieldInfo {
private final Field m_field;
private final ParamType m_type;
public FieldInfo(Field m_field, ParamType m_type)
{
this.m_field = m_field;
this.m_type = m_type;
}
Field getField() {
return m_field;
}
ParamType getType() {
return m_type;
}
}
}