Browse Source

Add result and thrown type to Invocation and InvocationHandler

master
Paweł Płazieński 4 months ago
parent
commit
8f928e0635
14 changed files with 73 additions and 78 deletions
  1. +1
    -1
      README.md
  2. +1
    -1
      pom.xml
  3. +3
    -3
      src/main/java/org/perfectable/introspection/AnnotationBuilder.java
  4. +1
    -1
      src/main/java/org/perfectable/introspection/proxy/ForwardingHandler.java
  5. +16
    -13
      src/main/java/org/perfectable/introspection/proxy/Invocation.java
  6. +6
    -6
      src/main/java/org/perfectable/introspection/proxy/InvocationHandler.java
  7. +14
    -18
      src/main/java/org/perfectable/introspection/proxy/Invocations.java
  8. +6
    -5
      src/main/java/org/perfectable/introspection/proxy/JavassistProxyService.java
  9. +4
    -4
      src/main/java/org/perfectable/introspection/proxy/JdkProxyService.java
  10. +6
    -3
      src/main/java/org/perfectable/introspection/proxy/LazyInitialization.java
  11. +1
    -1
      src/main/java/org/perfectable/introspection/proxy/MethodInvocation.java
  12. +1
    -1
      src/main/java/org/perfectable/introspection/proxy/ProxyBuilder.java
  13. +1
    -1
      src/main/java/org/perfectable/introspection/proxy/ProxyService.java
  14. +12
    -20
      src/test/java/org/perfectable/introspection/proxy/TestHandler.java

+ 1
- 1
README.md View File

@@ -265,6 +265,6 @@ Add as dependency:
<dependency>
<groupId>org.perfectable</groupId>
<artifactId>introspectable</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>
</dependency>
```

+ 1
- 1
pom.xml View File

@@ -8,7 +8,7 @@
</parent>

<artifactId>introspectable</artifactId>
<version>4.1.0-SNAPSHOT</version>
<version>5.0.0-SNAPSHOT</version>

<name>Introspectable</name>
<description>Easier java reflections</description>


+ 3
- 3
src/main/java/org/perfectable/introspection/AnnotationBuilder.java View File

@@ -207,7 +207,8 @@ public final class AnnotationBuilder<A extends Annotation> {
.unique();

@Immutable
private static final class AnnotationInvocationHandler<A> implements InvocationHandler<MethodInvocation<A>> {
private static final class AnnotationInvocationHandler<A>
implements InvocationHandler<@Nullable Object, RuntimeException, MethodInvocation<A>> {
private static final int MEMBER_NAME_HASH_MULTIPLIER = 127;

private final Class<A> annotationType;
@@ -229,8 +230,7 @@ public final class AnnotationBuilder<A extends Annotation> {
@Override
public @Nullable Object handle(MethodInvocation<A> invocation) {
MethodInvocation.Decomposer<A, @Nullable Object> decomposer = this::calculateMethodResult;
@Nullable Object result = invocation.decompose(decomposer);
return result;
return invocation.decompose(decomposer);
}

private @Nullable Object calculateMethodResult(Method method, @SuppressWarnings("unused") A receiver,


+ 1
- 1
src/main/java/org/perfectable/introspection/proxy/ForwardingHandler.java View File

@@ -9,7 +9,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*
* @param <T> type of objects handled
*/
public final class ForwardingHandler<T> implements InvocationHandler<MethodInvocation<T>> {
public final class ForwardingHandler<T> implements InvocationHandler<@Nullable Object, Throwable, MethodInvocation<T>> {

private T target;



+ 16
- 13
src/main/java/org/perfectable/introspection/proxy/Invocation.java View File

@@ -3,8 +3,6 @@ package org.perfectable.introspection.proxy;
import java.util.concurrent.Callable;
import java.util.function.Supplier;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Invocation of execution point.
*
@@ -14,21 +12,23 @@ import org.checkerframework.checker.nullness.qual.Nullable;
* <p>Depending on the proxy mechanism, this potentially could be any point in program, but most implementations only
* support capturing method calls into {@link MethodInvocation}.
*
* @param <R> Type of value returned by invocation
* @param <X> Type of exception thrown by invocation
* @see InvocationHandler
* @see MethodInvocation
*/
@FunctionalInterface
public interface Invocation {
public interface Invocation<R, X extends Throwable> {
/**
* Invokes the execution point.
*
* <p>It will either succeed and return a result, possibly null, or fail and throw an exception.
*
* @return result of an invocation.
* @throws Throwable exception that was thrown by invocation
* @throws X exception that was thrown by invocation
*/
@SuppressWarnings({"IllegalThrows", "AnnotationLocation"})
@Nullable Object invoke() throws Throwable;
@SuppressWarnings({"AnnotationLocation"})
R invoke() throws X;

/**
* Adapts {@link Runnable} to this interface.
@@ -37,7 +37,7 @@ public interface Invocation {
* @return invocation that when invoked, will call the runnable and if it doesn't throw runtime exception, it will
* return null.
*/
static Invocation fromRunnable(Runnable runnable) {
static Invocation<?, RuntimeException> fromRunnable(Runnable runnable) {
return new Invocations.RunnableAdapter(runnable);
}

@@ -45,21 +45,23 @@ public interface Invocation {
* Adapts {@link Callable} to this interface.
*
* @param callable runnable to run
* @param <R> type returned from callable
* @return invocation that when invoked, will call the runnable and if it doesn't throw exception, it will
* return whatever callable execution returns.
*/
static Invocation fromCallable(Callable<?> callable) {
return new Invocations.CallableAdapter(callable);
static <R> Invocation<R, Exception> fromCallable(Callable<R> callable) {
return new Invocations.CallableAdapter<>(callable);
}

/**
* Creates invocation that does only one thing: returns the provided argument.
*
* @param result what the invocation should return
* @param <T> type of result returned from invocation
* @return invocation that returns provided result.
*/
static Invocation returning(@Nullable Object result) {
return new Invocations.Returning(result);
static <T> Invocation<T, RuntimeException> returning(T result) {
return new Invocations.Returning<>(result);
}

/**
@@ -67,11 +69,12 @@ public interface Invocation {
*
* <p>Each execution of the invocation will fetch new exception.
*
* @param <X> type of exception thrown
* @param thrownSupplier supplier that will produce exceptions to throw
* @return invocation that throws exception
*/
static Invocation throwing(Supplier<Throwable> thrownSupplier) {
return new Invocations.Throwing(thrownSupplier);
static <X extends Throwable> Invocation<?, X> throwing(Supplier<X> thrownSupplier) {
return new Invocations.Throwing<>(thrownSupplier);
}

}

+ 6
- 6
src/main/java/org/perfectable/introspection/proxy/InvocationHandler.java View File

@@ -1,7 +1,5 @@
package org.perfectable.introspection.proxy;

import org.checkerframework.checker.nullness.qual.Nullable;

/**
* Handles an invocation of a execution point on a proxy and returns response.
*
@@ -10,19 +8,21 @@ import org.checkerframework.checker.nullness.qual.Nullable;
*
* @param <I> type of invocation supported. This is usually {@link MethodInvocation}

* @param <R> type of values returned from handler
* @param <X> type of exceptions thrown from handler
* @see ProxyBuilder#instantiate
* @see MethodInvocation
*/
@FunctionalInterface
public interface InvocationHandler<I extends Invocation> {
public interface InvocationHandler<R, X extends Throwable, I extends Invocation<?, ?>> {

/**
* Catches invocation executed on proxy and returns result that should be passed to the client.
*
* @param invocation invocation captured
* @return successful result of invocation processing
* @throws Throwable exception that will be thrown on the call site
* @throws X exception that will be thrown on the call site
*/
@SuppressWarnings({"IllegalThrows", "AnnotationLocation"})
@Nullable Object handle(I invocation) throws Throwable;
@SuppressWarnings({"AnnotationLocation"})
R handle(I invocation) throws X;
}

+ 14
- 18
src/main/java/org/perfectable/introspection/proxy/Invocations.java View File

@@ -3,51 +3,47 @@ package org.perfectable.introspection.proxy;
import java.util.concurrent.Callable;
import java.util.function.Supplier;

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

final class Invocations {
static final class Returning implements Invocation {
private final @Nullable Object result;
static final class Returning<T> implements Invocation<T, RuntimeException> {
private final T result;

Returning(@Nullable Object result) {
Returning(T result) {
this.result = result;
}

@Override
public @Nullable Object invoke() {
public T invoke() {
return result;
}
}

static final class Throwing implements Invocation {
private final Supplier<Throwable> thrownSupplier;
static final class Throwing<X extends Throwable> implements Invocation<Void, X> {
private final Supplier<X> thrownSupplier;

Throwing(Supplier<Throwable> thrownSupplier) {
Throwing(Supplier<X> thrownSupplier) {
this.thrownSupplier = thrownSupplier;
}

@CanIgnoreReturnValue
@Override
public @Nullable Object invoke() throws Throwable {
public Void invoke() throws X {
throw thrownSupplier.get();
}
}

static final class CallableAdapter implements Invocation {
private final Callable<?> callable;
static final class CallableAdapter<R> implements Invocation<R, Exception> {
private final Callable<R> callable;

CallableAdapter(Callable<?> callable) {
CallableAdapter(Callable<R> callable) {
this.callable = callable;
}

@Override
public @Nullable Object invoke() throws Throwable {
public R invoke() throws Exception {
return callable.call();
}
}

static final class RunnableAdapter implements Invocation {
static final class RunnableAdapter implements Invocation<Void, RuntimeException> {
private final Runnable runnable;

RunnableAdapter(Runnable runnable) {
@@ -55,7 +51,7 @@ final class Invocations {
}

@Override
public @Nullable Object invoke() {
public Void invoke() {
runnable.run();
return null;
}


+ 6
- 5
src/main/java/org/perfectable/introspection/proxy/JavassistProxyService.java View File

@@ -38,7 +38,7 @@ public final class JavassistProxyService implements ProxyService {

@Override
public <I> I instantiate(@Nullable ClassLoader classLoader, Class<?> baseClass, List<? extends Class<?>> interfaces,
InvocationHandler<? super MethodInvocation<I>> handler)
InvocationHandler<?, ?, ? super MethodInvocation<I>> handler)
throws UnsupportedFeatureException {
checkArgument(!Modifier.isFinal(baseClass.getModifiers()));
Class<I> proxyClass = createProxyClass(baseClass, interfaces);
@@ -63,7 +63,7 @@ public final class JavassistProxyService implements ProxyService {
}

private static <I> I instantiateProxyClass(Class<I> proxyClass,
InvocationHandler<? super MethodInvocation<I>> handler) {
InvocationHandler<?, ?, ? super MethodInvocation<I>> handler) {
MethodHandler handlerAdapter = JavassistInvocationHandlerAdapter.adapt(handler);
@SuppressWarnings("cast.unsafe")
I proxy = (@NonNull I) OBJENESIS.newInstance(proxyClass);
@@ -72,13 +72,14 @@ public final class JavassistProxyService implements ProxyService {
}

private static final class JavassistInvocationHandlerAdapter<T> implements MethodHandler {
private final InvocationHandler<? super MethodInvocation<T>> handler;
private final InvocationHandler<?, ?, ? super MethodInvocation<T>> handler;

static <X> JavassistInvocationHandlerAdapter<X> adapt(InvocationHandler<? super MethodInvocation<X>> handler) {
static <X> JavassistInvocationHandlerAdapter<X> adapt(
InvocationHandler<?, ?, ? super MethodInvocation<X>> handler) {
return new JavassistInvocationHandlerAdapter<>(handler);
}

private JavassistInvocationHandlerAdapter(InvocationHandler<? super MethodInvocation<T>> handler) {
private JavassistInvocationHandlerAdapter(InvocationHandler<?, ?, ? super MethodInvocation<T>> handler) {
this.handler = handler;
}



+ 4
- 4
src/main/java/org/perfectable/introspection/proxy/JdkProxyService.java View File

@@ -29,7 +29,7 @@ public final class JdkProxyService implements ProxyService {

@Override
public <I> I instantiate(@Nullable ClassLoader classLoader, Class<?> baseClass, List<? extends Class<?>> interfaces,
InvocationHandler<? super MethodInvocation<I>> handler)
InvocationHandler<?, ?, ? super MethodInvocation<I>> handler)
throws UnsupportedFeatureException {
if (!baseClass.getName().equals(Object.class.getName())) {
throw new UnsupportedFeatureException("JDK proxy cannot be created with superclass other than Object");
@@ -47,13 +47,13 @@ public final class JdkProxyService implements ProxyService {
}

private static final class JdkInvocationHandlerAdapter<T> implements java.lang.reflect.InvocationHandler {
private final InvocationHandler<? super MethodInvocation<T>> handler;
private final InvocationHandler<?, ?, ? super MethodInvocation<T>> handler;

static <T> JdkInvocationHandlerAdapter<T> adapt(InvocationHandler<? super MethodInvocation<T>> handler) {
static <T> JdkInvocationHandlerAdapter<T> adapt(InvocationHandler<?, ?, ? super MethodInvocation<T>> handler) {
return new JdkInvocationHandlerAdapter<>(handler);
}

private JdkInvocationHandlerAdapter(InvocationHandler<? super MethodInvocation<T>> handler) {
private JdkInvocationHandlerAdapter(InvocationHandler<?, ?, ? super MethodInvocation<T>> handler) {
this.handler = handler;
}



+ 6
- 3
src/main/java/org/perfectable/introspection/proxy/LazyInitialization.java View File

@@ -75,7 +75,7 @@ public final class LazyInitialization {
}

private static final class LazyInitializationHandler<T extends @NonNull Object>
implements InvocationHandler<MethodInvocation<T>> {
implements InvocationHandler<@Nullable Object, Throwable, MethodInvocation<T>> {
private static final Method EXTRACT_INSTANCE_METHOD =
introspect(Proxy.class).methods().named("extractInstance").parameters().unique();

@@ -93,10 +93,13 @@ public final class LazyInitialization {

@Override
public @Nullable Object handle(MethodInvocation<T> invocation) throws Throwable {
return invocation.decompose(this::replaceInvocation).invoke();
MethodInvocation.Decomposer<T, Invocation<@Nullable Object, Throwable>> decomposer =
this::replaceInvocation;
Invocation<@Nullable Object, Throwable> replaced = invocation.decompose(decomposer);
return replaced.invoke();
}

private Invocation replaceInvocation(Method method,
private Invocation<@Nullable Object, Throwable> replaceInvocation(Method method,
@SuppressWarnings("unused") T receiver,
@Nullable Object... arguments) {
if (EXTRACT_INSTANCE_METHOD.equals(method)) {


+ 1
- 1
src/main/java/org/perfectable/introspection/proxy/MethodInvocation.java View File

@@ -40,7 +40,7 @@ import static org.perfectable.introspection.Introspections.introspect;
*
* @param <T> Type of method receiver (i.e. {@code this})
*/
public final class MethodInvocation<T> implements Invocation {
public final class MethodInvocation<T> implements Invocation<@Nullable Object, Throwable> {
private static final Object[] EMPTY_ARGUMENTS = new Object[0];

private final Method method;


+ 1
- 1
src/main/java/org/perfectable/introspection/proxy/ProxyBuilder.java View File

@@ -121,7 +121,7 @@ public final class ProxyBuilder<I> {
* @param handler method that proxy will delegate its calls to
* @return proxy instance
*/
public I instantiate(InvocationHandler<? super MethodInvocation<I>> handler) {
public I instantiate(InvocationHandler<?, ?, ? super MethodInvocation<I>> handler) {
return service.instantiate(classLoader, baseClass, interfaces, handler);
}
}

+ 1
- 1
src/main/java/org/perfectable/introspection/proxy/ProxyService.java View File

@@ -78,7 +78,7 @@ public interface ProxyService {
* it doesn't support some feature required to do so
*/
<I> I instantiate(@Nullable ClassLoader classLoader, Class<?> baseClass, List<? extends Class<?>> interfaces,
InvocationHandler<? super MethodInvocation<I>> handler)
InvocationHandler<?, ?, ? super MethodInvocation<I>> handler)
throws UnsupportedFeatureException;

/** Thrown when factory cannot create a builder with requested features. */


+ 12
- 20
src/test/java/org/perfectable/introspection/proxy/TestHandler.java View File

@@ -10,7 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;

import static org.assertj.core.api.Assertions.assertThat;

public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
public class TestHandler<T> implements InvocationHandler<@Nullable Object, Throwable, MethodInvocation<T>> {
private final Queue<Expectance> expected = new ArrayDeque<>();

static <T> TestHandler<T> create() {
@@ -19,8 +19,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {

@Override
public @Nullable Object handle(MethodInvocation<T> invocation) throws Throwable {
MethodInvocation.Decomposer<T, Invocation> decomposer = this::replaceInvocation;
return invocation.decompose(decomposer).invoke();
return invocation.decompose(this::replaceInvocation).invoke();
}

public Expectance expectInvocation(@Nullable T proxy, Method method, @Nullable Object... arguments) {
@@ -35,7 +34,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
.isEmpty();
}

private Invocation replaceInvocation(Method method, @Nullable T receiver, @Nullable Object... arguments) {
private Invocation<?, ?> replaceInvocation(Method method, @Nullable T receiver, @Nullable Object... arguments) {
if (ObjectMethods.EQUALS.equals(method)) {
return new EqualsInvocation(receiver, arguments[0]);
}
@@ -46,14 +45,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
return new ToStringInvocation(receiver);
}
Expectance expectance = expected.remove();
Invocation result = expectance.process(method, receiver, arguments);
return result;
}

@FunctionalInterface
interface InvocationResult {
@SuppressWarnings({"IllegalThrows", "AnnotationLocation"})
@Nullable Object resolve() throws Throwable;
return expectance.process(method, receiver, arguments);
}

class Expectance {
@@ -61,7 +53,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
private final Method expectedMethod;
private final @Nullable Object[] expectedArguments;

private Invocation result = () -> new AssertionError("Result has not been set");
private Invocation<?, ?> result = Invocation.throwing(() -> new AssertionError("Result has not been set"));

Expectance(@Nullable T expectedProxy, Method expectedMethod, @Nullable Object... expectedArguments) {
this.expectedProxy = expectedProxy;
@@ -78,7 +70,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
}

@SuppressWarnings("argument.type.incompatible")
public Invocation process(Method method, @Nullable T receiver, @Nullable Object... arguments) {
public Invocation<?, ?> process(Method method, @Nullable T receiver, @Nullable Object... arguments) {
assertThat(receiver).isEqualTo(expectedProxy);
assertThat(method).isEqualTo(expectedMethod);
assertThat(arguments).isEqualTo(expectedArguments);
@@ -86,7 +78,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
}
}

private static class EqualsInvocation implements Invocation {
private static class EqualsInvocation implements Invocation<Boolean, RuntimeException> {
private final @Nullable Object receiver;
private final @Nullable Object other;

@@ -96,12 +88,12 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
}

@Override
public Object invoke() {
public Boolean invoke() {
return receiver == other; // SUPPRESS CompareObjectsWithEquals
}
}

private static class HashCodeInvocation implements Invocation {
private static class HashCodeInvocation implements Invocation<Integer, RuntimeException> {
private final @Nullable Object receiver;

HashCodeInvocation(@Nullable Object receiver) {
@@ -109,12 +101,12 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
}

@Override
public @Nullable Object invoke() {
public Integer invoke() {
return System.identityHashCode(receiver);
}
}

private static class ToStringInvocation implements Invocation {
private static class ToStringInvocation implements Invocation<String, RuntimeException> {
private final @Nullable Object receiver;

ToStringInvocation(@Nullable Object receiver) {
@@ -122,7 +114,7 @@ public class TestHandler<T> implements InvocationHandler<MethodInvocation<T>> {
}

@Override
public @Nullable Object invoke() {
public String invoke() {
if (receiver == null) {
return "null";
}


Loading…
Cancel
Save