Can Lambda expressions access private methods of classes outside their scope? - java

I want to gain reflective access to java.lang.String's package private constructor.
Namely, this one:
/*
* Package private constructor which shares value array for speed.
* this constructor is always expected to be called with share==true.
* a separate constructor is needed because we already have a public
* String(char[]) constructor that makes a copy of the given char[].
*/
String(char[] value, boolean share) {
// assert share : "unshared not supported";
this.value = value;
}
Creating a MethodHandle for it is simple enough, and so is invoking it.
The same is true for using Reflection directly.
But I'm curious whether it's possible to directly call the constructor via functional interfaces.
27602758 touches on a somewhat similar issue, but the solutions provided do not appear to work in this case.
The test case below compiles without issues. Everything works, except for the actual interface invocation.
package test;
import java.lang.invoke.CallSite;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public class Test {
// Creates a new String that shares the supplied char[]
private static interface StringCreator {
public String create(char[] value, boolean shared);
}
// Creates a new conventional String
private static String create(char[] value, boolean shared) {
return String.valueOf(value);
}
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the calling class
Lookup caller = MethodHandles.lookup();
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for #create()
MethodHandle conventional = caller.findStatic(
Test.class, "create", MethodType.methodType(String.class, char[].class, boolean.class)
);
StringCreator normal = getStringCreator(caller, conventional);
System.out.println(
normal.create("foo".toCharArray(), true)
// prints "foo"
);
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// test directly if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = (String) constructor.invokeExact(chars, true);
chars[0] = 'b'; // modify array contents
chars[1] = 'a';
chars[2] = 'r';
System.out.println(
s
// prints "bar"
);
// generate interface for constructor
StringCreator shared = getStringCreator(caller, constructor);
System.out.println(
shared.create("foo".toCharArray(), true)
// throws error
);
}
// returns a StringCreator instance
private static StringCreator getStringCreator(Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"create",
MethodType.methodType(StringCreator.class),
handle.type(),
handle,
handle.type()
);
return (StringCreator) callSite.getTarget().invokeExact();
}
}
Specficially the instruction
shared.create("foo".toCharArray(), true)
throws the following error:
Exception in thread "main" java.lang.IllegalAccessError: tried to access method java.lang.String.<init>([CZ)V from class test.Test$$Lambda$2/989110044 at test.Test.main(Test.java:59)
Why is this error still being thrown, despite access ostensibly being granted?
Can anyone come up with an explanation for why the generated interface has no access to a method that all of its components have access to?
Is there a solution or a viable alternative that actually works for this particular use case, without reverting to pure Reflection or MethodHandles?
Because I'm stumped.

The problem is that you override the lookup object to be trusted, so its access to a private method of String will pass the lookup procedure and lambda meta factory, but its still bound to your Test class as that’s the class which created the lookup object via MethodHandles.lookup() and the generated class will live in the same context. The JVM is quite generous regarding accessibility when it comes to these generated classes but apparently, accessing a private member of the bootstrap class java.lang.String from a class living in the context of your application class is not accepted.
You can get a lookup object living in an appropriate context via, e.g. MethodHandles.lookup() .in(String.class) (and then patching it to have private or “trusted” access), but then, you will get another problem: a class living in the context of java.lang.String (or just in the bootstrap loader’s context) will not have access to your custom interface StringCreator and can’t implement it.
The only solution is to use a lookup object living in the context of String and implementing one of the existing generic interfaces, accessible from the bootstrap class loader:
import java.lang.invoke.*;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.reflect.Field;
import java.util.function.BiFunction;
public class Test {
public static void main(String[] args) throws Throwable {
// Reflectively generate a TRUSTED Lookup for the String class
Lookup caller = MethodHandles.lookup().in(String.class);
Field modes = Lookup.class.getDeclaredField("allowedModes");
modes.setAccessible(true);
modes.setInt(caller, -1); // -1 == Lookup.TRUSTED
// create handle for shared String constructor
MethodHandle constructor = caller.findConstructor(
String.class, MethodType.methodType(void.class, char[].class, boolean.class)
);
// generate interface implementation for constructor
BiFunction<char[],Boolean,String> shared=getStringCreator(caller, constructor);
// test if the construcor is correctly accessed
char[] chars = "foo".toCharArray();
String s = shared.apply(chars, true);
chars[0] = 'b'; chars[1] = 'a'; chars[2] = 'r';// modify array contents
System.out.println(s); // prints "bar"
chars[0] = '1'; chars[1] = '2'; chars[2] = '3';
System.out.println(s); // prints "123"
}
private static BiFunction<char[],Boolean,String> getStringCreator(
Lookup caller, MethodHandle handle) throws Throwable {
CallSite callSite = LambdaMetafactory.metafactory(
caller,
"apply",
MethodType.methodType(BiFunction.class),
handle.type().generic(),
handle,
handle.type()
);
return (BiFunction) callSite.getTarget().invokeExact();
}
}

Related

How to change static variable value using ASM?

I started learning Java Agent few days ago. But documentation is not very good and beginners like me struggling to understand the basics. I created a basic multiplier class and export it to runnable jar using eclipse. Here is the code snippet.
Main jar file:
public class Multiplier {
public static void main(String[] args) {
int x = 10;
int y = 25;
int z = x * y;
System.out.println("Multiply of x*y = " + z);
}
}
Bytecode for above class
Now I want to manipulate the value of x from an agent. I tried to create the Agent class like this
Agent:
package myagent;
import org.objectweb.asm.*;
import java.lang.instrument.*;
public class Agent {
public static void premain(final String agentArg, final Instrumentation inst) {
System.out.println("Agent Started");
int x_modified = 5;
//Now How to push the new value (x_modified) to the multiplier class?
//I know I have to use ASM but can't figure it out how to do it.
//Result should be 125
}
}
My Question
How do I set the value of x from agent class to multiplier class using ASM?
Result should be 125.
The first thing, your agent has to do, is registering a ClassFileTransformer. The first thing, the class file transformer should do in its transform method, is checking the arguments to find out whether the current request is about the class we’re interested in, to return immediately if not.
If we are at the class we want to transform, we have to process the incoming class file bytes to return a new byte array. You can use ASM’s ClassReader to process to incoming bytes and chain it to a ClassWriter to produce a new array:
import java.lang.instrument.*;
import java.security.ProtectionDomain;
import org.objectweb.asm.*;
public class ExampleAgent implements ClassFileTransformer {
private static final String TRANSFORM_CLASS = "Multiplier";
private static final String TRANSFORM_METHOD_NAME = "main";
private static final String TRANSFORM_METHOD_DESC = "([Ljava/lang/String;)V";
public static void premain(String arg, Instrumentation instrumentation) {
instrumentation.addTransformer(new ExampleAgent());
}
public byte[] transform(ClassLoader loader, String className, Class<?> cl,
ProtectionDomain pd, byte[] classfileBuffer) {
if(!TRANSFORM_CLASS.equals(className)) return null;
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(cr, 0);
cr.accept(new ClassVisitor(Opcodes.ASM5, cw) {
#Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(
access, name, desc, signature, exceptions);
if(name.equals(TRANSFORM_METHOD_NAME)
&& desc.equals(TRANSFORM_METHOD_DESC)) {
return new MethodVisitor(Opcodes.ASM5, mv) {
#Override
public void visitIntInsn(int opcode, int operand) {
if(opcode == Opcodes.BIPUSH && operand == 10) operand = 5;
super.visitIntInsn(opcode, operand);
}
};
}
return mv;
}
}, 0);
return cw.toByteArray();
}
}
Note that by passing the ClassWriter to our custom ClassVisitor’s constructor and passing the MethodVisitor returned by the super.visitMethod invocation to our MethodVisitor’s constructor, we enable a chaining that reproduces the original class by default; all methods we’re not overriding will delegate to the specified ClassWriter/MethodVisitor reproducing the encountered artifact. Compare with the tutorial about ASM’s event model.
The example above enables an optimization by also passing the ClassReader instance to the ClassWriter’s constructor. This improves the efficiency of instrumenting a class when only making small changes, like we do here.
The crucial part is overriding visitMethod to return our custom MethodVisitor when we are at the “hot” method and overriding visitIntInsn to change the desired instruction. Note how these methods delegate to the super calls when not altering the behavior, just like the methods we didn’t override.
You have declared x inside main method. So it's scope is local. That's why you can't change the value of x from any other class.
To use ASM you need a custom CodeWriter in a custom ClassWriter which you pass to a ClassReader. http://asm.ow2.org/doc/tutorial.html This will allow you to visit all instructions in the code for each method.
In particular you will need to override the visitIntInsn method so when you see the first BIPUSH instruction in main you can replace the value 10, with what ever value you chose.
The output of ClassWriter is a byte[] which your Instrumentation will return instead of the original code at which point x will be whatever value you made it in the code.

Want to run non-threadsafe library in parallel - can it be done using multiple classloaders?

I work on a project where we use a library that is not guaranteed thread-safe (and isn't) and single-threaded in a Java 8 streams scenario, which works as expected.
We would like to use parallel streams to get the low hanging scalability fruit.
Unfortunately this cause the library to fail - most likely because one instance interferes with variables shared with the other instance - hence we need isolation.
I was considering using a separate classloader for each instance (possibly thread local) which to my knowledge should mean that for all practical purposes that I get the isolation needed but I am unfamiliar with deliberately constructing classloaders for this purpose.
Is this the right approach? How shall I do this in order to have proper production quality?
Edit: I was asked for additional information about the situation triggering the question, in order to understand it better. The question is still about the general situation, not fixing the library.
I have full control over the object created by the library (which is https://github.com/veraPDF/) as pulled in by
<dependency>
<groupId>org.verapdf</groupId>
<artifactId>validation-model</artifactId>
<version>1.1.6</version>
</dependency>
using the project maven repository for artifacts.
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>vera-dev</id>
<name>Vera development</name>
<url>http://artifactory.openpreservation.org/artifactory/vera-dev</url>
</repository>
</repositories>
For now it is unfeasible to harden the library.
EDIT: I was asked to show code. Our core adapter is roughly:
public class VeraPDFValidator implements Function<InputStream, byte[]> {
private String flavorId;
private Boolean prettyXml;
public VeraPDFValidator(String flavorId, Boolean prettyXml) {
this.flavorId = flavorId;
this.prettyXml = prettyXml;
VeraGreenfieldFoundryProvider.initialise();
}
#Override
public byte[] apply(InputStream inputStream) {
try {
return apply0(inputStream);
} catch (RuntimeException e) {
throw e;
} catch (ModelParsingException | ValidationException | JAXBException | EncryptedPdfException e) {
throw new RuntimeException("invoking VeraPDF validation", e);
}
}
private byte[] apply0(InputStream inputStream) throws ModelParsingException, ValidationException, JAXBException, EncryptedPdfException {
PDFAFlavour flavour = PDFAFlavour.byFlavourId(flavorId);
PDFAValidator validator = Foundries.defaultInstance().createValidator(flavour, false);
PDFAParser loader = Foundries.defaultInstance().createParser(inputStream, flavour);
ValidationResult result = validator.validate(loader);
// do in-memory generation of XML byte array - as we need to pass it to Fedora we need it to fit in memory anyway.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XmlSerialiser.toXml(result, baos, prettyXml, false);
final byte[] byteArray = baos.toByteArray();
return byteArray;
}
}
which is a function that maps from an InputStream (providing a PDF-file) to a byte array (representing the XML report output).
(Seeing the code, I've noticed that there is a call to the initializer in the constructor, which may be the culprit here in my particular case. I'd still like a solution to the generic problem.
We have faced similar challenges. Issues usually came from from static properties which became unwillingly "shared" between the various threads.
Using different classloaders worked for us as long as we could guarantee that the static properties were actually set on classes loaded by our class loader. Java may have a few classes which provide properties or methods which are not isolated among threads or are not thread-safe ('System.setProperties() and Security.addProvider() are OK - any canonical documentation on this matter is welcomed btw).
A potentially workable and fast solution - that at least can give you a chance to test this theory for your library - is to use a servlet engine such as Jetty or Tomcat.
Build a few wars that contain your library and start processes in parallel (1 per war).
When running code inside a servlet thread, the WebappClassLoaders of these engines attempt to load a classes from the parent class loader first (the same as the engine) and if it does not find the class, attempts to load it from the jars/classes packaged with the war.
With jetty you can programmatically hot deploy wars to the context of your choice and then theoretically scale the number of processors (wars) as required.
We have implemented our own class loader by extending URLClassLoader and have taken inspiration from the Jetty Webapp ClassLoader. It is not as hard a job as as it seems.
Our classloader does the exact opposite: it attempts to load a class from the jars local to the 'package' first , then tries to get them from the parent class loader. This guarantees that a library accidentally loaded by the parent classloader is never considered (first). Our 'package' is actually a jar that contains other jars/libraries with a customized manifest file.
Posting this class loader code "as is" would not make a lot of sense (and create a few copyright issues). If you want to explore that route further, I can try coming up with a skeleton.
Source of the Jetty WebappClassLoader
The answer actually depends on what your library relies on:
If your library relies on at least one native library, using ClassLoaders to isolate your library's code won't help because according to the JNI Specification, it is not allowed to load the same JNI native library into more than one class loader such that you would end up with a UnsatisfiedLinkError.
If you library relies on at least one external resource that is not meant to be shared like for example a file and that is modified by your library, you could end up with complex bugs and/or the corruption of the resource.
Assuming that you are not in the cases listed above, generally speaking if a class is known as non thread safe and doesn't modify any static fields, using a dedicated instance of this class per call or per thread is good enough as the class instance is then no more shared.
Here as your library obviously relies and modifies some static fields that are not meant to be shared, you indeed need to isolate the classes of your library in a dedicated ClassLoader and of course make sure that your threads don't share the same ClassLoader.
For this you could simply create an URLClassLoader to which you would provide the location of your library as URL (using URLClassLoader.newInstance(URL[] urls, ClassLoader parent)), then by reflection you would retrieve the class of your library corresponding to the entry point and invoke your target method. To avoid building a new URLClassLoader at each call, you could consider relying on a ThreadLocal to store the URLClassLoader or the Class or the Method instance to be used for a given thread.
So here is how you could proceed:
Let's say that the entry point of my library is the class Foo that looks like this:
package com.company;
public class Foo {
// A static field in which we store the name of the current thread
public static String threadName;
public void execute() {
// We print the value of the field before setting a value
System.out.printf(
"%s: The value before %s%n", Thread.currentThread().getName(), threadName
);
// We set a new value
threadName = Thread.currentThread().getName();
// We print the value of the field after setting a value
System.out.printf(
"%s: The value after %s%n", Thread.currentThread().getName(), threadName
);
}
}
This class is clearly not thread safe and the method execute modifies the value of a static field that is not meant to be modified by concurrent threads just like your use case.
Assuming that to launch my library I simply need to create an instance of Foo and invoke the method execute. I could store the corresponding Method in a ThreadLocal to retrieve it by reflection only once per thread using ThreadLocal.withInitial(Supplier<? extends S> supplier) as next:
private static final ThreadLocal<Method> TL = ThreadLocal.withInitial(
() -> {
try {
// Create the instance of URLClassLoader using the context
// CL as parent CL to be able to retrieve the potential
// dependencies of your library assuming that they are
// thread safe otherwise you will need to provide their
// URL to isolate them too
URLClassLoader cl = URLClassLoader.newInstance(
new URL[]{/* Here the URL of my library*/},
Thread.currentThread().getContextClassLoader()
);
// Get by reflection the class Foo
Class<?> myClass = cl.loadClass("com.company.Foo");
// Get by reflection the method execute
return myClass.getMethod("execute");
} catch (Exception e) {
// Here deal with the exceptions
throw new IllegalStateException(e);
}
}
);
And finally let's simulate a concurrent execution of my library:
// Launch 50 times concurrently my library
IntStream.rangeClosed(1, 50).parallel().forEach(
i -> {
try {
// Get the method instance from the ThreadLocal
Method myMethod = TL.get();
// Create an instance of my class using the default constructor
Object myInstance = myMethod.getDeclaringClass().newInstance();
// Invoke the method
myMethod.invoke(myInstance);
} catch (Exception e) {
// Here deal with the exceptions
throw new IllegalStateException(e);
}
}
);
You will get an output of the next type that shows that we have no conflicts between threads and the threads properly reuse its corresponding class/field's value from one call of execute to another:
ForkJoinPool.commonPool-worker-7: The value before null
ForkJoinPool.commonPool-worker-7: The value after ForkJoinPool.commonPool-worker-7
ForkJoinPool.commonPool-worker-7: The value before ForkJoinPool.commonPool-worker-7
ForkJoinPool.commonPool-worker-7: The value after ForkJoinPool.commonPool-worker-7
main: The value before null
main: The value after main
main: The value before main
main: The value after main
...
Since this approach will create one ClassLoader per thread, make sure to apply this approach using a thread pool with a fixed number of threads and the number of threads should be chosen wisely to prevent running out of memory because a ClassLoader is not free in term of memory footprint so you need to limit the total amount of instances according to your heap size.
Once you are done with your library, you should cleanup the ThreadLocal for each thread of your thread pool to prevent memory leaks and to do so here is how you could proceed:
// The size of your the thread pool
// Here as I used for my example the common pool, its size by default is
// Runtime.getRuntime().availableProcessors()
int poolSize = Runtime.getRuntime().availableProcessors();
// The cyclic barrier used to make sure that all the threads of the pool
// will execute the code that will cleanup the ThreadLocal
CyclicBarrier barrier = new CyclicBarrier(poolSize);
// Launch one cleanup task per thread in the pool
IntStream.rangeClosed(1, poolSize).parallel().forEach(
i -> {
try {
// Wait for all other threads of the pool
// This is needed to fill up the thread pool in order to make sure
// that all threads will execute the cleanup code
barrier.await();
// Close the URLClassLoader to prevent memory leaks
((URLClassLoader) TL.get().getDeclaringClass().getClassLoader()).close();
} catch (Exception e) {
// Here deal with the exceptions
throw new IllegalStateException(e);
} finally {
// Remove the URLClassLoader instance for this thread
TL.remove();
}
}
);
I found the question interesing and created a little tool for you:
https://github.com/kriegaex/ThreadSafeClassLoader
Currently it is not available as an official release on Maven Central yet, but you can get a snapshot like this:
<dependency>
<groupId>de.scrum-master</groupId>
<artifactId>threadsafe-classloader</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<!-- (...) -->
<repositories>
<repository>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ossrh</id>
<name>Sonatype OSS Snapshots</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
Class ThreadSafeClassLoader:
It uses JCL (Jar Class Loader) under the hood because it already offers class-loading, object instantiation and proxy generation features discussed in other parts of this thread. (Why re-invent the wheel?) What I added on top is a nice interface for exactly what we need here:
package de.scrum_master.thread_safe;
import org.xeustechnologies.jcl.JarClassLoader;
import org.xeustechnologies.jcl.JclObjectFactory;
import org.xeustechnologies.jcl.JclUtils;
import org.xeustechnologies.jcl.proxy.CglibProxyProvider;
import org.xeustechnologies.jcl.proxy.ProxyProviderFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class ThreadSafeClassLoader extends JarClassLoader {
private static final JclObjectFactory OBJECT_FACTORY = JclObjectFactory.getInstance();
static {
ProxyProviderFactory.setDefaultProxyProvider(new CglibProxyProvider());
}
private final List<Class> classes = new ArrayList<>();
public static ThreadLocal<ThreadSafeClassLoader> create(Class... classes) {
return ThreadLocal.withInitial(
() -> new ThreadSafeClassLoader(classes)
);
}
private ThreadSafeClassLoader(Class... classes) {
super();
this.classes.addAll(Arrays.asList(classes));
for (Class clazz : classes)
add(clazz.getProtectionDomain().getCodeSource().getLocation());
}
public <T> T newObject(ObjectConstructionRules rules) {
rules.validate(classes);
Class<T> castTo = rules.targetType;
return JclUtils.cast(createObject(rules), castTo, castTo.getClassLoader());
}
private Object createObject(ObjectConstructionRules rules) {
String className = rules.implementingType.getName();
String factoryMethod = rules.factoryMethod;
Object[] arguments = rules.arguments;
Class[] argumentTypes = rules.argumentTypes;
if (factoryMethod == null) {
if (argumentTypes == null)
return OBJECT_FACTORY.create(this, className, arguments);
else
return OBJECT_FACTORY.create(this, className, arguments, argumentTypes);
} else {
if (argumentTypes == null)
return OBJECT_FACTORY.create(this, className, factoryMethod, arguments);
else
return OBJECT_FACTORY.create(this, className, factoryMethod, arguments, argumentTypes);
}
}
public static class ObjectConstructionRules {
private Class targetType;
private Class implementingType;
private String factoryMethod;
private Object[] arguments;
private Class[] argumentTypes;
private ObjectConstructionRules(Class targetType) {
this.targetType = targetType;
}
public static ObjectConstructionRules forTargetType(Class targetType) {
return new ObjectConstructionRules(targetType);
}
public ObjectConstructionRules implementingType(Class implementingType) {
this.implementingType = implementingType;
return this;
}
public ObjectConstructionRules factoryMethod(String factoryMethod) {
this.factoryMethod = factoryMethod;
return this;
}
public ObjectConstructionRules arguments(Object... arguments) {
this.arguments = arguments;
return this;
}
public ObjectConstructionRules argumentTypes(Class... argumentTypes) {
this.argumentTypes = argumentTypes;
return this;
}
private void validate(List<Class> classes) {
if (implementingType == null)
implementingType = targetType;
if (!classes.contains(implementingType))
throw new IllegalArgumentException(
"Class " + implementingType.getName() + " is not protected by this thread-safe classloader"
);
}
}
}
I tested my concept with several unit and integration tests, among them one showing how to reproduce and solve the veraPDF problem.
Now this is what your code looks like when using my special classloader:
Class VeraPDFValidator:
We are just adding a static ThreadLocal<ThreadSafeClassLoader> member to our class, telling it which classes/libraries to put into the new classloader (mentioning one class per library is enough, subsequently my tool identifies the library automatically).
Then via threadSafeClassLoader.get().newObject(forTargetType(VeraPDFValidatorHelper.class)) we instantiate our helper class inside the thread-safe classloader and create a proxy object for it so we can call it from outside.
BTW, static boolean threadSafeMode only exists to switch between the old (unsafe) and new (thread-safe) usage of veraPDF so as to make the original problem reproducible for the negative integration test case.
package de.scrum_master.app;
import de.scrum_master.thread_safe.ThreadSafeClassLoader;
import org.verapdf.core.*;
import org.verapdf.pdfa.*;
import javax.xml.bind.JAXBException;
import java.io.InputStream;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Function;
import static de.scrum_master.thread_safe.ThreadSafeClassLoader.ObjectConstructionRules.forTargetType;
public class VeraPDFValidator implements Function<InputStream, byte[]> {
public static boolean threadSafeMode = true;
private static ThreadLocal<ThreadSafeClassLoader> threadSafeClassLoader =
ThreadSafeClassLoader.create( // Add one class per artifact for thread-safe classloader:
VeraPDFValidatorHelper.class, // - our own helper class
PDFAParser.class, // - veraPDF core
VeraGreenfieldFoundryProvider.class // - veraPDF validation-model
);
private String flavorId;
private Boolean prettyXml;
public VeraPDFValidator(String flavorId, Boolean prettyXml)
throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
this.flavorId = flavorId;
this.prettyXml = prettyXml;
}
#Override
public byte[] apply(InputStream inputStream) {
try {
VeraPDFValidatorHelper validatorHelper = threadSafeMode
? threadSafeClassLoader.get().newObject(forTargetType(VeraPDFValidatorHelper.class))
: new VeraPDFValidatorHelper();
return validatorHelper.validatePDF(inputStream, flavorId, prettyXml);
} catch (ModelParsingException | ValidationException | JAXBException | EncryptedPdfException e) {
throw new RuntimeException("invoking veraPDF validation", e);
}
}
}
Class VeraPDFValidatorHelper:
In this class we isolate all access to the broken library. Nothing special here, just code copied from the OP's question. Everything done here happens inside the thread-safe classloader.
package de.scrum_master.app;
import org.verapdf.core.*;
import org.verapdf.pdfa.*;
import org.verapdf.pdfa.flavours.PDFAFlavour;
import org.verapdf.pdfa.results.ValidationResult;
import javax.xml.bind.JAXBException;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class VeraPDFValidatorHelper {
public byte[] validatePDF(InputStream inputStream, String flavorId, Boolean prettyXml)
throws ModelParsingException, ValidationException, JAXBException, EncryptedPdfException
{
VeraGreenfieldFoundryProvider.initialise();
PDFAFlavour flavour = PDFAFlavour.byFlavourId(flavorId);
PDFAValidator validator = Foundries.defaultInstance().createValidator(flavour, false);
PDFAParser loader = Foundries.defaultInstance().createParser(inputStream, flavour);
ValidationResult result = validator.validate(loader);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
XmlSerialiser.toXml(result, baos, prettyXml, false);
return baos.toByteArray();
}
}
By isolating a library on a class loader per thread, you can guarantee any classes concurrency properties as you suggest. The only exception are libraries that explicitly interact with the bootstrap class loader or the system class loader. It is possible to inject classes into these class loaders by either reflection or the Instrumentation API. One example for such functionality would be Mockito's inline mock maker that does however not suffer a concurrency constraint as of my knowledge.
Implementing a class loader with this behavior is not all too difficult. The easiest solution would be to explicitly include the required jars in your project, e.g. as a resource. This way, you could use a URLClassLoader for loading your classes:
URL url = getClass().getClassLoader().getResource("validation-model-1.1.6.jar");
ClassLoader classLoader = new URLClassLoader(new URL[] {url}, null);
By referencing null as the super class loader of the URLClassLoader (second argument), you guarantee that there are no shared classes outside of the bootstrap classes. Note that you cannot use any classes of this created class loader from outside of it. However, if you add a second jar containing a class that triggers your logic, you can offer an entry point that becomes accessible without reflection:
class MyEntryPoint implements Callable<File> {
#Override public File call() {
// use library code.
}
}
Simply add this class to its own jar and supply it as a second element to the above URL array. Note that you cannot reference a library type as a return value as this type will not be available to the consumer that lives outside the class loader that makes use of the entry point.
By wrapping the class loader creation into a ThreadLocal, you can guarantee the class loaders uniqunes:
class Unique extends ThreadLocal<ClassLoader> implements Closable {
#Override protected ClassLoader initialValue() {
URL validation = Unique.class.getClassLoader()
.getResource("validation-model-1.1.6.jar");
URL entry = Unique.class.getClassLoader()
.getResource("my-entry.jar");
return new URLClassLoader(new URL[] {validation, entry}, null);
}
#Override public void close() throws IOException {
get().close(); // If Java 7+, avoid handle leaks.
set(null); // Make class loader eligable for GC.
}
public File doSomethingLibrary() throws Exception {
Class<?> type = Class.forName("pkg.MyEntryPoint", false, get());
return ((Callable<File>) type.newInstance()).call();
}
}
Note that class loaders are expensive objects and should be dereferenced when you do no longer need them even if a thread continues to live. Also, to avoid file leaks, you should close a URLClassLoader previously to dereferencing.
Finally, in order to continue using Maven's dependency resolution and in order to simplify your code, you can create a seperate Maven module where you define your entry point code and declare your Maven library dependencies. Upon packaging, use the Maven shade plugin to create an Uber jar that includes everything you need. This way, you only need to provide a single jar to your URLClassLoader and do not need to ensure all (transitive) dependencies manually.
This answer is based on my original "plugin" comment. And it starts with a class loader that inherits from boot and extensions class loaders only.
package safeLoaderPackage;
import java.net.URL;
import java.net.URLClassLoader;
public final class SafeClassLoader extends URLClassLoader{
public SafeClassLoader(URL[] paths){
super(paths, ClassLoader.getSystemClassLoader().getParent());
}
}
This is the only class that needs to be included in the user's class path. This url class loader inherits from the parent of ClassLoader.getSystemClassLoader(). It just includes the boot and the extensions class loader. It has no notion of the class path used by the user.
Next
package safeLoaderClasses;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class SecureClassLoaderPlugin <R> {
private URL[] paths;
private Class[] args;
private String method;
private String unsafe;
public void setMethodData(final String u, final URL[] p, String m, Class[] a){
method = m;
args = a;
paths = p;
unsafe = u;
}
public Collection<R> processUnsafe(Object[][] p){
int i;
BlockingQueue<Runnable> q;
ArrayList<R> results = new ArrayList<R>();
try{
i = p.length;
q = new ArrayBlockingQueue<Runnable>(i);
ThreadPoolExecutor tpe = new ThreadPoolExecutor(i, i, 0, TimeUnit.NANOSECONDS, q);
for(Object[] params : p)
tpe.execute(new SafeRunnable<R>(unsafe, paths, method, args, params, results));
while(tpe.getActiveCount() != 0){
Thread.sleep(10);
}
for(R r: results){
System.out.println(r);
}
tpe.shutdown();
}
catch(Throwable t){
}
finally{
}
return results;
}
}
and
package safeLoaderClasses;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import safeLoaderInterface.SafeClassLoader;
class SafeRunnable <R> implements Runnable{
final URL[] paths;
final private String unsafe;
final private String method;
final private Class[] args;
final private Object[] processUs;
final ArrayList<R> result;
SafeRunnable(String u, URL[] p, String m, Class[] a, Object[] params, ArrayList<R> r){
unsafe = u;
paths = p;
method = m;
args = a;
processUs = params;
result = r;
}
public void run() {
Class clazz;
Object instance;
Method m;
SafeClassLoader sl = null;
try{
sl = new SafeClassLoader(paths);
System.out.println(sl);
clazz = sl.loadClass(unsafe);
m = clazz.getMethod(method, args);
instance = clazz.newInstance();
synchronized(result){
result.add((R) m.invoke(instance, processUs));
}
}
catch(Throwable t){
t.printStackTrace();
}
finally{
try {
sl.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
are the plugin jar. No lambdas. Just a thread pool executor. Each thread just adds to a result list after execution.
The generics need polishing but I have tested these against this class (resides in a different jar)
package stackoverflow4;
public final class CrazyClass {
static int i = 0;
public int returnInt(){
System.out.println(i);
return 8/++i;
}
}
This would be the way to connect from one's code. The path to the class loader needs to be included because it is lost with the getParent() call
private void process(final String plugin, final String unsafe, final URL[] paths) throws Exception{
Object[][] passUs = new Object[][] {{},{}, {},{}, {},{},{},{},{},{}};
URL[] pathLoader = new URL[]{new File(new String(".../safeLoader.jar")).toURI().toURL(),
new File(new String(".../safeLoaderClasses.jar")).toURI().toURL()};
//instantiate the loader
SafeClassLoader sl = new SafeClassLoader(pathLoader);
System.out.println(sl);
Class clazz = sl.loadClass("safeLoaderClasses.SecureClassLoaderPlugin");
//Instance of the class that loads the unsafe jar and launches the thread pool executor
Object o = clazz.newInstance();
//Look up the method that set ups the unsafe library
Method m = clazz.getMethod("setMethodData",
new Class[]{unsafe.getClass(), paths.getClass(), String.class, new Class[]{}.getClass()});
//invoke it
m.invoke(o, new Object[]{unsafe,paths,"returnInt", new Class[]{}});
//Look up the method that invokes the library
m = clazz.getMethod("processUnsafe", new Class[]{ passUs.getClass()});
//invoke it
o = m.invoke(o, passUs);
//Close the loader
sl.close();
}
with up to 30+ threads and it seems to work. The plugin uses a separate class loader and each of the threads use their own class loader. After leaving the method everything is gc'ed.
I believe you should try to fix the problem before seeking for workaround.
You can always run you code in two threads, classloaders, processes, containers, VM, or machines. But they are none of the ideal.
I saw two defaultInstance() from the code. Do the instance threadsafe? If not, can we have two instance? Is it a factory or a singleton?
Second, where the conflicts happen? If it was about initialization/cache problem, a pre warming should fix.
Last but not least, if the library was open-source, fork it fix it and pull request.
"It is unfeasible to harden the library" but it is feasible to introduce such a bloody workaround like a custom class loader?
OK. I am the first who dislikes the replies which are not a reply to the original question. But I honestly believe that patching the library is much easier to do and to mantain than introducing a custom class loader.
The blocker is class org.verapdf.gf.model.impl.containers.StaticContainers which static fields can be easily changed to work per thread as shown below. This impacts six other classes
org.verapdf.gf.model.GFModelParser
org.verapdf.gf.model.factory.colors.ColorSpaceFactory
org.verapdf.gf.model.impl.cos.GFCosFileSpecification
org.verapdf.gf.model.impl.external.GFEmbeddedFile
org.verapdf.gf.model.impl.pd.colors.GFPDSeparation
org.verapdf.gf.model.tools.FileSpecificationKeysHelper
You can still have only one PDFAParser per thread. But the fork takes ten minutes to do and worked for me in a basic multithread smoke test. I'd test this and contact the original author of the library. Maybe he is happy to merge and you can just keep a Maven reference to the updated and mantained library.
package org.verapdf.gf.model.impl.containers;
import org.verapdf.as.ASAtom;
import org.verapdf.cos.COSKey;
import org.verapdf.gf.model.impl.pd.colors.GFPDSeparation;
import org.verapdf.gf.model.impl.pd.util.TaggedPDFRoleMapHelper;
import org.verapdf.model.pdlayer.PDColorSpace;
import org.verapdf.pd.PDDocument;
import org.verapdf.pdfa.flavours.PDFAFlavour;
import java.util.*;
public class StaticContainers {
private static ThreadLocal<PDDocument> document;
private static ThreadLocal<PDFAFlavour> flavour;
// TaggedPDF
public static ThreadLocal<TaggedPDFRoleMapHelper> roleMapHelper;
//PBoxPDSeparation
public static ThreadLocal<Map<String, List<GFPDSeparation>>> separations;
public static ThreadLocal<List<String>> inconsistentSeparations;
//ColorSpaceFactory
public static ThreadLocal<Map<String, PDColorSpace>> cachedColorSpaces;
public static ThreadLocal<Set<COSKey>> fileSpecificationKeys;
public static void clearAllContainers() {
document = new ThreadLocal<PDDocument>();
flavour = new ThreadLocal<PDFAFlavour>();
roleMapHelper = new ThreadLocal<TaggedPDFRoleMapHelper>();
separations = new ThreadLocal<Map<String, List<GFPDSeparation>>>();
separations.set(new HashMap<String,List<GFPDSeparation>>());
inconsistentSeparations = new ThreadLocal<List<String>>();
inconsistentSeparations.set(new ArrayList<String>());
cachedColorSpaces = new ThreadLocal<Map<String, PDColorSpace>>();
cachedColorSpaces.set(new HashMap<String,PDColorSpace>());
fileSpecificationKeys = new ThreadLocal<Set<COSKey>>();
fileSpecificationKeys.set(new HashSet<COSKey>());
}
public static PDDocument getDocument() {
return document.get();
}
public static void setDocument(PDDocument document) {
StaticContainers.document.set(document);
}
public static PDFAFlavour getFlavour() {
return flavour.get();
}
public static void setFlavour(PDFAFlavour flavour) {
StaticContainers.flavour.set(flavour);
if (roleMapHelper.get() != null) {
roleMapHelper.get().setFlavour(flavour);
}
}
public static TaggedPDFRoleMapHelper getRoleMapHelper() {
return roleMapHelper.get();
}
public static void setRoleMapHelper(Map<ASAtom, ASAtom> roleMap) {
StaticContainers.roleMapHelper.set(new TaggedPDFRoleMapHelper(roleMap, StaticContainers.flavour.get()));
}
}

Instantiate Java lambda function by name

I would like to create a lambda function in Java 8, get it's classname and then later instantiate the function again from its classname.
This is what I try:
import java.util.function.Consumer;
public class SimpleLambda
{
public static void call(String aLambdaClassName, String aArg) throws Exception
{
Class<Consumer<String>> lClass = (Class<Consumer<String>>) Class.forName(aLambdaClassName);
Consumer<String> newlamba = lClass.newInstance();
newlamba.accept(aArg);
}
public static void main(String[] args) throws Exception
{
{
// Attempt with a static method as lambda
Consumer<String> lambda = Host::action;
String classname = lambda.getClass().getName();
call(classname, "Hello world");
}
{
// Attempt with a locally defined lambda
Consumer<String> lambda = (s) -> { System.out.println(s); };
String classname = lambda.getClass().getName();
call(classname, "Hello world");
}
}
}
class Host {
public static void action(String aMessage) {
System.out.println(aMessage);
}
}
However, with this code (in both variants, using the static method reference and using the locally declared lambda), I get an exception:
Exception in thread "main" java.lang.ClassNotFoundException: mypackage.SimpleLambda$$Lambda$1/471910020
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Class.java:264)
at mypackage.SimpleLambda.main(SimpleLambda.java:12)
I would have expected that at I can at least re-instantiate the static method reference... nope, apparently not.
I have been using a similar approach with Groovy Closures and that worked nicely. So am I just doing something wrong with the Java 8 lambdas, or is it not possible to instantiate lambdas by name? I found some hints on the net that lambdas can be (de)serialized, so I would expect it should also be possible to instantiate them by name.
Well, it is a special property of Oracle’s JRE/OpenJDK to use “anonymous classes”, which can’t be accessed by name at all. But even without this, there is no reason why this ought to work:
Class.forName(String) tries to resolve the class via the caller’s ClassLoader. So even if lambda expressions were implemented using ordinary classes, there were not accessible if loaded via a different ClassLoader
Class.newInstance() only works if there is a public no-arg constructor. You can’t assume that there is a no-arg constructor nor that it is public
The assumption that the entire function’s logic has to reside in a single class is wrong. A counter-example would be java.lang.reflect.Proxy which generates interface implementations delegating to an InvocationHandler. Trying to re-instantiate such a proxy via its class name would fail, because you need the to pass the actual InvocationHandler instance to the proxy’s constructor. In principle, the JRE specific lambda expression implementation could use a similar pattern
Considering the points above, it should be clear that you can’t say that it worked with inner classes in general. There are a lot of constraints you have to fulfill for that.
Regarding Serialization, it works for serializable lambda expressions, because the persistent form is completely detached from the runtime implementation class, as described in this answer. So the name of the generated class is not contained in the serialized form and the deserializing end could have an entirely different runtime implementation.
Store the lambda instances in Map, keyed on the instance name. You can make the map globally available trough a singleton wrapper class (just watch out for synchronization issues).
class LambdaMap {
private HashMap<String, Consumer<String>> theMap;
private LambdaMap() {
theMap = new HashMap<>();
}
private static class INSTANCE_HOLDER {
private static LambdaMap INSTANCE = new LambdaMap();
}
public static LambdaMap getInstance() {
return INSTANCE_HOLDER.INSTANCE;
}
public Consumer<String> put(String key, Consumer<String> value) {
return theMap.put(key, value);
}
public static void Call(String aLambdaClassName, String aArg) {
Consumer<String> func = getInstance().theMap.get(aLambdaClassName);
if (func != null) {
func.accept(aArg);
}
}
}
class Host {
public static void action(String aMessage) {
System.out.println("Goodbye, " + aMessage);
}
}
public class GlobalLambdas {
public static void main(String[] args) {
LambdaMap.getInstance().put("print greeting", s -> {
System.out.println("Hello, " + s);
});
LambdaMap.getInstance().put("print goodbye", Host::action);
LambdaMap.Call("print greeting", "John");
LambdaMap.Call("print goodbye", "John");
}
}
run:
Hello, John
Goodbye, John

How to resolve incorrect parameters from LambdaMetafactory

I'm trying to execute annotated method within main method in App.java with LambdaMetafactory.metafactory():
import com.drfits.annotation.RunMethod;
import com.drfits.transfer.Transfer;
import com.drfits.transfer.TransferExecutor;
import com.drfits.transfer.TransferExecutorImpl;
import com.drfits.transfer.TransferImpl;
import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
import java.util.function.Function;
/**
* Application for execute annotated method
*/
public class App {
public static void main(String[] args) {
Transfer transfer = new TransferImpl("Hello, World!");
Method[] methods = TransferExecutorImpl.class.getMethods();
for (Method method : methods) {
RunMethod annotation = method.getAnnotation(RunMethod.class);
if (annotation != null) {
try {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle methodHandle = lookup.unreflect(method);
MethodType invokedType = MethodType.methodType(Function.class);
MethodType functionMethodType = MethodType.methodType(method.getReturnType(), method.getParameterTypes());
// Lambda which can be executed
TransferExecutor transferExecutor= new TransferExecutorImpl();
Function<Transfer, Void> commonLambda = transferExecutor::execute;
commonLambda.apply(transfer);
// Lambda constructed manually
Function<Transfer, Void> constructedLambda = (Function) LambdaMetafactory.metafactory(
lookup,
"apply",
invokedType,
functionMethodType,
methodHandle,
methodHandle.type()).getTarget().invokeExact();
constructedLambda.apply(transfer);
} catch (Throwable t) {
System.out.println(t.getMessage());
}
}
}
}
}
If I'm trying to execute this code it'll throw exception:
Incorrect number of parameters for instance method invokeVirtual com.drfits.transfer.TransferExecutorImpl.execute:(Transfer)void; 0 captured parameters, 1 functional interface method parameters, 1 implementation parameters
Using the code
TransferExecutor transferExecutor= new TransferExecutorImpl();
Function<Transfer, Void> commonLambda = transferExecutor::execute;
you are binding the Function to a particular instance of TransferExecutor. Your dynamic creation code lacks an instance for the invocation of the instance method TransferExecutorImpl.execute. That’s what the exception tries to tell you.
An instance method needs a target instance to be invoked on, hence your target method has a functional signature of (TransferExecutor,Transfer)→Void.
You can either, create a BiFunction<TransferExecutor,Transfer, Void> out of this method or bind an instance to it like with your transferExecutor::execute method reference. For the latter
change the invoked type to receive an instance of TransferExecutor
MethodType invokedType = MethodType.methodType(
Function.class, TransferExecutorImpl.class);
provide the argument at the invocation:
… .getTarget().invokeExact((TransferExecutorImpl)transferExecutor);
Note that there is still a subtle difference. The statement Function<Transfer, Void> commonLambda = transferExecutor::execute; refers to the interface method while the method you have identified via your annotation is the method declared in TransferExecutorImpl.
Regarding binding captured values, see this and that answer for more explanation and examples.
The error is trying to tell you are not passing another parameters. It expects one, but you are not passing it one.
I suggest you look at the parameters you are passing and compare them to the parameters actually passed when building a lambda.
Write what you are trying to do and a lambda first and see what parameters you should be passing.

What is the reason behind setAccessible method of AccessibleObject class have a boolean parameter?

I am very new in Reflection and I have a doubt like:
public void setAccessible(boolean flag) throws SecurityException
This method has a boolen parameter flag, which indicates the new accessibility of any fields or methods.
For an example if we are try to access a private method of a class from outside the class then we fetch the method using getDeclaredMethod and set the accessibility as true, so it can be invoked, like: method.setAccessible(true);
Now in which scenario we should use method.setAccessible(false); , for an example it can be used when there is a public method and we set the accessibility as false. But what is the need of that? Is my understanding clear?
If there is no use of method.setAccessible(false) then we can change the method signature like:
public void setAccessible() throws SecurityException
Probably you would never do setAccessible(false) in your entire life. This is because setAccessible doesn't the change the visiblity of the a member permanently. When you to something like method.setAccessible(true) you are allowed to make subsequent calls on this method instance even if the method in the original source is private.
For example consider this:
A.java
*******
public class A
{
private void fun(){
....
}
}
B.java
***********
public class B{
public void someMeth(){
Class clz = A.class;
String funMethod = "fun";
Method method = clz.getDeclaredMethod(funMethod);
method.setAccessible(true);
method.invoke(); //You can do this, perfectly legal;
/** but you cannot do this(below), because fun method's visibilty has been
turned on public only for the method instance obtained above **/
new A().fun(); //wrong, compilation error
/**now you may want to re-switch the visibility to of fun() on method
instance to private so you can use the below line**/
method.setAccessible(false);
/** but doing so doesn't make much effect **/
}
}
Scenario: you removed protection from a private field with Field.setAccessible(true), read it and returned the field into original state with Field.setAccessible(false).
//create class PrivateVarTest { private abc =5; and private getA() {sop()}}
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class PrivateVariableAcc {
public static void main(String[] args) throws Exception {
PrivateVarTest myClass = new PrivateVarTest();
Field field1 = myClass.getClass().getDeclaredField("a");
field1.setAccessible(true);
System.out.println("This is access the private field-"
+ field1.get(myClass));
Method mm = myClass.getClass().getDeclaredMethod("getA");
mm.setAccessible(true);
System.out.println("This is calling the private method-"
+ mm.invoke(myClass, null));
}
}

Categories

Resources