Skip to content

Instantly share code, notes, and snippets.

@forax
Last active November 1, 2016 22:45
Show Gist options
  • Save forax/c08ce0a8dc88705bce97b17b63e7e2d5 to your computer and use it in GitHub Desktop.
Save forax/c08ce0a8dc88705bce97b17b63e7e2d5 to your computer and use it in GitHub Desktop.
package java.lang.invoke;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.AnnotatedElement;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.function.Function;
import java.util.stream.Collectors;
public class AccessBroker {
private AccessBroker() { throw new InternalError(); }
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.PACKAGE, ElementType.MODULE})
public @interface GrantAccess {
String[] frameworks();
}
static final List<Function<Class<?>, AnnotatedElement>> ANNOTATION_PROVIDERS =
List.of(type -> type, Class::getPackage, Class::getModule);
private static final ClassValue<Function<String, Optional<Lookup>>> CACHE = new ClassValue<Function<String, Optional<Lookup>>>() {
@Override
protected Function<String, Optional<Lookup>> computeValue(Class<?> type) {
Set<String> allowedFrameworks = ANNOTATION_PROVIDERS
.stream()
.map(provider -> provider.apply(type))
.map(element -> element.getDeclaredAnnotation(GrantAccess.class))
.filter(Objects::nonNull)
.flatMap(access -> Arrays.stream(access.frameworks()))
.collect(Collectors.toSet());
if (allowedFrameworks.isEmpty()) {
return __ -> Optional.empty();
}
Optional<Lookup> lookup = Optional.of(new Lookup(type));
return frameworkName -> lookup.filter(__ -> allowedFrameworks.contains(frameworkName));
}
};
public interface AccessFacade {
public Optional<Lookup> getAccess(Class<?> type);
}
private static final CopyOnWriteArraySet<String> knownFrameworks = new CopyOnWriteArraySet<>();
public static AccessFacade getAccessFacade(String framework) {
if (!knownFrameworks.add(framework)) {
throw new IllegalStateException(); // framework already registered
}
return type -> CACHE.get(type).apply(framework);
}
}
import java.lang.invoke.AccessBroker;
import java.lang.invoke.AccessBroker.AccessFacade;
import java.lang.invoke.AccessBroker.GrantAccess;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
public class AccessBrokerMain {
@GrantAccess(frameworks = "hibernate")
static class A {
private void foo() { System.out.println("hello"); }
}
static class B {
public static void bar() {
// access A members from B
AccessFacade facade = AccessBroker.getAccessFacade("hibernate");
Lookup lookup = facade.getAccess(A.class).get();
MethodHandle mh;
try {
mh = lookup.findVirtual(A.class, "foo", MethodType.methodType(void.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new AssertionError(e);
}
try {
mh.invokeExact(new A());
} catch (Throwable e) {
throw new AssertionError(e);
}
}
}
public static void main(String[] args) {
B.bar();
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment