Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BaseMethod.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BaseMethod.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BaseMethod.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BaseMethod.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,624 @@ +/* + * 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.helper; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.Arrays; +import java.util.Map; + +import org.apache.felix.scr.impl.metadata.DSVersion; +import org.osgi.service.log.LogService; + + +/** + * Component method to be invoked on service (un)binding. + */ +public abstract class BaseMethod<P> +{ + + private final DSVersion dsVersion; + private final boolean configurableServiceProperties; + + private final String m_methodName; + private final Class<?> m_componentClass; + + private volatile Method m_method; + + private final boolean m_methodRequired; + + private volatile State m_state; + + protected BaseMethod( final String methodName, + final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties ) + { + this( methodName, methodName != null, componentClass, dsVersion, configurableServiceProperties ); + } + + + protected BaseMethod( final String methodName, + final boolean methodRequired, final Class<?> componentClass, final DSVersion dsVersion, final boolean configurableServiceProperties ) + { + m_methodName = methodName; + m_methodRequired = methodRequired; + m_componentClass = componentClass; + this.dsVersion = dsVersion; + this.configurableServiceProperties = configurableServiceProperties; + if ( m_methodName == null ) + { + m_state = NotApplicable.INSTANCE; + } + else + { + m_state = NotResolved.INSTANCE; + } + } + + protected final DSVersion getDSVersion() + { + return dsVersion; + } + + + protected final boolean isDS12Felix() + { + return configurableServiceProperties; + } + + + protected final String getMethodName() + { + return m_methodName; + } + + final Method getMethod() + { + return m_method; + } + + protected final Class<?> getComponentClass() + { + return m_componentClass; + } + + + void setMethod( Method method, SimpleLogger logger ) + { + this.m_method = method; + + if ( method != null ) + { + m_state = Resolved.INSTANCE; + logger.log( LogService.LOG_DEBUG, "Found {0} method: {1}", new Object[] + { getMethodNamePrefix(), method }, null ); + } + else if ( m_methodRequired ) + { + m_state = NotFound.INSTANCE; + logger.log(LogService.LOG_ERROR, "{0} method [{1}] not found; Component will fail", + new Object[] + { getMethodNamePrefix(), getMethodName() }, null); + } + else + { + // optional method not found, log as DEBUG and ignore + logger.log( LogService.LOG_DEBUG, "{0} method [{1}] not found, ignoring", new Object[] + { getMethodNamePrefix(), getMethodName() }, null ); + m_state = NotApplicable.INSTANCE; + } + } + + + State getState() + { + return m_state; + } + + + /** + * Finds the method named in the {@link #m_methodName} field in the given + * <code>targetClass</code>. If the target class has no acceptable method + * the class hierarchy is traversed until a method is found or the root + * of the class hierarchy is reached without finding a method. + * + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class or any super class. + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + * @param logger + */ + private Method findMethod( SimpleLogger logger ) throws InvocationTargetException + { + boolean acceptPrivate = getDSVersion().isDS11(); + boolean acceptPackage = getDSVersion().isDS11(); + + final Class<?> targetClass = getComponentClass(); + final ClassLoader targetClasslLoader = targetClass.getClassLoader(); + final String targetPackage = getPackageName( targetClass ); + Class<?> theClass = targetClass; + + while (true) + { + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "Locating method " + getMethodName() + " in class " + theClass.getName(), null ); + } + + try + { + Method method = doFindMethod( theClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + // log and return null + logger.log( LogService.LOG_ERROR, + "findMethod: Suitable but non-accessible method {0} found in class {1}, subclass of {2}", new Object[] + { getMethodName(), theClass.getName(), targetClass.getName() }, null ); + break; + } + + // if we get here, we have no method, so check the super class + theClass = theClass.getSuperclass(); + if ( theClass == null ) + { + break; + } + + // super class method check ignores private methods and accepts + // package methods only if in the same package and package + // methods are (still) allowed + acceptPackage &= targetClasslLoader == theClass.getClassLoader() + && targetPackage.equals( getPackageName( theClass ) ); + + // private methods will not be accepted any more in super classes + acceptPrivate = false; + } + + // nothing found after all these years ... + return null; + } + + + protected abstract Method doFindMethod( final Class<?> targetClass, final boolean acceptPrivate, + final boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException, InvocationTargetException; + + + private MethodResult invokeMethod( final Object componentInstance, final P rawParameter, SimpleLogger logger ) + throws InvocationTargetException + { + try + { + if ( componentInstance != null ) + { + final Object[] params = getParameters(m_method, rawParameter); + logger.log( LogService.LOG_DEBUG, "invoking {0}: {1}: parameters {2}", new Object[] + { getMethodNamePrefix(), getMethodName(), Arrays.asList( params ) }, null ); + Object result = m_method.invoke(componentInstance, params); + logger.log( LogService.LOG_DEBUG, "invoked {0}: {1}", new Object[] + { getMethodNamePrefix(), getMethodName() }, null ); + return new MethodResult((m_method.getReturnType() != Void.TYPE), (Map<String, Object>) result); + } + else + { + logger.log( LogService.LOG_WARNING, "Method {0}: {1} cannot be called on null object", + new Object[] + { getMethodNamePrefix(), getMethodName() }, null ); + } + } + catch ( IllegalStateException ise ) + { + logger.log( LogService.LOG_DEBUG, ise.getMessage(), null ); + return null; + } + catch ( IllegalAccessException ex ) + { + // 112.3.1 If the method is not is not declared protected or + // public, SCR must log an error message with the log service, + // if present, and ignore the method + logger.log( LogService.LOG_DEBUG, "Method {0} cannot be called", new Object[] + { getMethodName() }, ex ); + } + catch ( InvocationTargetException ex ) + { + throw ex; + } + catch ( Throwable t ) + { + throw new InvocationTargetException( t ); + } + + // assume success (also if the method is not available or accessible) + return MethodResult.VOID; // TODO: or null ?? + } + + protected boolean returnValue() + { + // allow returning Map if declared as DS 1.2-Felix or newer + return isDS12Felix(); + } + + /** + * Returns the parameter array created from the <code>rawParameter</code> + * using the actual parameter type list of the <code>method</code>. + * @param method + * @param rawParameter + * @return + * @throws IllegalStateException If the required parameters cannot be + * extracted from the <code>rawParameter</code> + */ + protected abstract Object[] getParameters( Method method, P rawParameter ); + + + protected String getMethodNamePrefix() + { + return ""; + } + + + //---------- Helpers + + /** + * Finds the named public or protected method in the given class or any + * super class. If such a method is found, its accessibility is enfored by + * calling the <code>Method.setAccessible</code> method if required and + * the method is returned. Enforcing accessibility is required to support + * invocation of protected methods. + * + * + * @param clazz The <code>Class</code> which provides the method. + * @param name The name of the method. + * @param parameterTypes The parameters to the method. Passing + * <code>null</code> is equivalent to using an empty array. + * + * @param logger + * @return The named method with enforced accessibility or <code>null</code> + * if no such method exists in the class. + * + * @throws SuitableMethodNotAccessibleException If method with the given + * name taking the parameters is found in the class but the method + * is not accessible. + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to access the desired method. + */ + public /* static */ Method getMethod( Class<?> clazz, String name, Class[] parameterTypes, boolean acceptPrivate, + boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException, + InvocationTargetException + { + try + { + // find the declared method in this class + Method method = clazz.getDeclaredMethod( name, parameterTypes ); + + // accept public and protected methods only and ensure accessibility + if ( accept( method, acceptPrivate, acceptPackage, returnValue() ) ) + { + return method; + } + + // the method would fit the requirements but is not acceptable + throw new SuitableMethodNotAccessibleException(); + } + catch ( NoSuchMethodException nsme ) + { + // thrown if no method is declared with the given name and + // parameters + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + String argList = ( parameterTypes != null ) ? Arrays.asList( parameterTypes ).toString() : ""; + logger.log( LogService.LOG_DEBUG, "Declared Method {0}.{1}({2}) not found", new Object[] + { clazz.getName(), name, argList }, 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 method " ).append( name ).append( '(' ); + for ( int i = 0; parameterTypes != null && i < parameterTypes.length; i++ ) + { + buf.append( parameterTypes[i].getName() ); + if ( i > 0 ) + { + buf.append( ", " ); + } + } + buf.append( ") in class class " ).append( clazz.getName() ).append( ". Assuming no such method." ); + logger.log( LogService.LOG_WARNING, buf.toString(), cdfe ); + } + } + catch ( SuitableMethodNotAccessibleException e) + { + throw e; + } + catch ( Throwable throwable ) + { + // unexpected problem accessing the method, don't let everything + // blow up in this situation, just throw a declared exception + throw new InvocationTargetException( throwable, "Unexpected problem trying to get method " + name ); + } + + // caught and ignored exception, assume no method and continue search + return null; + } + + + /** + * Returns <code>true</code> if the method is acceptable to be returned from the + * {@link #getMethod(Class, String, Class[], boolean, boolean, SimpleLogger)} and also + * makes the method accessible. + * <p> + * This method returns <code>true</code> iff: + * <ul> + * <li>The method has <code>void</code> return type</li> + * <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> + * This method is package private for unit testing purposes. It is not + * meant to be called from client code. + * + * + * @param method The method to check + * @param acceptPrivate Whether a private method is acceptable + * @param acceptPackage Whether a package private method is acceptable + * @param allowReturnValue whether the method can return a value (to update service registration properties) + * @return whether the method is acceptable + */ + protected static boolean accept( final Method method, boolean acceptPrivate, boolean acceptPackage, boolean allowReturnValue ) + { + if (!(Void.TYPE == method.getReturnType() || (ClassUtils.MAP_CLASS == method.getReturnType() && allowReturnValue))) + { + return false; + } + + // check modifiers now + int mod = method.getModifiers(); + + // no static method + if ( Modifier.isStatic( mod ) ) + { + return false; + } + + // accept public and protected methods + if ( Modifier.isPublic( mod ) || Modifier.isProtected( mod ) ) + { + setAccessible( method ); + return true; + } + + // accept private if accepted + if ( Modifier.isPrivate( mod ) ) + { + if ( acceptPrivate ) + { + setAccessible( method ); + return true; + } + + return false; + } + + // accept default (package) + if ( acceptPackage ) + { + setAccessible( method ); + return true; + } + + // else don't accept + return false; + } + + + private static void setAccessible(final Method method) + { + AccessController.doPrivileged( new PrivilegedAction<Object>() + { + public Object run() + { + method.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 ) : ""; + } + + + //---------- State management ------------------------------------ + + /** + * Calls the declared method on the given component with the provided + * method call arguments. + * + * + * + * + * @param componentInstance The component instance on which to call the + * method + * @param rawParameter The parameter container providing the actual + * parameters to provide to the called method + * @param methodCallFailureResult The result to return from this method if + * calling the method resulted in an exception. + * + * @param logger + * @return <code>true</code> if the method was called successfully or the + * method was not found and was not required. <code>false</code> if + * the method was not found but required. + * <code>methodCallFailureResult</code> is returned if the method was + * found and called, but the method threw an exception. + */ + public MethodResult invoke( final Object componentInstance, final P rawParameter, + final MethodResult methodCallFailureResult, SimpleLogger logger ) + { + try + { + return m_state.invoke( this, componentInstance, rawParameter, logger ); + } + catch ( InvocationTargetException ite ) + { + logger.log( LogService.LOG_ERROR, "The {0} method has thrown an exception", new Object[] + { getMethodName() }, ite.getCause() ); + } + + return methodCallFailureResult; + } + + + public boolean methodExists( SimpleLogger logger ) + { + return m_state.methodExists( this, logger ); + } + + private static interface State + { + + <P> MethodResult invoke( final BaseMethod<P> baseMethod, final Object componentInstance, final P rawParameter, SimpleLogger logger ) + throws InvocationTargetException; + + + <P> boolean methodExists( final BaseMethod<P> baseMethod, SimpleLogger logger ); + } + + private static class NotApplicable implements State + { + + private static final State INSTANCE = new NotApplicable(); + + + public <P> MethodResult invoke( final BaseMethod<P> baseMethod, final Object componentInstance, final P rawParameter, SimpleLogger logger ) + { + return MethodResult.VOID; + } + + + public <P> boolean methodExists( final BaseMethod<P> baseMethod, SimpleLogger logger ) + { + return true; + } + } + + private static class NotResolved implements State + { + private static final State INSTANCE = new NotResolved(); + + + private synchronized <P> void resolve( final BaseMethod<P> baseMethod, SimpleLogger logger ) + { + logger.log( LogService.LOG_DEBUG, "getting {0}: {1}", new Object[] + {baseMethod.getMethodNamePrefix(), baseMethod.getMethodName()}, null ); + + // resolve the method + Method method = null; + try + { + method = baseMethod.findMethod( logger ); + } + catch ( InvocationTargetException ex ) + { + logger.log( LogService.LOG_WARNING, "{0} cannot be found", new Object[] + {baseMethod.getMethodName()}, ex.getTargetException() ); + } + + baseMethod.setMethod( method, logger ); + } + + + public <P> MethodResult invoke( final BaseMethod<P> baseMethod, final Object componentInstance, final P rawParameter, SimpleLogger logger ) + throws InvocationTargetException + { + resolve( baseMethod, logger ); + return baseMethod.getState().invoke( baseMethod, componentInstance, rawParameter, logger ); + } + + + public <P> boolean methodExists( final BaseMethod<P> baseMethod, SimpleLogger logger ) + { + resolve( baseMethod, logger ); + return baseMethod.getState().methodExists( baseMethod, logger ); + } + } + + private static class NotFound implements State + { + private static final State INSTANCE = new NotFound(); + + + public <P> MethodResult invoke( final BaseMethod<P> baseMethod, final Object componentInstance, final P rawParameter, SimpleLogger logger ) + { + // 112.3.1 If the method is not found , SCR must log an error + // message with the log service, if present, and ignore the + // method + logger.log( LogService.LOG_ERROR, "{0} method [{1}] not found", new Object[] + { baseMethod.getMethodNamePrefix(), baseMethod.getMethodName() }, null ); + return null; + } + + + public <P> boolean methodExists( final BaseMethod<P> baseMethod, SimpleLogger logger ) + { + return false; + } + } + + private static class Resolved implements State + { + private static final State INSTANCE = new Resolved(); + + + public <P> MethodResult invoke( final BaseMethod<P> baseMethod, final Object componentInstance, final P rawParameter, SimpleLogger logger ) + throws InvocationTargetException + { + return baseMethod.invokeMethod( componentInstance, rawParameter, logger ); + } + + + public <P> boolean methodExists( final BaseMethod<P> baseMethod, SimpleLogger logger ) + { + return true; + } + } +}
Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethod.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethod.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethod.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethod.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,694 @@ +/* + * 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.helper; + + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.apache.felix.scr.impl.manager.ComponentContextImpl; +import org.apache.felix.scr.impl.manager.RefPair; +import org.apache.felix.scr.impl.metadata.DSVersion; +import org.apache.felix.scr.impl.metadata.ReferenceMetadata; +import org.apache.felix.scr.impl.metadata.ReferenceMetadata.ReferenceScope; +import org.osgi.framework.BundleContext; +import org.osgi.service.log.LogService; + + +/** + * Component method to be invoked on service (un)binding. + */ +public class BindMethod extends BaseMethod<BindParameters> +implements org.apache.felix.scr.impl.helper.ReferenceMethod +{ + private final String m_referenceClassName; + + private final ReferenceMetadata.ReferenceScope m_referenceScope; + + private enum ParamType { + serviceReference, + serviceObjects, + serviceType, + map + } + + //initialized for cases where there is no method. + private volatile List<ParamType> m_paramTypes = Collections.emptyList(); + + + public BindMethod( final String methodName, + final Class<?> componentClass, final String referenceClassName, final DSVersion dsVersion, final boolean configurableServiceProperties, ReferenceScope referenceScope ) + { + super( methodName, componentClass, dsVersion, configurableServiceProperties ); + m_referenceClassName = referenceClassName; + m_referenceScope = referenceScope; + } + + + /** + * Finds the method named in the {@link #m_methodName} field in the given + * <code>targetClass</code>. If the target class has no acceptable method + * the class hierarchy is traversed until a method is found or the root + * of the class hierarchy is reached without finding a method. + * + * + * @param targetClass The class in which to look for the method + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class or any super class. + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + */ + @Override + protected Method doFindMethod( final Class<?> targetClass, + final boolean acceptPrivate, + final boolean acceptPackage, + final SimpleLogger logger ) + throws SuitableMethodNotAccessibleException, InvocationTargetException + { + // 112.3.1 The method is searched for using the following priority + // 1 - ServiceReference single parameter + // 2 - DS 1.3+ : ComponentServiceObjects single parameter + // 3 - Service object single parameter + // 4 - Service interface assignment compatible single parameter + // 5 - DS 1.3+ : Single argument with Map + // 6 - DS 1.1/DS 1.2 : two parameters, first the type of or assignment compatible with the service, the second Map + // 7 - DS 1.3+ : one or more parameters of types ServiceReference, ServiceObjects, interface type, + // or assignment compatible to interface type, in any order. + + // flag indicating a suitable but inaccessible method has been found + boolean suitableMethodNotAccessible = false; + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "doFindMethod: Looking for method " + targetClass.getName() + "." + getMethodName(), null ); + } + + // Case 1 - Service reference parameter + Method method; + try + { + method = getServiceReferenceMethod( targetClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + m_paramTypes = Collections.singletonList(ParamType.serviceReference); + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + + // Case 2 - ComponentServiceObjects parameter + if ( getDSVersion().isDS13() ) + { + try + { + method = getComponentObjectsMethod( targetClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + m_paramTypes = Collections.singletonList(ParamType.serviceObjects); + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + } + + // for further methods we need the class of the service object + final Class<?> parameterClass = ClassUtils.getClassFromComponentClassLoader( targetClass, m_referenceClassName, logger ); + if ( parameterClass != null ) + { + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( + LogService.LOG_DEBUG, + "doFindMethod: No method taking ServiceReference found, checking method taking " + + parameterClass.getName(), null ); + } + + // Case 3 - Service object parameter + try + { + method = getServiceObjectMethod( targetClass, parameterClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + m_paramTypes = Collections.singletonList(ParamType.serviceType); + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + + // Case 4 - Service interface assignment compatible methods + try + { + method = getServiceObjectAssignableMethod( targetClass, parameterClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + m_paramTypes = Collections.singletonList(ParamType.serviceType); + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + + // Case 5 - DS 1.3+ : Single argument with Map + if ( getDSVersion().isDS13() ) + { + try + { + method = getMapMethod( targetClass, parameterClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + m_paramTypes = Collections.singletonList(ParamType.map); + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + } + + // signatures taking a map are only supported starting with DS 1.1 + if ( getDSVersion().isDS11() && !getDSVersion().isDS13() ) + { + + // Case 6 - same as case 3, but + Map param (DS 1.1 only) + try + { + method = getServiceObjectWithMapMethod( targetClass, parameterClass, acceptPrivate, acceptPackage, logger ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + List<ParamType> paramTypes = new ArrayList<ParamType>(2); + paramTypes.add(ParamType.serviceType); + paramTypes.add(ParamType.map); + m_paramTypes = paramTypes; + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + + // Case 6 - same as case 4, but + Map param (DS 1.1 only) + try + { + method = getServiceObjectAssignableWithMapMethod( targetClass, parameterClass, acceptPrivate, + acceptPackage ); + if ( method != null ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + method, null ); + } + List<ParamType> paramTypes = new ArrayList<ParamType>(2); + paramTypes.add(ParamType.serviceType); + paramTypes.add(ParamType.map); + m_paramTypes = paramTypes; + return method; + } + } + catch ( SuitableMethodNotAccessibleException ex ) + { + suitableMethodNotAccessible = true; + } + + } + // Case 7 - Multiple parameters + if ( getDSVersion().isDS13() ) + { + for (Method m: targetClass.getDeclaredMethods()) + { + if (getMethodName().equals(m.getName())) { + Class<?>[] parameterTypes = m.getParameterTypes(); + boolean matches = true; + boolean specialMatch = true; + List<ParamType> paramTypes = new ArrayList<ParamType>(parameterTypes.length); + for (Class<?> paramType: parameterTypes) { + if (paramType == ClassUtils.SERVICE_REFERENCE_CLASS) + { + if (specialMatch && parameterClass == ClassUtils.SERVICE_REFERENCE_CLASS) + { + specialMatch = false; + paramTypes.add(ParamType.serviceType); + } + else + { + paramTypes.add(ParamType.serviceReference); + } + } + else if (paramType == ClassUtils.COMPONENTS_SERVICE_OBJECTS_CLASS) + { + if (specialMatch && parameterClass == ClassUtils.COMPONENTS_SERVICE_OBJECTS_CLASS) + { + specialMatch = false; + paramTypes.add(ParamType.serviceType); + } + else + { + paramTypes.add(ParamType.serviceObjects); + } + } + else if (paramType == Map.class) + { + if (specialMatch && parameterClass == Map.class) + { + specialMatch = false; + paramTypes.add(ParamType.serviceType); + } + else + { + paramTypes.add(ParamType.map); + } + } + else if (paramType.isAssignableFrom( parameterClass ) ) + { + paramTypes.add(ParamType.serviceType); + } + else + { + matches = false; + break; + } + } + if (matches) + { + if ( accept( m, acceptPrivate, acceptPackage, returnValue() ) ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "doFindMethod: Found Method " + m, null ); + } + m_paramTypes = paramTypes; + return m; + } + suitableMethodNotAccessible = true; + } + } + } + } + } + else if ( logger.isLogEnabled( LogService.LOG_WARNING ) ) + { + logger.log( + LogService.LOG_WARNING, + "doFindMethod: Cannot check for methods taking parameter class " + m_referenceClassName + ": " + + targetClass.getName() + " does not see it", null ); + } + + // if at least one suitable method could be found but none of + // the suitable methods are accessible, we have to terminate + if ( suitableMethodNotAccessible ) + { + logger.log( LogService.LOG_ERROR, + "doFindMethod: Suitable but non-accessible method found in class {0}", new Object[] + { targetClass.getName() }, null ); + throw new SuitableMethodNotAccessibleException(); + } + + // no method found + return null; + } + + /** + * Returns a method taking a single <code>ServiceReference</code> object + * as a parameter or <code>null</code> if no such method exists. + * + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + */ + private Method getServiceReferenceMethod( final Class<?> targetClass, boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger ) + throws SuitableMethodNotAccessibleException, InvocationTargetException + { + return getMethod( targetClass, getMethodName(), new Class[] + { ClassUtils.SERVICE_REFERENCE_CLASS }, acceptPrivate, acceptPackage, logger ); + } + + private Method getComponentObjectsMethod( final Class<?> targetClass, boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger ) + throws SuitableMethodNotAccessibleException, InvocationTargetException + { + return getMethod(targetClass, getMethodName(), + new Class[] { ClassUtils.COMPONENTS_SERVICE_OBJECTS_CLASS }, acceptPrivate, acceptPackage, + logger); + } + + + /** + * Returns a method taking a single parameter of the exact type declared + * for the service reference or <code>null</code> if no such method exists. + * + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + */ + private Method getServiceObjectMethod( final Class<?> targetClass, final Class<?> parameterClass, boolean acceptPrivate, + boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException, InvocationTargetException + { + return getMethod( targetClass, getMethodName(), new Class[] + { parameterClass }, acceptPrivate, acceptPackage, logger ); + } + + + /** + * Returns a method taking a single object whose type is assignment + * compatible with the declared service type or <code>null</code> if no + * such method exists. + * + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + */ + private Method getServiceObjectAssignableMethod( final Class<?> targetClass, final Class<?> parameterClass, + boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException + { + // Get all potential bind methods + Method candidateBindMethods[] = targetClass.getDeclaredMethods(); + boolean suitableNotAccessible = false; + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( + LogService.LOG_DEBUG, + "getServiceObjectAssignableMethod: Checking " + candidateBindMethods.length + + " declared method in class " + targetClass.getName(), null ); + } + + // Iterate over them + for ( int i = 0; i < candidateBindMethods.length; i++ ) + { + Method method = candidateBindMethods[i]; + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "getServiceObjectAssignableMethod: Checking " + method, null ); + } + + // Get the parameters for the current method + Class[] parameters = method.getParameterTypes(); + + // Select only the methods that receive a single + // parameter + // and a matching name + if ( parameters.length == 1 && method.getName().equals( getMethodName() ) ) + { + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, "getServiceObjectAssignableMethod: Considering " + method, null ); + } + + // Get the parameter type + final Class<?> theParameter = parameters[0]; + + // Check if the parameter type is ServiceReference + // or is assignable from the type specified by the + // reference's interface attribute + if ( theParameter.isAssignableFrom( parameterClass ) ) + { + if ( accept( method, acceptPrivate, acceptPackage, false ) ) + { + return method; + } + + // suitable method is not accessible, flag for exception + suitableNotAccessible = true; + } + else if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( + LogService.LOG_DEBUG, + "getServiceObjectAssignableMethod: Parameter failure: Required " + theParameter + "; actual " + + parameterClass.getName(), null ); + } + + } + } + + // if one or more suitable methods which are not accessible is/are + // found an exception is thrown + if ( suitableNotAccessible ) + { + throw new SuitableMethodNotAccessibleException(); + } + + // no method with assignment compatible argument found + return null; + } + + + /** + * Returns a method taking two parameters, the first being of the exact + * type declared for the service reference and the second being a + * <code>Map</code> or <code>null</code> if no such method exists. + * + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + */ + private Method getServiceObjectWithMapMethod( final Class<?> targetClass, final Class<?> parameterClass, + boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException, + InvocationTargetException + { + return getMethod( targetClass, getMethodName(), new Class[] + { parameterClass, ClassUtils.MAP_CLASS }, acceptPrivate, acceptPackage, logger ); + } + + + /** + * Returns a method taking two parameters, the first being an object + * whose type is assignment compatible with the declared service type and + * the second being a <code>Map</code> or <code>null</code> if no such + * method exists. + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + */ + private Method getServiceObjectAssignableWithMapMethod( final Class<?> targetClass, final Class<?> parameterClass, + boolean acceptPrivate, boolean acceptPackage ) throws SuitableMethodNotAccessibleException + { + // Get all potential bind methods + Method candidateBindMethods[] = targetClass.getDeclaredMethods(); + boolean suitableNotAccessible = false; + + // Iterate over them + for ( int i = 0; i < candidateBindMethods.length; i++ ) + { + final Method method = candidateBindMethods[i]; + final Class[] parameters = method.getParameterTypes(); + if ( parameters.length == 2 && method.getName().equals( getMethodName() ) ) + { + + // parameters must be refclass,map + if ( parameters[0].isAssignableFrom( parameterClass ) && parameters[1] == ClassUtils.MAP_CLASS ) + { + if ( accept( method, acceptPrivate, acceptPackage, false ) ) + { + return method; + } + + // suitable method is not accessible, flag for exception + suitableNotAccessible = true; + } + } + } + + // if one or more suitable methods which are not accessible is/are + // found an exception is thrown + if ( suitableNotAccessible ) + { + throw new SuitableMethodNotAccessibleException(); + } + + // no method with assignment compatible argument found + return null; + } + + /** + * Returns a method taking a single map parameter + * or <code>null</code> if no such method exists. + * + * + * @param targetClass The class in which to look for the method. Only this + * class is searched for the method. + * @param acceptPrivate <code>true</code> if private methods should be + * considered. + * @param acceptPackage <code>true</code> if package private methods should + * be considered. + * @param logger + * @return The requested method or <code>null</code> if no acceptable method + * can be found in the target class. + * @throws SuitableMethodNotAccessibleException If a suitable method was + * found which is not accessible + * @throws InvocationTargetException If an unexpected Throwable is caught + * trying to find the requested method. + */ + private Method getMapMethod( final Class<?> targetClass, final Class<?> parameterClass, + boolean acceptPrivate, boolean acceptPackage, SimpleLogger logger ) throws SuitableMethodNotAccessibleException, + InvocationTargetException + { + return getMethod( targetClass, getMethodName(), new Class[] + { ClassUtils.MAP_CLASS }, acceptPrivate, acceptPackage, logger ); + } + + public <S, T> boolean getServiceObject( ComponentContextImpl<S> key, RefPair<S, T> refPair, BundleContext context, SimpleLogger logger ) + { + //??? this resolves which we need.... better way? + if ( refPair.getServiceObject(key) == null && methodExists( logger ) ) + { + if ( m_paramTypes.contains(ParamType.serviceType) ) { + return refPair.getServiceObject(key, context, logger); + } + } + return true; + } + + @Override + protected Object[] getParameters( Method method, BindParameters bp ) + { + ComponentContextImpl key = bp.getComponentContext(); + Object[] result = new Object[ m_paramTypes.size()]; + RefPair<?, ?> refPair = bp.getRefPair(); + int i = 0; + for ( ParamType pt: m_paramTypes ) { + switch (pt) { + case serviceReference: + result[i++] = refPair.getRef(); + break; + + case serviceObjects: + result[i++] = ((ComponentServiceObjectsHelper)bp.getComponentContext().getComponentServiceObjectsHelper()).getServiceObjects(refPair.getRef()); + break; + + case map: + result[i++] = new ReadOnlyDictionary<String, Object>( refPair.getRef() ); + break; + + case serviceType: + result[i++] = refPair.getServiceObject(key); + break; + + default: throw new IllegalStateException("unexpected ParamType: " + pt); + + } + } + return result; + } + + + @Override + protected String getMethodNamePrefix() + { + return "bind"; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethods.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethods.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethods.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindMethods.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,79 @@ +/* + * 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.helper; + + +import org.apache.felix.scr.impl.metadata.DSVersion; +import org.apache.felix.scr.impl.metadata.ReferenceMetadata; + +/** + * @version $Rev: 1637793 $ $Date: 2014-11-10 06:51:04 +0100 (Mon, 10 Nov 2014) $ + */ +public class BindMethods implements ReferenceMethods +{ + private final ReferenceMethod m_bind; + private final ReferenceMethod m_updated; + private final ReferenceMethod m_unbind; + + public BindMethods( ReferenceMetadata m_dependencyMetadata, Class<?> instanceClass, + final DSVersion dsVersion, final boolean configurableServiceProperties ) + { + ReferenceMetadata.ReferenceScope referenceScope = m_dependencyMetadata.getScope(); + m_bind = new org.apache.felix.scr.impl.helper.BindMethod( + m_dependencyMetadata.getBind(), + instanceClass, + m_dependencyMetadata.getInterface(), + dsVersion, configurableServiceProperties, referenceScope + ); + m_updated = new org.apache.felix.scr.impl.helper.UpdatedMethod( + m_dependencyMetadata.getUpdated(), + instanceClass, + m_dependencyMetadata.getInterface(), + dsVersion, configurableServiceProperties, referenceScope + ); + m_unbind = new org.apache.felix.scr.impl.helper.UnbindMethod( + m_dependencyMetadata.getUnbind(), + instanceClass, + m_dependencyMetadata.getInterface(), + dsVersion, configurableServiceProperties, referenceScope + ); + } + + public ReferenceMethod getBind() + { + return m_bind; + } + + public ReferenceMethod getUnbind() + { + return m_unbind; + } + + public ReferenceMethod getUpdated() + { + return m_updated; + } + + public InitReferenceMethod getInit() + { + return null; + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindParameters.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindParameters.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindParameters.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/BindParameters.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,45 @@ +/* + * 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.helper; + +import org.apache.felix.scr.impl.manager.ComponentContextImpl; +import org.apache.felix.scr.impl.manager.RefPair; + +public class BindParameters +{ + + private final ComponentContextImpl<?> componentContext; + private final RefPair<?, ?> refPair; + public BindParameters(ComponentContextImpl<?> componentContext, RefPair<?, ?> refPair) + { + this.componentContext = componentContext; + this.refPair = refPair; + } + + public ComponentContextImpl<?> getComponentContext() + { + return componentContext; + } + + public RefPair<?, ?> getRefPair() + { + return refPair; + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ClassUtils.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ClassUtils.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ClassUtils.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ClassUtils.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,163 @@ +/* + * 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.helper; + + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +import org.apache.felix.scr.impl.Activator; +import org.osgi.framework.ServiceReference; +import org.osgi.service.component.ComponentServiceObjects; +import org.osgi.service.log.LogService; +import org.osgi.service.packageadmin.ExportedPackage; +import org.osgi.service.packageadmin.PackageAdmin; + + +/** + * Utility methods for class handling used by method and field references. + */ +public class ClassUtils +{ + + private static final Class<?> OBJECT_CLASS = Object.class; + + public static final Class<?> SERVICE_REFERENCE_CLASS = ServiceReference.class; + + public static final Class<?> COMPONENTS_SERVICE_OBJECTS_CLASS = ComponentServiceObjects.class; + + public static final Class<?> MAP_CLASS = Map.class; + public static final Class<?> MAP_ENTRY_CLASS = Map.Entry.class; + + public static final Class<?> COLLECTION_CLASS = Collection.class; + public static final Class<?> LIST_CLASS = List.class; + + /** + * Returns the class object representing the class of the field reference + * The class loader of the component class is used to load the service class. + * <p> + * It may well be possible, that the class loader of the target class cannot + * see the service object class, for example if the service reference is + * inherited from a component class of another bundle. + * + * @return The class object for the referred to service or <code>null</code> + * if the class loader of the <code>targetClass</code> cannot see that + * class. + */ + public static Class<?> getClassFromComponentClassLoader( + final Class<?> componentClass, + final String className, + final SimpleLogger logger ) + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( + LogService.LOG_DEBUG, + "getReferenceClass: Looking for interface class {0} through loader of {1}", + new Object[] {className, componentClass.getName()}, null ); + } + + try + { + // need the class loader of the target class, which may be the + // system classloader, which case getClassLoader may retur null + ClassLoader loader = componentClass.getClassLoader(); + if ( loader == null ) + { + loader = ClassLoader.getSystemClassLoader(); + } + + final Class<?> referenceClass = loader.loadClass( className ); + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: Found class {0}", new Object[] {referenceClass.getName()}, null ); + } + return referenceClass; + } + catch ( final ClassNotFoundException cnfe ) + { + // if we can't load the class, perhaps the method is declared in a + // super class so we try this class next + } + + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: Not found through component class, using PackageAdmin service", null ); + } + + // try to load the class with the help of the PackageAdmin service + PackageAdmin pa = ( PackageAdmin ) Activator.getPackageAdmin(); + if ( pa != null ) + { + final String referenceClassPackage = className.substring( 0, className + .lastIndexOf( '.' ) ); + ExportedPackage[] pkg = pa.getExportedPackages( referenceClassPackage ); + if ( pkg != null ) + { + for ( int i = 0; i < pkg.length; i++ ) + { + try + { + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( + LogService.LOG_DEBUG, + "getParameterClass: Checking Bundle {0}/{1}", + new Object[] {pkg[i].getExportingBundle().getSymbolicName(), pkg[i].getExportingBundle().getBundleId()}, null ); + } + + Class<?> referenceClass = pkg[i].getExportingBundle().loadClass( className ); + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: Found class {0}", new Object[] {referenceClass.getName()}, null ); + } + return referenceClass; + } + catch ( ClassNotFoundException cnfe ) + { + // exported package does not provide the interface !!!! + } + } + } + else if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: No bundles exporting package {0} found", new Object[] {referenceClassPackage}, null ); + } + } + else if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: PackageAdmin service not available, cannot find class", null ); + } + + // class cannot be found, neither through the component nor from an + // export, so we fall back to assuming Object + if ( logger.isLogEnabled( LogService.LOG_DEBUG ) ) + { + logger.log( LogService.LOG_DEBUG, + "getParameterClass: No class found, falling back to class Object", null ); + } + return OBJECT_CLASS; + } +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Coercions.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Coercions.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Coercions.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/Coercions.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,442 @@ +/* + * 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.helper; + +import java.lang.reflect.Array; +import java.util.Collection; + +import org.osgi.framework.Bundle; +import org.osgi.service.component.ComponentException; + +/** + * This implements the coercion table in RFC 190 5.6.3 + * + */ +public class Coercions +{ +// Numbers are AtomicInteger, AtomicLong, BigDecimal, BigInteger, Byte, Double, Float, Integer, Long, Short +//annotation fields can be primitives, String, Class, enums, annotations, and arrays of the preceding types +// input scalars +// String | Integer | Long | Float +// | Double | Byte | Short +//| Character | Boolean + private static final byte byte0 = 0; + private static final double double0 = 0; + private static final float float0 = 0; + private static final int int0 = 0; + private static final long long0 = 0; + private static final short short0 = 0; + + public static Object coerce(Class<?> type, Object raw, Bundle bundle ) + { + if (type == Byte.class || type == byte.class) + { + return coerceToByte(raw); + } + if (type == Boolean.class || type == boolean.class) + { + return coerceToBoolean(raw); + } + if (type == Class.class) + { + return coerceToClass(raw, bundle); + } + if (type == Double.class || type == double.class) + { + return coerceToDouble(raw); + } + if (type.isEnum()) + { + Class clazz = type; //TODO is there a better way to do ? enum creation? + return coerceToEnum(raw, clazz); + } + if (type == Float.class || type == float.class) + { + return coerceToFloat(raw); + } + if (type == Integer.class || type == int.class) + { + return coerceToInteger(raw); + } + if (type == Long.class || type == long.class) + { + return coerceToLong(raw); + } + if (type == Short.class || type == short.class) + { + return coerceToShort(raw); + } + if (type == String.class) + { + return coerceToString(raw); + } + throw new ComponentException ("unexpected output type " + type); + } + + public static byte coerceToByte(Object o) + { + o = multipleToSingle( o, byte0 ); + if (o instanceof Byte) + { + return (Byte)o; + } + if (o instanceof String) + { + try + { + return Byte.parseByte( (String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: byte0; + } + if (o instanceof Character) + { + return ( byte ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).byteValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static double coerceToDouble(Object o) + { + o = multipleToSingle( o, double0 ); + if (o instanceof Double) + { + return (Double)o; + } + if (o instanceof String) + { + try + { + return Double.parseDouble((String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: 0; + } + if (o instanceof Character) + { + return ( double ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).doubleValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static float coerceToFloat(Object o) + { + o = multipleToSingle( o, float0 ); + if (o instanceof Float) + { + return (Float)o; + } + if (o instanceof String) + { + try + { + return Float.parseFloat((String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: 0; + } + if (o instanceof Character) + { + return ( float ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).floatValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static int coerceToInteger(Object o) + { + o = multipleToSingle( o, int0 ); + if (o instanceof Integer) + { + return (Integer)o; + } + if (o instanceof String) + { + try + { + return Integer.parseInt( (String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: 0; + } + if (o instanceof Character) + { + return ( int ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).intValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static long coerceToLong(Object o) + { + o = multipleToSingle( o, long0 ); + if (o instanceof Long) + { + return (Long)o; + } + if (o instanceof String) + { + try + { + return Long.parseLong( (String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: 0; + } + if (o instanceof Character) + { + return ( long ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).longValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static short coerceToShort(Object o) + { + o = multipleToSingle( o, short0 ); + if (o instanceof Short) + { + return (Short)o; + } + if (o instanceof String) + { + try + { + return Short.parseShort( (String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Boolean) + { + return (Boolean)o? 1: short0; + } + if (o instanceof Character) + { + return ( short ) ((Character)o).charValue(); + } + if (o instanceof Number) + { + return ((Number)o).shortValue(); + } + if (o == null) + { + return 0; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static String coerceToString(Object o) + { + o = multipleToSingle( o, null ); + if (o instanceof String) + { + return (String)o; + } + if (o == null) + { + return null; + } + + return o.toString(); + } + + public static boolean coerceToBoolean(Object o) + { + o = multipleToSingle( o, false ); + if (o instanceof Boolean) + { + return (Boolean)o; + } + if (o instanceof String) + { + try + { + return Boolean.parseBoolean( (String)o ); + } + catch ( NumberFormatException e ) + { + throw new ComponentException(e); + } + } + if (o instanceof Character) + { + return ((Character)o).charValue() != 0; + } + if (o instanceof Number) + { + return ((Number)o).intValue() != 0; + } + if (o == null) + { + return false; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static Class<?> coerceToClass(Object o, Bundle b) + { + o = multipleToSingle( o, null ); + if (o == null) + { + return null; + } + if (o instanceof String) + { + try + { + return b.loadClass( (String)o ); + } + catch ( ClassNotFoundException e ) + { + throw new ComponentException(e); + } + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + public static <T extends Enum<T>> T coerceToEnum(Object o, Class<T> clazz) + { + o = multipleToSingle( o, null ); + if (o instanceof String) + { + try + { + return Enum.valueOf( clazz, (String)o ); + } + catch ( IllegalArgumentException e ) + { + throw new ComponentException(e); + } + } + if (o == null) + { + return null; + } + throw new ComponentException( "Unrecognized input type: " + o); + } + + private static Object multipleToSingle(Object o, Object defaultValue) + { + if (o instanceof Collection) + { + return firstCollectionElement( o, defaultValue ); + } + if (o != null && o.getClass().isArray()) { + return firstArrayElement( o, defaultValue); + } + return o; + } + + private static Object firstCollectionElement( Object raw, Object defaultValue ) + { + if (!(raw instanceof Collection)) + { + throw new ComponentException("Not a collection: " + raw); + } + Collection<?> c = ( Collection<?> ) raw; + if (c.isEmpty()) + { + return defaultValue; + } + return c.iterator().next(); + } + + private static Object firstArrayElement(Object o, Object defaultValue) + { + if (o == null || !o.getClass().isArray()) { + throw new ComponentException("Not an array: " + o); + } + if (Array.getLength( o ) == 0) + { + return defaultValue; + } + return Array.get( o, 0 ); + } + +} Added: felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ComponentMethods.java URL: http://svn.apache.org/viewvc/felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ComponentMethods.java?rev=1689973&view=auto ============================================================================== --- felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ComponentMethods.java (added) +++ felix/sandbox/pderop/dependencymanager.ds/org.apache.felix.dependencymanager.ds/src/org/apache/felix/scr/impl/helper/ComponentMethods.java Wed Jul 8 22:10:14 2015 @@ -0,0 +1,99 @@ +/* + * 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.helper; + +import java.util.HashMap; +import java.util.Map; + +import org.apache.felix.scr.impl.metadata.ComponentMetadata; +import org.apache.felix.scr.impl.metadata.DSVersion; +import org.apache.felix.scr.impl.metadata.ReferenceMetadata; + +/** + * @version $Rev: 1653352 $ $Date: 2015-01-20 20:58:11 +0100 (Tue, 20 Jan 2015) $ + */ +public class ComponentMethods +{ + private ActivateMethod m_activateMethod; + private ModifiedMethod m_modifiedMethod; + private DeactivateMethod m_deactivateMethod; + + private final Map<String, ReferenceMethods> bindMethodMap = new HashMap<String, ReferenceMethods>(); + + public synchronized void initComponentMethods( ComponentMetadata componentMetadata, Class<?> implementationObjectClass ) + { + if (m_activateMethod != null) + { + return; + } + DSVersion dsVersion = componentMetadata.getDSVersion(); + boolean configurableServiceProperties = componentMetadata.isConfigurableServiceProperties(); + boolean supportsInterfaces = componentMetadata.isConfigureWithInterfaces(); + m_activateMethod = new ActivateMethod( componentMetadata.getActivate(), componentMetadata + .isActivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces ); + m_deactivateMethod = new DeactivateMethod( componentMetadata.getDeactivate(), + componentMetadata.isDeactivateDeclared(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces ); + + m_modifiedMethod = new ModifiedMethod( componentMetadata.getModified(), implementationObjectClass, dsVersion, configurableServiceProperties, supportsInterfaces ); + + for ( ReferenceMetadata referenceMetadata: componentMetadata.getDependencies() ) + { + final String refName = referenceMetadata.getName(); + final ReferenceMethods methods; + if ( referenceMetadata.getField() != null && referenceMetadata.getBind() != null) + { + methods = new DuplexReferenceMethods( + new FieldMethods( referenceMetadata, implementationObjectClass, dsVersion, configurableServiceProperties), + new BindMethods( referenceMetadata, implementationObjectClass, dsVersion, configurableServiceProperties)); + } + else if ( referenceMetadata.getField() != null ) + { + methods = new FieldMethods( referenceMetadata, implementationObjectClass, dsVersion, configurableServiceProperties); + } + else + { + methods = new BindMethods( referenceMetadata, implementationObjectClass, dsVersion, configurableServiceProperties); + } + bindMethodMap.put( refName, methods ); + } + } + + public ActivateMethod getActivateMethod() + { + return m_activateMethod; + } + + public DeactivateMethod getDeactivateMethod() + { + return m_deactivateMethod; + } + + public ModifiedMethod getModifiedMethod() + { + return m_modifiedMethod; + } + + public ReferenceMethods getBindMethods(String refName ) + { + return bindMethodMap.get( refName ); + } + +}
