blob: 755b0dcf3db69f949ccccd4947bc3326f1c7eb62 [file] [log] [blame]
/*
* 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.methods;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.felix.scr.impl.inject.ActivatorParameter;
import org.apache.felix.scr.impl.inject.LifecycleMethod;
import org.apache.felix.scr.impl.inject.MethodResult;
import org.apache.felix.scr.impl.inject.ScrComponentContext;
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.DSVersion;
import org.osgi.service.log.LogService;
public class ActivateMethod extends BaseMethod<ActivatorParameter, Object> implements LifecycleMethod
{
protected final boolean m_supportsInterfaces;
public ActivateMethod( final String methodName,
final boolean methodRequired,
final Class<?> componentClass,
final DSVersion dsVersion,
final boolean configurableServiceProperties,
final boolean supportsInterfaces)
{
super( methodName, methodRequired, componentClass, dsVersion, configurableServiceProperties );
m_supportsInterfaces = supportsInterfaces;
}
@Override
protected MethodInfo<Object> doFindMethod( final Class<?> targetClass,
final boolean acceptPrivate,
final boolean acceptPackage,
final ComponentLogger logger )
throws SuitableMethodNotAccessibleException, InvocationTargetException
{
boolean suitableMethodNotAccessible = false;
try
{
// find the declared method in this class
final Method method = getMethod( targetClass, getMethodName(), new Class[]
{ ClassUtils.COMPONENT_CONTEXT_CLASS }, acceptPrivate, acceptPackage, logger );
if ( method != null )
{
return new MethodInfo<>(method);
}
}
catch ( SuitableMethodNotAccessibleException thrown )
{
logger.log( LogService.LOG_DEBUG, "SuitableMethodNotAccessible", thrown );
suitableMethodNotAccessible = true;
}
if (getDSVersion().isDS11())
{
List<Method> methods = getSortedMethods( targetClass);
for (Method m: methods)
{
final Class<?>[] parameterTypes = m.getParameterTypes();
if (parameterTypes.length == 1)
{
Class<?> type = parameterTypes[0];
//single parameter method with parameter ComponentContext will already have been found.
if (type == ClassUtils.BUNDLE_CONTEXT_CLASS)
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
if (getDSVersion().isDS13() && isAnnotation(type))
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
if (type == ClassUtils.MAP_CLASS)
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
if (type == int.class)
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
if (type == Integer.class)
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
}
else if (parameterTypes.length > 1)
{
boolean accept = true;
for (Class<?> type: parameterTypes)
{
accept = type == ClassUtils.COMPONENT_CONTEXT_CLASS
|| type == ClassUtils.BUNDLE_CONTEXT_CLASS
|| type == ClassUtils.MAP_CLASS
|| ( isDeactivate() && ( type == int.class || type == Integer.class))
|| ( getDSVersion().isDS13() && isAnnotation(type));
if ( !accept )
{
break;
}
}
if (accept)
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
}
else //no parameters
{
if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) )
{
return new MethodInfo<>(m);
}
suitableMethodNotAccessible = true;
}
}
}
if ( suitableMethodNotAccessible )
{
throw new SuitableMethodNotAccessibleException();
}
return null;
}
@Override
protected void setTypes(Object types)
{
// Don't care about types
}
boolean isDeactivate()
{
return false;
}
/**
* returns the declared methods of the target class, with the correct name, sorted by number of parameters ( no parameters last)
* @param targetClass class to examine methods of
* @return sorted methods of correct name;
*/
List<Method> getSortedMethods(Class<?> targetClass)
{
List<Method> result = new ArrayList<>();
Method[] methods = targetClass.getDeclaredMethods();
for (Method m: methods)
{
if (m.getName().equals(getMethodName()))
{
result.add(m);
}
}
Collections.sort(result, new Comparator<Method>(){
@Override
public int compare(Method m1, Method m2)
{
final int l1 = m1.getParameterTypes().length;
final int l2 = m2.getParameterTypes().length;
if ( l1 == 0)
{
return l2;
}
if ( l2 == 0)
{
return -l1;
}
if (l1 == 1 && l2 == 1)
{
final Class<?> t1 = m1.getParameterTypes()[0];
final Class<?> t2 = m2.getParameterTypes()[0];
//t1, t2 can't be equal
if (t1 == ClassUtils.COMPONENT_CONTEXT_CLASS) return -1;
if (t2 == ClassUtils.COMPONENT_CONTEXT_CLASS) return 1;
if (t1 == ClassUtils.BUNDLE_CONTEXT_CLASS) return -1;
if (t2 == ClassUtils.BUNDLE_CONTEXT_CLASS) return 1;
if (isAnnotation(t1)) return isAnnotation(t2)? 0: -1;
if (isAnnotation(t2)) return 1;
if (t1 == ClassUtils.MAP_CLASS) return -1;
if (t2 == ClassUtils.MAP_CLASS) return 1;
if (t1 == int.class) return -1;
if (t2 == int.class) return 1;
if (t1 == Integer.class) return -1;
if (t2 == Integer.class) return 1;
return 0;
}
return l1 - l2;
}
});
return result;
}
private boolean isAnnotation(final Class<?> t1)
{
return t1.isAnnotation() || (m_supportsInterfaces && t1.isInterface() && !(t1 == ClassUtils.MAP_CLASS));
}
@Override
protected Object[] getParameters( Method method, ActivatorParameter rawParameter )
{
final Class<?>[] parameterTypes = method.getParameterTypes();
final ActivatorParameter ap = rawParameter;
final Object[] param = new Object[parameterTypes.length];
for ( int i = 0; i < param.length; i++ )
{
if ( parameterTypes[i] == ClassUtils.COMPONENT_CONTEXT_CLASS )
{
param[i] = ap.getComponentContext();
}
else if ( parameterTypes[i] == ClassUtils.BUNDLE_CONTEXT_CLASS )
{
param[i] = ap.getComponentContext().getBundleContext();
}
else if ( parameterTypes[i] == ClassUtils.MAP_CLASS )
{
// note: getProperties() returns a ReadOnlyDictionary which is a Map
param[i] = ap.getComponentContext().getProperties();
}
else if ( parameterTypes[i] == ClassUtils.INTEGER_CLASS || parameterTypes[i] == Integer.TYPE )
{
param[i] = ap.getReason();
}
else
{
param[i] = Annotations.toObject(parameterTypes[i],
(Map<String, Object>) ap.getComponentContext().getProperties(),
ap.getComponentContext().getBundleContext().getBundle(), m_supportsInterfaces);
}
}
return param;
}
@Override
protected String getMethodNamePrefix()
{
return "activate";
}
/**
* @see org.apache.felix.scr.impl.inject.LifecycleMethod#invoke(Object,
* ScrComponentContext, int, MethodResult)
*/
@Override
public MethodResult invoke(final Object componentInstance,
final ScrComponentContext componentContext,
final int reason,
final MethodResult methodCallFailureResult) {
return invoke(componentInstance, new ActivatorParameter(componentContext, reason), methodCallFailureResult);
}
@Override
public MethodResult invoke(final Object componentInstance,
final ActivatorParameter rawParameter,
final MethodResult methodCallFailureResult)
{
if (methodExists( rawParameter.getComponentContext().getLogger() ))
{
return super.invoke(componentInstance, rawParameter, methodCallFailureResult );
}
return null;
}
}