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

org.perfectable.introspection.proxy.ProxyService Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
package org.perfectable.introspection.proxy;

import java.util.Arrays;
import java.util.List;
import java.util.ServiceLoader;
import java.util.stream.Stream;

import com.google.errorprone.annotations.Immutable;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
 * Method of producing proxies.
 *
 * 

There are multiple ways of creating proxy in Java, from simple {@link java.lang.reflect.Proxy}, through support * frameworks like javassist, to class generation libraries like cglib or bytebuddy. This class creates universal facade * to potentially all these methods. * *

Different frameworks supports different proxying features, for example, javassist can create proxy classes that * extend specific non-final class, not only that implement specific interfaces. These features are represented by * {@link Feature} and can be tested for support (using {@link #supportsAllFeatures} or {@link #supportsFeature}). * *

Interface implementation can be registered to be used with {@link ServiceLoader}, and should be obtained using * {@link #INSTANCES}. * *

This interface can be implemented externally, and registered with Service Loader to be usable in * {@link ProxyBuilder}. * * @see ProxyBuilder */ @Immutable public interface ProxyService { /** * Container managing all instances registered with {@link ServiceLoader}. */ Container INSTANCES = new Container(); /** * If the concrete service supports specified feature. * * @param feature feature to test * @return if service supports feature */ boolean supportsFeature(Feature feature); /** * If the concrete service supports all of specified features. * * @param features feature set to test * @return if service supports features */ default boolean supportsAllFeatures(Feature... features) { return Stream.of(features).allMatch(this::supportsFeature); } /** * Creates proxy instance. * *

This method either creates new proxy class, or reuses one that matches. This proxy class extends * {@code baseClass} and implements {@code interfaces}, and is created in {@code classLoader}. After creating this * class, its object is instantiated. The object will delegate every call that it receives to specified * {@link InvocationHandler}. The only exception is {@link Object#finalize}, which must be ignored. * *

This method assumes that the arguments were correctly prepared: *

    *
  • {@code baseClass} is not final
  • *
  • {@code baseClass} is actually a class (not an interface)
  • *
  • {@code baseClass} and each element of {@code interfaces} has classloader exactly {@code classLoader}
  • *
  • Each element in {@code interfaces} is an interface (not a class)
  • *
* * @param type of proxy * @param classLoader classloader used to create proxy class * @param baseClass proxy superclass, cannot be final, must be a class * @param interfaces additional interfaces that proxy class should implement * @param handler invocation handler to pass method execution for this proxy instance * @return proxy instance * @throws UnsupportedFeatureException when this service cannot create proxy with requested parameters, because * it doesn't support some feature required to do so */ I instantiate(@Nullable ClassLoader classLoader, Class baseClass, List> interfaces, InvocationHandler> handler) throws UnsupportedFeatureException; /** Thrown when factory cannot create a builder with requested features. */ @SuppressWarnings("serial") final class UnsupportedFeatureException extends RuntimeException { @SuppressWarnings("javadoc") public UnsupportedFeatureException(String message) { super(message); } } /** Features that service can support. */ enum Feature { /** Service can create proxies that have superclass other than Object. */ SUPERCLASS } /** * Container that manages service-loaded instances of {@link ProxyService}. */ final class Container { private final ServiceLoader cache = ServiceLoader.load(ProxyService.class); /** * Extracts proxy service with specified features. * * @param requiredFeatures features that returned service must support * @return proxy service * @throws UnsupportedFeatureException when no proxy service could be found with specified features. */ public ProxyService get(Feature... requiredFeatures) { for (ProxyService service : cache) { if (service.supportsAllFeatures(requiredFeatures)) { return service; } } throw new UnsupportedFeatureException("No proxy service supports " + Arrays.toString(requiredFeatures)); } @SuppressWarnings("PMD.UnnecessaryConstructor") Container() { // package-private access } } }