Modify

Opened 12 months ago

Closed 12 months ago

Last modified 12 months ago

#7703 closed defect (fixed)

How to get resources from inside plugin?

Reported by: kendzi Owned by: bastiK
Priority: critical Component: Core
Version: Keywords:
Cc:

Description (last modified by kendzi)

In plugin loading class I try to get resource file from my plugin jar (kendzi3d.jar) using call:

getClass().getResource(“/resources/pluginProperties.properties”);

but in response I get:

jar:file:/C:/Documents%20and%20Settings/kendzi/Dane%20aplikacji/JOSM/plugins/ImportImagePlugin.jar!/resources/pluginProperties.properties

So url is pointing to file inside different plugin! ImportImagePlugin plugin have file with same name.

call to getClass()
result:
class kendzi.josm.kendzi3d.Kendzi3DPlugin (inside kendzi3d.jar)

It seems that it is bug with plugin class loader.

Or there is any other way to get files from my plugin jar?

Attachments (0)

Change History (7)

comment:1 Changed 12 months ago by kendzi

  • Description modified (diff)

comment:2 Changed 12 months ago by bastiK

  • Owner changed from team to bastiK
  • Status changed from new to assigned

comment:3 Changed 12 months ago by bastiK

Currently we have a single class loader for all plugins. I can change it such that each plugin has its own class loader that only sees the plugin jar, the main josm jar and the jar files of the required plugins.

However, there seem to be some side effects of having multiple class loaders:

  • instances of the same class that are instantiated by different class loaders are type-incompatible, i.e. you cannot cast one to the other
  • static initialization code may be executed multiple times
  • there are multiple instances of static fields, one for each class loader

These problems only apply to plugin dependencies, e.g. opendata requires jts.

Given these issues, I suggest, we keep the current broken behaviour.

comment:4 in reply to: ↑ description Changed 12 months ago by bastiK

Replying to kendzi:

Or there is any other way to get files from my plugin jar?

As a workaround, we could provide a plugin class loader, that is only used to get resources, but not classes.

comment:5 follow-up: Changed 12 months ago by bastiK

  • Resolution set to fixed
  • Status changed from assigned to closed

In 5241/josm:

fixed #7703 - How to get resources from inside plugin?

comment:6 in reply to: ↑ 5 ; follow-up: Changed 12 months ago by kendzi

Replying to bastiK:

In 5241/josm:

fixed #7703 - How to get resources from inside plugin?

I well test solution but as I understand it solve this one problem but the rest remains.

And problems are:

  • what if two plugins use the same class in the same package?
  • what if two plugins try loads externals jar file with different and not compatible version?
  • how to load external jar from plugin? (eg jog4j) I’m using reflection on class loader [1], but it should be some “official” way to do it.
  • as I saw some plugins extract class from externals jar into plugins jar, this may cause problems if two plugins are using the same jars
  • it should be possible to share some code between plugins, but really should plugins see each other? Maybe plugins should declared some public api to internals?
  • it should be possible to share resources between plugins eg icons. But it would be nice if in first place they were take from orginal jar.
  • provided solution won’t work with external libraries which usually using call to getResource()

Did I say that I hate class loaders?

Maybe some solution to problems can be solve by using OSGi for plugins?

[1] https://github.com/kendzi/kendzi3d/blob/master/kendzi.josm.plugin3d/src/kendzi/josm/kendzi3d/NativeLibPlugin.java#L206

comment:7 in reply to: ↑ 6 Changed 12 months ago by bastiK

Replying to kendzi:

Replying to bastiK:

In 5241/josm:

fixed #7703 - How to get resources from inside plugin?

I well test solution but as I understand it solve this one problem but the rest remains.

And problems are:

  • what if two plugins use the same class in the same package?

Java conventions dictate, that you use a unique package name for each project.

  • what if two plugins try loads externals jar file with different and not compatible version?

Right, that wouldn't work, but let's wait until this actually happens. There are still workarounds given the current setup.

  • how to load external jar from plugin? (eg jog4j) I’m using reflection on class loader [1], but it should be some “official” way to do it.

I think a good solution would be to list the required libs in the Manifest, e.g.

Class-Path: resource://lib/mylib.jar resource://lib/otherlib.jar

The "resource:" prefix would indicate that the file needs to be copied from the plugin jar to the plugin folder before loading the plugin. (Or write a custom class loader, that gets the class files from the inner jars on the fly. this site has interesting info on class loaders, especially in the comments.)

It gets more complicated, when there is some code that gets to decide which libraries to load (e.g. depending on operating system and architecture).

In case you haven't seen it already: the ImportImagePlugin also loads external libs, but uses a different hack, if I remember correctly.

  • as I saw some plugins extract class from externals jar into plugins jar, this may cause problems if two plugins are using the same jars
  • it should be possible to share some code between plugins, but really should plugins see each other? Maybe plugins should declared some public api to internals?

Ideally, each plugin would see the classes of the dependent plugins and JOSM core. However, the obvious solution doesn't work as written above. What do you suggest?

  • it should be possible to share resources between plugins eg icons. But it would be nice if in first place they were take from orginal jar.

I can provide a similar fix for icon loading. It would look something like this: new ImageProvider(name).setClassLoader(plugin.getResourceClassLoader()).get();

  • provided solution won’t work with external libraries which usually using call to getResource()

True, unless you use a designated class loader.

Did I say that I hate class loaders?

I'm not too enthusiastic about it either. Also, the plugin code is serious foobar, so it feels like opening a can of worms. Anyway, I wouldn't mind if you provided some patches for core. :)

Maybe some solution to problems can be solve by using OSGi for plugins?

[1] https://github.com/kendzi/kendzi3d/blob/master/kendzi.josm.plugin3d/src/kendzi/josm/kendzi3d/NativeLibPlugin.java#L206

Here is a temporary "solution" that avoids hacking the JVM: Your plugin consists of a single bootstrap class and the real plugin is a jar file wrapped inside the plugin jar. Your single class would copy the real plugin jar and some libraries to the plugin folder and execute everything using a URLClassLoader. This would work similar to the plugin loading code in JOSM core.

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed .
as The resolution will be set. Next status will be 'closed'.
The resolution will be deleted. Next status will be 'reopened'.
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.