bytedeco / javacpp-presets

The missing Java distribution of native C++ libraries
Other
2.63k stars 731 forks source link

JAR entry org/bytedeco/cpython/linux-x86_64/lib/pkgconfig/libssl.pc not found #1319

Open CristianPi opened 1 year ago

CristianPi commented 1 year ago

Spring boot 3.0.2 app. build with spring-boot-maven-plugin.

stack

app_1         | java.io.FileNotFoundException: JAR entry org/bytedeco/cpython/linux-x86_64/lib/pkgconfig/libssl.pc not found in /opt/target/dist.jar
app_1         |         at org.springframework.boot.loader.jar.JarURLConnection.throwFileNotFound(JarURLConnection.java:175)
app_1         |         at org.springframework.boot.loader.jar.JarURLConnection.connect(JarURLConnection.java:98)
app_1         |         at org.springframework.boot.loader.jar.JarURLConnection.getJarFile(JarURLConnection.java:106)
app_1         |         at org.bytedeco.javacpp.Loader.extractResource(Loader.java:777)
app_1         |         at org.bytedeco.javacpp.Loader.extractResource(Loader.java:756)
app_1         |         at org.bytedeco.javacpp.Loader.extractResource(Loader.java:803)
app_1         |         at org.bytedeco.javacpp.Loader.cacheResource(Loader.java:684)
app_1         |         at org.bytedeco.javacpp.Loader.cacheResource(Loader.java:477)
app_1         |         at org.bytedeco.javacpp.Loader.cacheResource(Loader.java:444)
app_1         |         at org.bytedeco.javacpp.Loader.cacheResource(Loader.java:432)
app_1         |         at org.bytedeco.cpython.presets.python.cachePackage(python.java:222)
app_1         |         at org.bytedeco.cpython.presets.python.cachePackages(python.java:264)
app_1         |         at com.moveo.analysis.utils.Python.configpkg(Python.java:90)
app_1         |         at com.moveo.analysis.utils.Python.<clinit>(Python.java:25)

I imagine this is a config problem, as it works locally without building the jar.

Any direction?

saudet commented 1 year ago

Sounds like it's just not getting bundled in dist.jar for some reason? Someone's going to need to figure out why Spring Boot doesn't want to bundle that file...

CristianPi commented 1 year ago

@saudet it's strange, because that file exists.. the jar is like 1.5GB /BOOT-INF/lib/

image

So it must be a spring problem?

Maybe related to this spring issue

saudet commented 1 year ago

Sprint Boot does override the default JAR handler as you saw per https://github.com/spring-projects/spring-boot/issues/14011. So I guess this means we can't call JarURLConnection.getJarFile() on nested jar files. Hum, how should we be getting the name of the JAR file? @wilkinsona

saudet commented 1 year ago

Actually, we should be able to get the name from JarURLConnection.getJarFileURL(), but then JavaCPP also needs to list the entries in the file. I don't see an equivalent to JarFile.entries() that doesn't require JarFile... I'm starting to think that this is the same kind of issue as with jlink, GraalVM Native Image, and OSGi that offer no standard way to list resources, see https://github.com/bytedeco/javacpp/issues/506#issuecomment-883796393. If Spring Boot offers some way to list resources though, we can integrate that into JavaCPP. Contributions are welcome!

wilkinsona commented 1 year ago

I'm not sure that I fully understand what the Loader code intends to do. Hopefully the following will show how to find a particular "directory" on the classpath (from within a jar or nested jar) and then list all of its entries:

package com.example.demo;

import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class JarContentsApplication {

    public static void main(String[] args) throws IOException {
        String location = "org/bytedeco/cpython/linux-x86_64/";
        URL classResource = JarContentsApplication.class.getClassLoader().getResource(location);
        URLConnection connection = classResource.openConnection();
        JarFile jarFile = ((JarURLConnection)connection).getJarFile();
        String name = jarFile.getName().substring(jarFile.getName().lastIndexOf("/") + 1);
        System.out.println(name);
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry entry = entries.nextElement();
            if (entry.getName().startsWith(location)) {
                System.out.println("  " + entry.getName());
            }
        }
    }

}

Here's a zip of a complete application containing the above code. ./gradlew bootRun will run the application with cpython-3.10.8-1.5.8-linux-x86_64.jar directly on the classpath as a file on the filesystem. ./gradlew bootJar will produce an executable jar file with cpython-3.10.8-1.5.8-linux-x86_64.jar nested within. You can run the application using java -jar build/libs/jar-contents-0.0.1-SNAPSHOT.jar. You should see the same output for each run, i.e. the code works irrespective of the structure of the classpath.

saudet commented 1 year ago

This should be fixed with pull https://github.com/bytedeco/javacpp/pull/685.

@CristianPi Please give it a try with the snapshots: http://bytedeco.org/builds/

Thanks @huazai023!!