-
Notifications
You must be signed in to change notification settings - Fork 590
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
Integration of JavaCPP fails for Eclipse/OSGi application #506
Comments
The presets currently don't come with anything for OSGi, so users like @reckart simply create their own uber JAR bundle out of whatever they need, and it works fine. If you would like to contribute changes to enhance OSGi support, that would be great! Thanks /cc @timothyjward |
It works for me to:
|
Thanks for describing your setup. But I would like to to skip the creation of a fat jar and just include javacpp and some of the presets form my Eclipse PDE Target-Platform. The Eclipse m2e-pde integration is used to include bundles from Maven-central and to wrap Maven artifacts that are not yet OSGi-bundles. The only things that prevents me from using this is that the javacpp.Loader cannot handle For the first problem I have created a solution based on what I have described initially and pushed it to my javacpp fork: https://github.com/HannesWell/javacpp/tree/bundleresourceURLsupport But while thinking again about my use case I got came to the conclusion that for us it would be better to be able to fully intercept the search for resources of the Loader and to be able to control the resource search of the loader. This would have the additional advantage that javacpp does not have any dependency to Furthermore I consider to use Java's ServiceLoader API instead of providing a static registry. Anyway, if you agree with the second approach I can create a PR and we can discuss the details there. |
That's not a blocker for me, since the mentioned Eclipse m2e-pde integration is capable of wrapping Maven artifacts as OSGi bundles. However it would reduce the configuration effort for any of the presets if the presets would also have OSGi Metadata included. Since this was done for javacpp in PR #332 I think it should be possible for javacpp-presets as well. But I think this is an issue for the presets repo. |
I'm pretty sure @timothyjward got the bit for @HannesWell Why do we need to have dependencies on external APIs to load Java resources? Could you elaborate on this? I thought the JDK had all the necessary interfaces that properly abstracted all that away for any container we may be running on.
Yes, like I said, contributions welcome! @timothyjward was supposed to start working on that, but that didn't happen, so you could pick it up from there. |
@HannesWell I started looking at this a bit more closely. What is the class of the |
From the tests created by @timothyjward I assume he's using the When running Equinox based applications usually the URLs with a schema unknown to the Loader then cause problems in
When calling So I don't see a suitable way how to enable using javacpp in an Eclipse Equinox environment without an optional/provided dependency to I wonder why it is working for @reckart, so it would be great if he can provide more details. Because even when I pack the java-cpp preset jars into a fat bundle (i.e. Eclipse plug-in) I got the same issues as described above. |
I can't provide you with access to the code, but if there is something specific I should look for and report, please ask. |
Note, it works for us when running in Eclipse (Equinox) as well as when running in Karaf (Felix I believe). |
However for me it would be even better if we could fully intercept the resource search in a generic approach, even tough it goes beyond the scope of this issue: I already have created an exemplary commit using a static Using a Suppose one wants to provide an own
More information can be found in the Java-Doc of When you want to provide an own ResourceResolver service in an OSGi application the following steps have to be performed additionally:
When one uses the static version of Apache Aries SPI fly the steps 4 and 5 are maybe not necessary More information about the OSGi Service Loader Mediator Service can be found under the following links:
This actually goes beyond the scope of this issue but for me it would be sufficient. However it it could also be used to resolve this issue in its actual intention: make javacpp work in an Eclipse-Equinox OSGi environment. For example one could provide a corresponding
Javacpp could provide the description above as an article/wiki entry, so users that require it can implement it by themself accordingly or javacpp could create a separate javacpp.eclipse.osgi Maven module that provides the EquinoxBundleResourceResolver, has the corresponding MANIFEST.MF directives and pulls in the |
Thanks for your quick reply @reckart.
That's interesting and I'm puzzled how this works for you under Equinox. If yes, how does the javacpp.Loader handles them when it comes to unpacking the jars and did you configure something special so it can be handled? And if it is not a bundleresource URL, how did you manage to get another URL? I've bundled the main artefacts of javacpp and the presets for cpython, numpy and openblas in one plug-in and the platform specific artefacts in corresponding fragments for the main-plug-in (we support Windows and Linux). Furthermore we use a stripped version of the embedded-python Python class. Have you build your jars in a different manner completely different, that it works for you? |
This is incorrect. Felix also returns URLs with a custom scheme, also called
There are a variety of reasons, but yes, most frameworks support exploded format bundles. You definitely can't rely on the
At a guess you haven't added the Bundle-NativeCode header to your OSGi bundle? This is the thing which tells OSGi (in the absence of JavaCPP) where your native code lives and causes it to be properly loaded as needed. If you add this then do things start working properly? |
It actually works fine for cases where we don't need to extract directories. The API provided by Ok, so this is getting complicated. I don't think coming up with a plugin system of sorts is the right way to go. We have a similar problem in the case of GraalVM Native Image and jlink, and although it doesn't look like there is a way to list resources for the former yet, according to @AlanBateman https://bugs.jython.org/msg12173, it's possible to do it for the later with something like this using
If we can use @HGuillemet Could you update |
This is what I get in
|
I'm not quite sure why I'm receiving notifications from this issue but just to say that the jrt file system provider is for accessing resources in the run-time image. IDEs and some tools use it to access the class files or other resources in the current or remote run-time image. If I understand the issue here then there is a custom URL stream handler somewhere that uses the "bundleresource" scheme. The 4-arg java.net.URL constructor specifies how URL stream handlers are located. For Eclipse/Equinox then I would expect deploying a URLStreamHandlerProvider implementation on the class path would be the simplest. URL uses ServiceLoader to locate custom URLStreamHandlerProvider implementations on the class path. |
Thanks for this clarification.
You're right, I haven't specified the
Thanks. That's the same for me. |
I think that's the key-point of the problem.
Thanks for the hint, but we want to use python and numpy as part of that.
Yes indeed it is probably to complicated. However I would be satisfied with a 'simple' ResourceResolver registry like I have prototyped here: |
@HannesWell in our code (where it works), we do have a |
If this is the goal then realistically JavaCPP will need some OSGi aware code which gets hold of the bundle containing the native code (based on the classloader being a Before going down this route I would like to be sure that setting the |
We can avoid OSGi or framework-specific code in JavaCPP if we use some service loader mechanism: adding a jar providing the service for the framework actually used to the class path or module path would be enough. We can either use a custom service, like @HannesWell suggests, but would something simple that transforms a URL with the unknown scheme to a standard (file: or jar:) not be enough ? For Eclipse, the implementation would just call Or we use an existing standard service: |
This is unlikely to work well. In an OSGi framework there isn’t a classpath or module path to use for this. Each bundle has a classloader and those are wired in a graph to delegate loading for certain packages (as defined by Import-Package in the bundle). ServicLoader will therefore not see anything to load unless carefully managed.
This is approximately what I’m suggesting as “OSGi aware code”, but I would recommend using the OSGi api over the Equinox API so that it works in Felix too.
This would also be some OSGi aware code to provide a Filesystem on top of a bundle. |
@AlanBateman Thanks for pitching it! I'm sorry I didn't make the reason clear as to why I mentioned you here. I'm under the impression that
@HannesWell From what I understand, that approach works only for JAR files, and not for native images from jlink, GraalVM, or what have you, am I right? In the case of OSGi though, if we're fine with supporting JAR files only for now, how about we put those few lines of code (that or what @timothyjward suggested) in JavaCPP but using reflection instead to avoid adding dependencies or expanding the API? It's not like loading libraries is any sort of a bottleneck, and it would also save us (read me) from maintaining an API that's not general enough anyway.
@HGuillemet It looks to me like using |
Ok, I see than Service loading is not really compatible with OSGi and would need some complex intermediary code. Let's set aside the service loading options. Another option is to not use JavaCPP at all for extracting native libs but use the OSGi feature based on
Maybe the What would me missing is a JavaCPP tools to facilitate the process and avoid to have to run some sample app first to populate the JavaCPP cache directory. This approach would also be useful in another context when building an app to be distributed as a jlink standalone image in order to package a pre-extracted directory of native libs instead of poluting the user home directory with copies of the extracted libs. @saudet, what do you think about it ? |
The "jrt" file system provider was created to avoid defining a new API for tools/IDEs that need to locate class files and other resources in a run-time image, e.g. an IDE running on JDK 8 may have opened a project that is configured to use JDK 11 or JDK 17 so code in the IDE needs to access these run-time images to discover the modules/classes. As regards whether its the "preferred mechanism" for accessing resources then probably not. Applications or libraries will continue to use Class::getResourceXXX to locate the resources in their own library/module and ClassLoader::getResourceXXX to scan the class path for resources. I don't think I can help with the issue here. All I can say is that if there are bundleresource:/ URLs in the picture then these URLs can only be opened if there a corresponding URL stream handler. Some OSGi implementations are known to hack into the JDK to register their URL stream handlers, something that probably broke with Java 16 as URL internals are no longer accessible. This is why I mentioned the URLStreamHandlerProvider class as that is the supported way to deploy URL stream handle factories for other protocols, it's much better than setting the system-wide factory. |
@HGuillemet Sure, that's already possible. Try to run
(We can also then put those paths in, respectively, PATH and PYTHONPATH to use them from the command line.)
@AlanBateman Well, JavaCPP is a "tool" that needs to locate files, but those files are not "class files". They are instead other kinds of files, such as native libraries, but also anything else that is required by them, such as source code written in Python for CPython, NumPy, SciPy, etc or even in JavaScript, for that matter, in the case of frameworks like Node.js. Could you please explain how to accomplish that with Class::getResourceXXX and ClassLoader::getResourceXXX? And if it's possible to "scan" everything with these 2 methods, why can't IDEs use them? Why come up with a whole new "jrt" thing when it's not needed? I haven't found a way to use those methods, for example, to scan a subdirectory. How are we meant to accomplish what |
If the "jrt" file system works for you then great, it gives you a file system view of a run-time image so you can walk directory files, list files in directories, and so on. Note the is not limited to the current run-time. As I said, a tool or IDE running on JDK X may need to introspect the classes/resources into another JDK Y on the file system. This is very different to the Class.getXXX which are a standard API for application or library to locate its own resources. Resources just have a name, there is no notion of directories in the resource API. For shared libraries then you'll need to extract to the local file system to be useful, but I assume you know that already. |
As a workaround for classpath walking in cases where the typical classpath scanning libraries do not work, I do this:
So if I understand the discussion correctly, then it should be possible to generate such a file at build time (e.g. using a custom Maven plugin), place it into the Maven artifacts which contain the native libs/resources and make the loader consult this file in order to determine what to extract into the cache instead of having the loader rely on classpath scanning. |
Ok, good, thanks! I just wanted to make sure there was no other way of doing what we need to do here in the case of jlink images.
Yes, JavaCPP does that to make resources accessible to native libraries, which is something that should be part of the JDK, because then everyone could reuse the same code instead of coming up with their own hacks, but I've gone over and over again these things with Panama, and it never leads anywhere, so let's not have a discussion about this. :)
Sure, that's a valid way to work around these usability issues of the JDK, but that information is already in JAR files as well as in the images generated by jlink and GraalVM. Why not use that information? From what I understand of what @AlanBateman says, So we've got 3 options here:
Options 3 implemented with reflection is probably the easiest thing to do to get something working with OSGi for now, so if that sounds OK to you @HannesWell please send a pull request with the updated code and I'll be happy to merge that! |
Setting the
Agree on that.
If javacpp would only unzip the jars we could also install the bundles in exploded format adding However for any approach it has to be considered that the path of a jars cached content within the cache is different when the URL schema is unknown to javacpp, at least at the moment. |
@reckart your suggestion would be possible approach to solve this problem and make javacpp work out of the box for users. But of course it would require more work on the side of javacpp.
Maybe I'm wrong but shouldn't a FileSystem for a container specific scheme be provided by that container/Framework? Otherwise again you would have to implement many different FileSystems for different containers. I would be very happy to send a PR to solve this issue for now. But I'm not exactly sure how an (optional) dependency to a OSGi implementation should be avoided by using reflection? Do you mean the ClassLoader should search for Eclipses FileLocator class by reflection? I expect this to not work, because the javacpp bundle is not wired to the corresponding eclipse package and therefore probably will not find that class. A change like the one I have prototyped here (HannesWell@634b249) would not introduce any new API or any new runtime dependency and could be applied for other OSGi implementations as well, with a moderate amount of required changes. So if you agree with that, I can fine tune it and submit a PR. |
This can work but needs to be done carefully.
This will not work, for exactly the reason you describe, unless the JavaCPP bundle imports the package. If the package is imported JavaCPP can only ever be used in Eclipse Equinox, and not any other OSGi implementation (e.g. Apache Felix, Eclipse Concierge, Knopflerfish...)
I would suggest that there should be an implementation independent call out to handle library loading in OSGi such as:
|
With the "org.bytedeco.javacpp.cachedir.nosubdir" system property set to "true", the cache won't depend on any names related to Java, so it should work with that.
Like @timothyjward says I would expect these classes to be available on the system class loader. I don't see how OSGi could manage anything without essentially extending the JDK somehow.
That creates a dependency on |
That's a promising approach to me, of which all OSGi implementations should benefit from. I will (try to) implement a solution based on that and submit a PR for it.
Thanks for that hint. I will experiment with it in the context of OSGi.
I have never tested that, but I will check it. However I suspect an optional dependency to the abstract OSGi framework still necessary in order to compile the code. But imho an optional or provided dependency isn't a problem. |
Apparently we get "file" and "jar" URLs with Felix, so we may not have to worry about that one anyway: |
I would check this, as I don't believe it will apply to URLs from
Looking at the linked code, and specifically this line: |
Could you check it? I'm sure there's people happily using Jetty in Felix, so they would have hit issues there I'm sure.
Seems to be a public method, so I don't think we need to call |
Apache Felix 6.0.4 gives:
Ok, but that's an Equinox-specific class, so there would still need to be a solution for Felix and other OSGi frameworks. |
FYI: the defunct Eclipse Gemini project has an implementation of classpath scanning for OSGI which works with Equinox and Felix (Karaf): https://search.maven.org/search?q=g:org.eclipse.gemini.blueprint - the implementation is exposed via a Spring ResourcePatternResolver API. Works fine for JAR bundles. For using it in the real-world, I had to make some changes to it through because it didn't properly scan Eclipse workspace projects. |
What happens with Class.getResource()? |
|
This would be the quickest solution, but it would also be the most dirty one, IMO. Even tough you are probably right that it wont cause errors when strong encapsulation is enforced because the method is public. Nevertheless I would prefer to avoid using reflection and instead use a publicly available API instead, ideally one that is available in all OSGi implementations. So my favorite at the moment is @timothyjward suggestion and having a provided dependency to OGSi that can handle the absence of OSGi flawlessly.
That sounds interesting, do you mean it should be used as dependency or as an inspiration how the problem could be solved for javacpp? Unfortunately I will likely won't have to time to complete the implementation before my vacation next week (for about 1-2 weeks), but I will come back to this afterwards. |
It might provide some inspirations. In particular, as I said, the case of working with proper JAR bundles and working with Eclipse workspace bundle projects (or other "alternative" bundle resolving/wiring mechanisms) may need to be considered by whatever implementation there may be. |
I have created a PR to implement the suggested solution. |
I'm trying to integrate bytedeco/cpython into an Eclipse based application (which uses the Equinox OSGi runtime, like all other Eclipse applications too). Even tough I tried multiple different approaches to integrate cpython and javacpp, but non of them was successful:
All suffered the same problem: the
javacpp.Loader
cannot handle URL's using thebundleresource
schema (likebundleresource://491.fwk108568334:2/org/bytedeco/javacpp/windows-x86_64/api-ms-win-crt-locale-l1-1-0.dll
) properly.These URLs are returned by the EquinoxClassLoader used by Eclipse when the
javacpp.Loader
quires resources in itsfindResources()
methods.With those URLs the caching of packages fails and the libraries cannot be loaded.
Therefore my question is: Is it possible to use javacpp and its preset in an Eclipse/OSGi application? And if yes are there some best-practices how to integrate them?
I noticed you have a
osgi
branch where some contributions were made some time ago, but it seems to be stale and I could not find information how to use the current main artefacts in Eclipse/OSGi.If it is not possible yet, one solution to solve the problem mentioned above could be to include org.eclipse.platform:org.eclipse.osgi as optional dependency and use the
org.eclipse.osgi.storage.url.BundleURLConverter
to resolvebundleresource
URLs if that class is available at runtime (if it cannot be loaded it is likely that it is not required).If the suggested change is suitable and wanted, I can create a PR for it.
I'm not sure this is the onyl required step to enable the use of javacpp in Eclipse but, it seems like it is a necessary first one.
The text was updated successfully, but these errors were encountered: