All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.datastax.oss.driver.internal.core.util.Reflection Maven / Gradle / Ivy

The newest version!
/*
 * Copyright DataStax, Inc.
 *
 * Licensed 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 profilesByConfig = MultimapBuilder.hashKeys().arrayListValues().build(); for (DriverExecutionProfile profile : context.getConfig().getProfiles().values()) { profilesByConfig.put(profile.getComparisonKey(rootOption), profile.getName()); } // Instantiate each distinct configuration, and associate it with the corresponding profiles ImmutableMap.Builder result = ImmutableMap.builder(); for (Collection profiles : profilesByConfig.asMap().values()) { // Since all profiles use the same config, we can use any of them String profileName = profiles.iterator().next(); ComponentT policy = buildFromConfig(context, profileName, classNameOption, expectedSuperType, defaultPackages) .orElseThrow( () -> new IllegalArgumentException( String.format( "Missing configuration for %s in profile %s", rootOption.getPath(), profileName))); for (String profile : profiles) { result.put(profile, policy); } } return result.build(); } /** * @param profileName if null, this is a global policy, use the default profile and look for a * one-arg constructor. If not null, this is a per-profile policy, look for a two-arg * constructor. */ public static Optional buildFromConfig( InternalDriverContext context, String profileName, DriverOption classNameOption, Class expectedSuperType, String... defaultPackages) { DriverExecutionProfile config = (profileName == null) ? context.getConfig().getDefaultProfile() : context.getConfig().getProfile(profileName); String configPath = classNameOption.getPath(); LOG.debug("Creating a {} from config option {}", expectedSuperType.getSimpleName(), configPath); if (!config.isDefined(classNameOption)) { LOG.debug("Option is not defined, skipping"); return Optional.empty(); } String className = config.getString(classNameOption); return Optional.of( resolveClass( context, profileName, expectedSuperType, configPath, className, defaultPackages)); } /** * @param profileName if null, this is a global policy, use the default profile and look for a * one-arg constructor. If not null, this is a per-profile policy, look for a two-arg * constructor. */ public static ImmutableList buildFromConfigList( InternalDriverContext context, String profileName, DriverOption classNamesOption, Class expectedSuperType, String... defaultPackages) { DriverExecutionProfile config = (profileName == null) ? context.getConfig().getDefaultProfile() : context.getConfig().getProfile(profileName); String configPath = classNamesOption.getPath(); LOG.debug( "Creating a list of {} from config option {}", expectedSuperType.getSimpleName(), configPath); if (!config.isDefined(classNamesOption)) { LOG.debug("Option is not defined, skipping"); return ImmutableList.of(); } List classNames = config.getStringList(classNamesOption); ImmutableList.Builder components = ImmutableList.builder(); for (String className : classNames) { components.add( resolveClass( context, profileName, expectedSuperType, configPath, className, defaultPackages)); } return components.build(); } @NonNull private static ComponentT resolveClass( InternalDriverContext context, String profileName, Class expectedSuperType, String configPath, String className, String[] defaultPackages) { Class clazz = null; if (className.contains(".")) { LOG.debug("Building from fully-qualified name {}", className); clazz = loadClass(context.getClassLoader(), className); } else { LOG.debug("Building from unqualified name {}", className); for (String defaultPackage : defaultPackages) { String qualifiedClassName = defaultPackage + "." + className; LOG.debug("Trying with default package {}", qualifiedClassName); clazz = loadClass(context.getClassLoader(), qualifiedClassName); if (clazz != null) { break; } } } if (clazz == null) { throw new IllegalArgumentException( String.format("Can't find class %s (specified by %s)", className, configPath)); } Preconditions.checkArgument( expectedSuperType.isAssignableFrom(clazz), "Expected class %s (specified by %s) to be a subtype of %s", className, configPath, expectedSuperType.getName()); Constructor constructor; Class[] argumentTypes = (profileName == null) ? new Class[] {DriverContext.class} : new Class[] {DriverContext.class, String.class}; try { constructor = clazz.asSubclass(expectedSuperType).getConstructor(argumentTypes); } catch (NoSuchMethodException e) { throw new IllegalArgumentException( String.format( "Expected class %s (specified by %s) " + "to have an accessible constructor with arguments (%s)", className, configPath, Joiner.on(',').join(argumentTypes))); } try { @SuppressWarnings("JavaReflectionInvocation") ComponentT instance = (profileName == null) ? constructor.newInstance(context) : constructor.newInstance(context, profileName); return instance; } catch (Exception e) { // ITE just wraps an exception thrown by the constructor, get rid of it: Throwable cause = (e instanceof InvocationTargetException) ? e.getCause() : e; throw new IllegalArgumentException( String.format( "Error instantiating class %s (specified by %s): %s", className, configPath, cause.getMessage()), cause); } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy