Skip to content

Commit

Permalink
Refactors and improves (#10)
Browse files Browse the repository at this point in the history
Signed-off-by: Laird Nelson <ljnelson@gmail.com>
  • Loading branch information
ljnelson authored Feb 7, 2024
1 parent 497e6de commit 31e3772
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 38 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Maven dependency:
<groupId>org.microbean</groupId>
<artifactId>microbean-interceptor</artifactId>
<!-- Always check https://search.maven.org/artifact/org.microbean/microbean-interceptor for up-to-date available versions. -->
<version>0.2.1</version>
<version>0.2.2</version>
</dependency>
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@
<detectJavaApiLink>false</detectJavaApiLink>
<links>https://docs.oracle.com/en/java/javase/11/docs/api,https://jakarta.ee/specifications/interceptors/2.1/apidocs/</links>

<!-- maven-release-plugin properties; see http://maven.apache.org/maven-release/maven-release-plugin/ -->
<!-- maven-release-plugin properties; see https://maven.apache.org/maven-release/maven-release-plugin/ -->
<goals>deploy,post-site,scm-publish:publish-scm</goals>

<scm.url>scm:git:git@github.com:microbean/microbean-interceptor.git</scm.url>
Expand Down
49 changes: 30 additions & 19 deletions src/main/java/org/microbean/interceptor/InterceptorMethod.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
*
* Copyright © 2023 microBean™.
* Copyright © 2023–2024 microBean™.
*
* 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
Expand Down Expand Up @@ -47,14 +47,15 @@ public interface InterceptorMethod {


/*
* Static methods.
* Public static methods.
*/


/**
* Returns a new {@link InterceptorMethod} that adapts the supplied {@code static} {@link Method}.
*
* @param staticMethod a {@code static} {@link Method}; must not be {@code null}
* @param staticMethod a {@code static} {@link Method}; must not be {@code null}; must accept exactly one {@link
* InvocationContext}-typed argument
*
* @return a new {@link InterceptorMethod}; never {@code null}
*
Expand All @@ -68,31 +69,33 @@ public static InterceptorMethod of(final Method staticMethod) {
* Returns a new {@link InterceptorMethod} that adapts the supplied {@link Method} and the supplied {@link Supplier}
* of its receiver.
*
* @param m a {@link Method}; must not be {@code null}
* @param m a {@link Method}; must not be {@code null}; must accept exactly one {@link InvocationContext}-typed
* argument
*
* @param targetSupplier a {@link Supplier} of the supplied {@link Method}'s receiver; may be {@code null} in which
* case the supplied {@link Method} must be {@code static}
* @param targetSupplier a {@link Supplier} of the supplied {@link Method}'s receiver; often memoized; may be {@code
* null} in which case the supplied {@link Method} must be {@code static}
*
* @return a new {@link InterceptorMethod}; never {@code null}
*
* @exception NullPointerException if {@code m} is {@code null}
*
* @exception IllegalStateException if {@linkplain java.lang.invoke.MethodHandles.Lookup#unreflect(Method)
* @exception InterceptorException if {@linkplain java.lang.invoke.MethodHandles.Lookup#unreflect(Method)
* unreflecting} fails
*/
public static InterceptorMethod of(final Method m, final Supplier<?> targetSupplier) {
try {
return of(privateLookupIn(m.getDeclaringClass(), lookup()).unreflect(m), targetSupplier);
} catch (final IllegalAccessException e) {
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
}
}

/**
* Returns a new {@link InterceptorMethod} that adapts the supplied {@link MethodHandle}.
*
* @param receiverlessOrBoundMethodHandle a {@link MethodHandle}; must not be {@code null}; must either not require a
* receiver or must be already {@linkplain MethodHandle#bindTo(Object) bound} to one
* receiver or must be already {@linkplain MethodHandle#bindTo(Object) bound} to one; must accept exactly one {@link
* InvocationContext}-typed argument
*
* @return a new {@link InterceptorMethod}; never {@code null}
*
Expand All @@ -106,11 +109,13 @@ public static InterceptorMethod of(final MethodHandle receiverlessOrBoundMethodH
* Returns a new {@link InterceptorMethod} that adapts the supplied {@link MethodHandle} and the supplied {@link
* Supplier of its receiver}.
*
* @param mh a {@link MethodHandle}; must not be {@code null}
* @param mh a {@link MethodHandle}; must not be {@code null}; must either accept two arguments where the first
* argument's type is a valid receiver type and the second argument's type is {@link InvocationContext}, or one
* argument whose type is {@link InvocationContext}
*
* @param receiverSupplier a {@link Supplier} of the supplied {@link MethodHandle}'s receiver; may be {@code null} in
* which case the supplied {@link MethodHandle} must either not require a receiver or must be already {@linkplain
* MethodHandle#bindTo(Object) bound} to one
* @param receiverSupplier a {@link Supplier} of the supplied {@link MethodHandle}'s receiver; often memoized; may be
* {@code null} in which case the supplied {@link MethodHandle} must either not require a receiver or must be already
* {@linkplain MethodHandle#bindTo(Object) bound} to one
*
* @return a new {@link InterceptorMethod}; never {@code null}
*
Expand All @@ -130,9 +135,9 @@ public static InterceptorMethod of(final MethodHandle mh, final Supplier<?> rece
throw e;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
} catch (final Throwable e) {
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
}
return null;
};
Expand All @@ -143,16 +148,22 @@ public static InterceptorMethod of(final MethodHandle mh, final Supplier<?> rece
return ic -> invokeExact(unboundInterceptorMethod, receiverSupplier, ic);
}


/*
* Private static methods.
*/


private static Object invokeExact(final MethodHandle mh, final InvocationContext ic) {
try {
return mh.invokeExact(ic);
} catch (final RuntimeException | Error e) {
throw e;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
} catch (final Throwable e) {
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
}
}

Expand All @@ -163,9 +174,9 @@ private static Object invokeExact(final MethodHandle mh, final Supplier<?> recei
throw e;
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
} catch (final Throwable e) {
throw new IllegalStateException(e.getMessage(), e);
throw new InterceptorException(e.getMessage(), e);
}
}

Expand Down
33 changes: 16 additions & 17 deletions src/test/java/org/microbean/interceptor/TestLambdaMetafactory.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
*
* Copyright © 2023 microBean™.
* Copyright © 2023–2024 microBean™.
*
* 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
Expand Down Expand Up @@ -37,6 +37,7 @@
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.assertNotNull;

// This is really more of an exploratory class than a test of anything in the org.microbean.interceptor package.
final class TestLambdaMetafactory {

private TestLambdaMetafactory() {
Expand Down Expand Up @@ -112,7 +113,7 @@ private final <T, U extends T> Function<InvocationContext, Object> frobnicate2(f
} else if (aroundInvokeMethodType.parameterType(1) != InvocationContext.class) {
throw new IllegalArgumentException("aroundInvokeMethodType.methodType().parameterType(0) != InvocationContext.class: " + aroundInvokeMethodType);
}

final ConstantCallSite metafactory =
(ConstantCallSite)metafactory(lookup,
"apply",
Expand All @@ -126,7 +127,7 @@ private final <T, U extends T> Function<InvocationContext, Object> frobnicate2(f
}

// See https://stackoverflow.com/a/77060967/208288
//
//
// If we want to use invokeExact() on target/factory, which we do here because we don't know how often the Function
// we return will be invoked, we need to change the type of its receiver parameter to permit the invokeExact call to
// work in this use case. Normally the symbolic type descriptor kind of does this for you if I understand right, but
Expand All @@ -150,10 +151,10 @@ private final <T, U extends T> Function<InvocationContext, Object> frobnicate2(f
}
};
}
private final <T> Function<T, Function<InvocationContext, Object>> frobnicate(final Lookup lookup,
final MethodHandle aroundInvokeMethod,
final Class<T> receiverType)

private final <T> Function<T, InterceptorMethod> frobnicate(final Lookup lookup,
final MethodHandle aroundInvokeMethod,
final Class<T> receiverType)
throws Throwable {
Objects.requireNonNull(receiverType); // for now
final MethodType aroundInvokeMethodType = aroundInvokeMethod.type();
Expand All @@ -172,18 +173,18 @@ private final <T> Function<T, Function<InvocationContext, Object>> frobnicate(fi
} else if (aroundInvokeMethodType.parameterType(1) != InvocationContext.class) {
throw new IllegalArgumentException("aroundInvokeMethodType.methodType().parameterType(0) != InvocationContext.class: " + aroundInvokeMethodType);
}

final ConstantCallSite ccs =
(ConstantCallSite)metafactory(lookup,
"apply",
methodType(Function.class, receiverType), // Function is the functional interface we want. receiverType is a capture.
methodType(Object.class, Object.class), // Function<Object, InvocationContext> erasure
"intercept",
methodType(InterceptorMethod.class, receiverType), // InterceptorMethod is the functional interface we want. receiverType is a capture.
methodType(Object.class, InvocationContext.class),
aroundInvokeMethod,
methodType(Object.class, InvocationContext.class));
final MethodHandle factory = ccs.getTarget();
return t -> {
try {
return (Function<InvocationContext, Object>)factory.invoke(t);
return (InterceptorMethod)factory.invoke(t);
} catch (RuntimeException | Error e) {
throw e;
} catch (final InterruptedException e) {
Expand All @@ -193,18 +194,16 @@ private final <T> Function<T, Function<InvocationContext, Object>> frobnicate(fi
throw new IllegalStateException(e.getMessage(), e);
}
};
// return ccs.getTarget();
}

@Test
final void testFrobnicate() throws Throwable {
final Lookup lookup = lookup();
MethodHandle mh = lookup.findVirtual(this.getClass(), "aroundInvokeVirtual", methodType(Object.class, InvocationContext.class));
Function<TestLambdaMetafactory, Function<InvocationContext, Object>> factory = frobnicate(lookup, mh, TestLambdaMetafactory.class);
final Function<InvocationContext, Object> f = factory.apply(this);
// final Function<InvocationContext, Object> f = (Function<InvocationContext, Object>)mh.invokeExact(this);
Function<TestLambdaMetafactory, InterceptorMethod> factory = frobnicate(lookup, mh, TestLambdaMetafactory.class);
final InterceptorMethod f = factory.apply(this);
assertNotNull(f);
f.apply(new SimpleInvocationContext());
f.intercept(new SimpleInvocationContext());
}

@Test
Expand Down

0 comments on commit 31e3772

Please sign in to comment.