com.datastax.oss.driver.internal.core.util.Reflection Maven / Gradle / Ivy
/*
* 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 com.datastax.oss.driver.internal.core.util;
import com.datastax.oss.driver.api.core.config.DriverExecutionProfile;
import com.datastax.oss.driver.api.core.config.DriverOption;
import com.datastax.oss.driver.api.core.context.DriverContext;
import com.datastax.oss.driver.internal.core.context.InternalDriverContext;
import com.datastax.oss.driver.shaded.guava.common.base.Joiner;
import com.datastax.oss.driver.shaded.guava.common.base.Preconditions;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableList;
import com.datastax.oss.driver.shaded.guava.common.collect.ImmutableMap;
import com.datastax.oss.driver.shaded.guava.common.collect.ListMultimap;
import com.datastax.oss.driver.shaded.guava.common.collect.MultimapBuilder;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Reflection {
private static final Logger LOG = LoggerFactory.getLogger(Reflection.class);
/**
* Loads a class by name using the given {@link ClassLoader}.
*
* If the class loader is null, the class will be loaded using the class loader that loaded the
* driver.
*
* @return null if the class does not exist or could not be loaded.
*/
@Nullable
public static Class loadClass(@Nullable ClassLoader classLoader, @NonNull String className) {
try {
Class clazz;
if (classLoader == null) {
LOG.trace("Attempting to load {} with driver's class loader", className);
clazz = Class.forName(className);
} else {
LOG.trace("Attempting to load {} with {}", className, classLoader);
clazz = Class.forName(className, true, classLoader);
}
LOG.trace("Successfully loaded {}", className);
return clazz;
} catch (LinkageError | Exception e) {
// Note: only ClassNotFoundException, LinkageError and SecurityException
// are declared to be thrown; however some class loaders (Apache Felix)
// may throw other checked exceptions, which cannot be caught directly
// because that would cause a compilation failure.
LOG.debug(
String.format("Could not load %s with loader %s: %s", className, classLoader, e), e);
if (classLoader == null) {
return null;
} else {
// If the user-supplied class loader is unable to locate the class, try with the driver's
// default class loader. This is useful in OSGi deployments where the user-supplied loader
// may be able to load some classes but not all of them. Besides, the driver bundle, in
// OSGi, has a "Dynamic-Import:*" directive that makes its class loader capable of locating
// a great number of classes.
return loadClass(null, className);
}
}
}
/**
* Tries to create an instance of a class, given an option defined in the driver configuration.
*
*
For example:
*
*
* my-policy.class = my.package.MyPolicyImpl
*
*
* The class will be instantiated via reflection, it must have a constructor that takes a {@link
* DriverContext} argument.
*
* @param context the driver context.
* @param classNameOption the option that indicates the class. It will be looked up in the default
* profile of the configuration stored in the context.
* @param expectedSuperType a super-type that the class is expected to implement/extend.
* @param defaultPackages the default packages to prepend to the class name if it's not qualified.
* They will be tried in order, the first one that matches an existing class will be used.
* @return the new instance, or empty if {@code classNameOption} is not defined in the
* configuration.
*/
public static Optional buildFromConfig(
InternalDriverContext context,
DriverOption classNameOption,
Class expectedSuperType,
String... defaultPackages) {
return buildFromConfig(context, null, classNameOption, expectedSuperType, defaultPackages);
}
/**
* Tries to create a list of instances, given an option defined in the driver configuration.
*
* For example:
*
*
* my-policy.classes = [my.package.MyPolicyImpl1,my.package.MyPolicyImpl2]
*
*
* Each class will be instantiated via reflection, and must have a constructor that takes a {@link
* DriverContext} argument.
*
* @param context the driver context.
* @param classNamesOption the option that indicates the class list. It will be looked up in the
* default profile of the configuration stored in the context.
* @param expectedSuperType a super-type that the classes are expected to implement/extend.
* @param defaultPackages the default packages to prepend to the class names if they are not
* qualified. They will be tried in order, the first one that matches an existing class will
* be used.
* @return the list of new instances, or an empty list if {@code classNamesOption} is not defined
* in the configuration.
*/
public static ImmutableList buildFromConfigList(
InternalDriverContext context,
DriverOption classNamesOption,
Class expectedSuperType,
String... defaultPackages) {
return buildFromConfigList(context, null, classNamesOption, expectedSuperType, defaultPackages);
}
/**
* Tries to create multiple instances of a class, given options defined in the driver
* configuration and possibly overridden in profiles.
*
* For example:
*
*
* my-policy.class = package1.PolicyImpl1
* profiles {
* my-profile { my-policy.class = package2.PolicyImpl2 }
* }
*
*
* The class will be instantiated via reflection, it must have a constructor that takes two
* arguments: the {@link DriverContext}, and a string representing the profile name.
*
* This method assumes the policy is mandatory, the class option must be present at least for
* the default profile.
*
* @param context the driver context.
* @param classNameOption the option that indicates the class (my-policy.class in the example
* above).
* @param rootOption the root of the section containing the policy's configuration (my-policy in
* the example above). Profiles that have the same contents under that section will share the
* same policy instance.
* @param expectedSuperType a super-type that the class is expected to implement/extend.
* @param defaultPackages the default packages to prepend to the class name if it's not qualified.
* They will be tried in order, the first one that matches an existing class will be used.
* @return the policy instances by profile name. If multiple profiles share the same
* configuration, a single instance will be shared by all their entries.
*/
public static Map buildFromConfigProfiles(
InternalDriverContext context,
DriverOption classNameOption,
DriverOption rootOption,
Class expectedSuperType,
String... defaultPackages) {
// Find out how many distinct configurations we have
ListMultimap