-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[java] Parsing failed in ParseLock#doParse() java.io.IOException: Stream closed #4899
Comments
Luckily, this was reproducible with pmd itself, by just running It turns out, that we run into a Java optimization: Opened Jar Files are cached and there is only one instance in the running JVM for each physically existing JarFile. Our ClasspathClassLoader is extending URLClassLoader for loading the bytecode class-files from the auxiliary classpath via Here's a reproducer of the issue (it uses import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLConnection;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Semaphore;
class Scratch {
static Path jarFilePath = Path.of(System.getProperty("user.home"), ".m2/repository/net/sourceforge/pmd/pmd-core/7.0.0/pmd-core-7.0.0.jar");
private static class MyClassLoader extends URLClassLoader {
public MyClassLoader(URL[] urls, ClassLoader parent) {
super(urls, parent);
}
@Override
public void close() throws IOException {
super.close();
System.out.println(Thread.currentThread().getName() + ": Closed MyClassLoader");
}
}
private static class MyThread implements Runnable {
private final int number;
private final CyclicBarrier waitForDone;
private final Semaphore grapResource;
public MyThread(int number, CyclicBarrier waitForDone, Semaphore grapResource) {
this.number = number;
this.waitForDone = waitForDone;
this.grapResource = grapResource;
}
private void log(String message) {
System.out.println("%s: %s".formatted(Thread.currentThread().getName(), message));
}
@Override
public void run() {
try {
URL jarFileUrl = jarFilePath.toUri().toURL();
String pmdVersionResourcePath = "net/sourceforge/pmd/PMDVersion.class";
try (URLClassLoader loader = new MyClassLoader(new URL[]{jarFileUrl}, null)) {
InputStream stream;
// make sure, each threads gets the resource one after another, so that the underlying JarFile
// is definitely cached (if caching is enabled)
grapResource.acquire();
try {
stream = loader.getResourceAsStream(pmdVersionResourcePath);
if (stream == null) {
throw new RuntimeException("resource not found: " + pmdVersionResourcePath);
}
} finally {
grapResource.release();
}
// wait for the first thread to finish and close its URLClassLoader
if (number != 0) {
waitForDone.await();
}
byte[] bytes;
try (InputStream in = stream; ByteArrayOutputStream out = new ByteArrayOutputStream()) {
log("Reading now...");
in.transferTo(out);
bytes = out.toByteArray();
}
if (bytes.length != 2935) {
throw new IllegalStateException("Read error");
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (number == 0) {
// we are the first thread. Signal the other waiting threads so that
// they can continue. They should see now closed streams.
try {
waitForDone.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
}
}
}
}
public static void main(String[] args) throws InterruptedException, IOException {
System.out.println("Scratch");
// Disable caching...
//jarFilePath.toUri().toURL().openConnection().setDefaultUseCaches(false);
int numberOfThreads = 2;
CyclicBarrier waitForDone = new CyclicBarrier(numberOfThreads);
Semaphore grabResource = new Semaphore(1);
List<Thread> threads = new ArrayList<>(numberOfThreads);
for (int i = 0; i < numberOfThreads; i++) {
threads.add(new Thread(new MyThread(i, waitForDone, grabResource), "Thread" + i));
}
threads.forEach(Thread::start);
for (Thread thread : threads) {
thread.join();
}
}
} This gives the following output:
The caching can be disabled globally by calling URLConnection#setDefaultUseCaches(false). I'm working on solution... |
Affects PMD Version:
PMD 7.0.0
Description:
Running maven build in multi-threaded mode
-T
Exception Stacktrace:
Code Sample demonstrating the issue:
With current implementation the
doParse()
can be called only once, socanReenter
should returnfalse
orclassReader
should be shared orInputStream.reset()
should be called.Steps to reproduce:
Use maven multi-module project with lots of modules
mvn clean verify -T 2
jdk21
maven 3.9.6
maven-pmd-plugin 3.21.2
The text was updated successfully, but these errors were encountered: