com.azure.core.implementation.ReflectionUtils Maven / Gradle / Ivy
Show all versions of azure-core Show documentation
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
package com.azure.core.implementation;
import com.azure.core.util.logging.ClientLogger;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* Utility methods that aid in performing reflective operations.
*/
public abstract class ReflectionUtils {
private static final ClientLogger LOGGER = new ClientLogger(ReflectionUtils.class);
private static final ReflectionUtilsApi INSTANCE;
static {
ReflectionUtilsApi instance;
try {
LOGGER.verbose("Attempting to use java.lang.invoke package to handle reflection.");
instance = new ReflectionUtilsMethodHandle();
LOGGER.verbose("Successfully used java.lang.invoke package to handle reflection.");
} catch (LinkageError ignored) {
LOGGER.verbose("Failed to use java.lang.invoke package to handle reflection. Falling back to "
+ "java.lang.reflect package to handle reflection.");
instance = new ReflectionUtilsClassic();
LOGGER.verbose("Successfully used java.lang.reflect package to handle reflection.");
}
INSTANCE = instance;
}
/**
* Creates an {@link ReflectiveInvoker} instance that will invoke a {@link Method}.
*
* Calls {@link #getMethodInvoker(Class, Method, boolean)} with {@code scopeToAzureCore} set to true.
*
* @param targetClass The class that contains the method.
* @param method The method to invoke.
* @return An {@link ReflectiveInvoker} instance that will invoke the method.
* @throws NullPointerException If {@code method} is null.
* @throws Exception If the {@link ReflectiveInvoker} cannot be created.
*/
public static ReflectiveInvoker getMethodInvoker(Class> targetClass, Method method) throws Exception {
return getMethodInvoker(targetClass, method, true);
}
/**
* Creates an {@link ReflectiveInvoker} instance that will invoke a {@link Method}.
*
* {@code targetClass} may be null but when using an environment that supports MethodHandles for handling reflection
* this may result in exceptions being thrown due to the inability to scope the MethodHandle to a module. To attempt
* to alleviate this issue, if {@code targetClass} is null {@link Method#getDeclaringClass()} will be used to infer
* the class.
*
* {@code scopeToAzure} is only when used when MethodHandles are being used and Java 9+ modules are being used. This
* will determine whether to use a MethodHandles.Lookup scoped to {@code azure-core} or to use a public
* MethodHandles.Lookup. Scoping a MethodHandles.Lookup to {@code azure-core} requires to module containing the
* class to open or export to {@code azure-core} which generally only holds true for other Azure SDKs, for example
* there are cases where a reflective invocation is needed to Jackson which won't open or export to
* {@code azure-core} and the only APIs invoked reflectively are public APIs so the public MethodHandles.Lookup will
* be used.
*
* @param targetClass The class that contains the method.
* @param method The method to invoke.
* @param scopeToAzureCore If Java 9+ modules is being used this will scope MethodHandle-based reflection to using
* {@code azure-core} as the scoped module, otherwise this is ignored.
* @return An {@link ReflectiveInvoker} instance that will invoke the method.
* @throws NullPointerException If {@code method} is null.
* @throws Exception If the {@link ReflectiveInvoker} cannot be created.
*/
public static ReflectiveInvoker getMethodInvoker(Class> targetClass, Method method, boolean scopeToAzureCore)
throws Exception {
if (method == null) {
throw LOGGER.logExceptionAsError(new NullPointerException("'method' cannot be null."));
}
targetClass = (targetClass == null) ? method.getDeclaringClass() : targetClass;
return INSTANCE.getMethodInvoker(targetClass, method, scopeToAzureCore);
}
/**
* Creates an {@link ReflectiveInvoker} instance that will invoke a {@link Constructor}.
*
* Calls {@link #getConstructorInvoker(Class, Constructor, boolean)} with {@code scopeToAzureCore} set to true.
*
* @param targetClass The class that contains the constructor.
* @param constructor The constructor to invoke.
* @return An {@link ReflectiveInvoker} instance that will invoke the constructor.
* @throws NullPointerException If {@code constructor} is null.
* @throws Exception If the {@link ReflectiveInvoker} cannot be created.
*/
public static ReflectiveInvoker getConstructorInvoker(Class> targetClass, Constructor> constructor)
throws Exception {
return getConstructorInvoker(targetClass, constructor, true);
}
/**
* Creates an {@link ReflectiveInvoker} instance that will invoke a {@link Constructor}.
*
* {@code targetClass} may be null but when using an environment that supports MethodHandles for handling reflection
* this may result in exceptions being thrown due to the inability to scope the MethodHandle to a module. To attempt
* to alleviate this issue, if {@code targetClass} is null {@link Constructor#getDeclaringClass()} will be used to
* infer the class.
*
* {@code scopeToAzure} is only when used when MethodHandles are being used and Java 9+ modules are being used. This
* will determine whether to use a MethodHandles.Lookup scoped to {@code azure-core} or to use a public
* MethodHandles.Lookup. Scoping a MethodHandles.Lookup to {@code azure-core} requires to module containing the
* class to open or export to {@code azure-core} which generally only holds true for other Azure SDKs, for example
* there are cases where a reflective invocation is needed to Jackson which won't open or export to
* {@code azure-core} and the only APIs invoked reflectively are public APIs so the public MethodHandles.Lookup will
* be used.
*
* @param targetClass The class that contains the constructor.
* @param constructor The constructor to invoke.
* @param scopeToAzureCore If Java 9+ modules is being used this will scope MethodHandle-based reflection to using
* {@code azure-core} as the scoped module, otherwise this is ignored.
* @return An {@link ReflectiveInvoker} instance that will invoke the constructor.
* @throws NullPointerException If {@code constructor} is null.
* @throws Exception If the {@link ReflectiveInvoker} cannot be created.
*/
public static ReflectiveInvoker getConstructorInvoker(Class> targetClass, Constructor> constructor,
boolean scopeToAzureCore) throws Exception {
if (constructor == null) {
throw LOGGER.logExceptionAsError(new NullPointerException("'constructor' cannot be null."));
}
targetClass = (targetClass == null) ? constructor.getDeclaringClass() : targetClass;
return INSTANCE.getConstructorInvoker(targetClass, constructor, scopeToAzureCore);
}
/**
* Determines whether a Java 9+ module-based implementation of {@link ReflectionUtilsApi} is being used.
*
* @return Whether a Java 9+ module-based implementation of {@link ReflectionUtilsApi} is being used.
*/
public static boolean isModuleBased() {
return INSTANCE.isModuleBased();
}
/**
* Creates a dummy {@link ReflectiveInvoker} that will always return null. Used for scenarios where an {@link ReflectiveInvoker} is
* needed as an identifier but will never be used.
*
* @return A dummy {@link ReflectiveInvoker} that will always return null.
*/
public static ReflectiveInvoker createNoOpInvoker() {
return new NoOpReflectiveInvoker();
}
private static final class NoOpReflectiveInvoker implements ReflectiveInvoker {
@Override
public Object invokeStatic(Object... args) {
return null;
}
@Override
public Object invokeWithArguments(Object target, Object... args) {
return null;
}
@Override
public int getParameterCount() {
return 0;
}
}
ReflectionUtils() {
}
}