mgarin / weblaf

WebLaF is a fully open-source Look & Feel and component library written in pure Java for cross-platform desktop Swing applications.
http://weblookandfeel.com
GNU General Public License v3.0
1.15k stars 235 forks source link

classpath problem with IconManager in accessing svg icon resource within a jarfile #644

Closed mokun closed 4 years ago

mokun commented 4 years ago

Q1: How do you gain the access to the svg icon resource when running my app in the form of a jarfile ?

I have no issue in gaining the access to the svg icon currently being resided in the resource folder when I run my app in Eclipse as follows :

IconSet iconSet = new RuntimeIconSet("mars-sim-set");

String s2 = CrewEditor.class.getResource(LANDER_SVG).getPath();
File f2 = new File(s2);

iconSet.addIcon(new SvgIconSource (
    "lander",
    new FileResource(f2),
    new Dimension(16, 16)));

IconManager.addIconSet(iconSet);

ImageIcon landerIcon = new LazyIcon("lander").getIcon();

Note that

s2 is file:/D:/Data/msp%20binary/5348/5348.jar!/svg/icons/lander_hab.svg
f2 is file:\D:\Data\msp%20binary\5348\5348.jar!\svg\icons\lander_hab.svg

The example for loading a svg in the wiki is for fetching an svg online, but loading it from the application itself seems more complicated.

I pretty much using new FileResource(new File(s2)) to point to the location of the resource folder.

Q2: is there a way to reconcile the differences in terms of classpath between initializing the IconManager when running my app in Eclipse and when running it in jarfile ?

Exception in thread "AWT-EventQueue-0" com.alee.managers.icon.IconException: Unable to load icon: lander
        at com.alee.managers.icon.set.AbstractIconSet.findIcon(AbstractIconSet.java:140)
        at com.alee.managers.icon.IconManager.findIcon(IconManager.java:313)
        at com.alee.managers.icon.IconManager.findIcon(IconManager.java:286)
        at com.alee.managers.icon.IconManager.getIcon(IconManager.java:261)
        at com.alee.managers.icon.LazyIcon.getIcon(LazyIcon.java:96)
        at org.mars_sim.msp.ui.swing.MainWindow.initIconManager(MainWindow.java:345)
        at org.mars_sim.msp.ui.swing.configeditor.SimulationConfigEditor.<init>(SimulationConfigEditor.java:140)
        at org.mars_sim.main.MarsProject.lambda$handleNewSimulation$1(MarsProject.java:701)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: com.alee.managers.icon.IconException: Unable to load Icon: lander
        at com.alee.managers.icon.data.AbstractIconSource.loadIcon(AbstractIconSource.java:145)
        at com.alee.managers.icon.set.AbstractIconSet.findIcon(AbstractIconSet.java:125)
        ... 20 more
Caused by: java.lang.RuntimeException: Unable to load SVG from resource: com.alee.api.resource.FileResource@61475401
        at com.alee.extended.svg.SvgIcon.<init>(SvgIcon.java:101)
        at com.alee.extended.svg.SvgIconSource.loadIcon(SvgIconSource.java:147)
        at com.alee.extended.svg.SvgIconSource.loadIcon(SvgIconSource.java:41)
        at com.alee.managers.icon.data.AbstractIconSource.loadIcon(AbstractIconSource.java:133)
        ... 21 more
Caused by: com.alee.api.resource.ResourceException: Unable to open FileResource stream for path: D:\Data\msp binary\5348\file:\D:\Data\msp%20binary\5348\5348.jar!\svg\icons\lander_hab.svg
        at com.alee.api.resource.FileResource.getInputStream(FileResource.java:85)
        at com.alee.extended.svg.SvgIcon.<init>(SvgIcon.java:86)
        ... 24 more
Caused by: java.io.FileNotFoundException: D:\Data\msp binary\5348\file:\D:\Data\msp%20binary\5348\5348.jar!\svg\icons\lander_hab.svg (The filename, directory name, or volume label syntax is incorrect)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:212)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:154)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:109)
        at com.alee.api.resource.FileResource.getInputStream(FileResource.java:81)
        ... 25 more

Q3: why would the FileResource classs repeated the first segment of the file directory, namely, the D:\Data\msp binary\5348\ duplicate part in D:\Data\msp binary\5348\file:\D:\Data\msp%20binary\5348\5348.jar!\svg\icons\lander_hab.svg ?

See below, if I unzip the 5348.jar to look inside this jarfile, I have no problem finding the 'lander_hab.svgresiding in the directory '\svg\icons\ :

image

image

Q4 : Can the FileResource class be streamlined to automatically resolve this classpath issue, without having to mess with InputStream class ?

mokun commented 4 years ago

I'm peeking inside the FileResource class. Looks like when I do this new FileResource(f2), it will go look for the absolute path of the lander_hab.svg.

    /**
     * Constructs new {@link FileResource}.
     *
     * @param file resource {@link File}
     */
    public FileResource ( @NotNull final File file )
    {
        this ( file.getAbsolutePath () );
    }
mokun commented 4 years ago

According to this discussion, the easiest way to reconcile the differences in eclipse and in jarfile is to use InputStream.

mokun commented 4 years ago

Note that I use String s2 = CrewEditor.class.getResource(LANDER_SVG).getPath(); to get the absolute path first.

Then I use File f2 = new File(s2) to get the File instance to be passed into the FileResource(final File file) constructor.

    /**
     * Constructs new {@link FileResource}.
     *
     * @param path resource file path
     */
    public FileResource ( @NotNull final String path )
    {
        this.path = path;
    }

If I skip converting the s2 into f2 and get s2 directly passed into the FileResource(final String path) constructor, again the eclipse has no problem with it.

But even this way, I still cannot acesss the svg file in jarfile.

Also, the duplicated path issue is wrong and the FileNotFoundException is gone.

But I have a new ResourceException` as follows :

Caused by: com.alee.api.resource.ResourceException: Unable to open FileResource stream for path: file:/D:/Data/msp%20binary/5348/5348.jar!/svg/icons/lander_hab.svg

See more below :

Exception in thread "AWT-EventQueue-0" com.alee.managers.icon.IconException: Unable to load icon: lander
        at com.alee.managers.icon.set.AbstractIconSet.findIcon(AbstractIconSet.java:140)
        at com.alee.managers.icon.IconManager.findIcon(IconManager.java:313)
        at com.alee.managers.icon.IconManager.findIcon(IconManager.java:286)
        at com.alee.managers.icon.IconManager.getIcon(IconManager.java:261)
        at com.alee.managers.icon.LazyIcon.getIcon(LazyIcon.java:96)
        at org.mars_sim.msp.ui.swing.MainWindow.initIconManager(MainWindow.java:328)
        at org.mars_sim.msp.ui.swing.configeditor.SimulationConfigEditor.<init>(SimulationConfigEditor.java:140)
        at org.mars_sim.main.MarsProject.lambda$handleNewSimulation$1(MarsProject.java:701)
        at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
        at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:721)
        at java.desktop/java.awt.EventQueue$4.run(EventQueue.java:715)
        at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
        at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
        at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
        at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
        at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
        at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
        at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
Caused by: com.alee.managers.icon.IconException: Unable to load Icon: lander
        at com.alee.managers.icon.data.AbstractIconSource.loadIcon(AbstractIconSource.java:145)
        at com.alee.managers.icon.set.AbstractIconSet.findIcon(AbstractIconSet.java:125)
        ... 20 more
Caused by: java.lang.RuntimeException: Unable to load SVG from resource: com.alee.api.resource.FileResource@6710bc90
        at com.alee.extended.svg.SvgIcon.<init>(SvgIcon.java:101)
        at com.alee.extended.svg.SvgIconSource.loadIcon(SvgIconSource.java:147)
        at com.alee.extended.svg.SvgIconSource.loadIcon(SvgIconSource.java:41)
        at com.alee.managers.icon.data.AbstractIconSource.loadIcon(AbstractIconSource.java:133)
        ... 21 more
Caused by: com.alee.api.resource.ResourceException: Unable to open FileResource stream for path: file:/D:/Data/msp%20binary/5348/5348.jar!/svg/icons/lander_hab.svg
        at com.alee.api.resource.FileResource.getInputStream(FileResource.java:85)
        at com.alee.extended.svg.SvgIcon.<init>(SvgIcon.java:86)
        ... 24 more
Caused by: java.io.FileNotFoundException: file:\D:\Data\msp%20binary\5348\5348.jar!\svg\icons\lander_hab.svg (The filename, directory name, or volume label syntax is incorrect)
        at java.base/java.io.FileInputStream.open0(Native Method)
        at java.base/java.io.FileInputStream.open(FileInputStream.java:212)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:154)
        at java.base/java.io.FileInputStream.<init>(FileInputStream.java:109)
        at com.alee.api.resource.FileResource.getInputStream(FileResource.java:81)
        ... 25 more
mgarin commented 4 years ago

The answer to all three questions is that you're using FileResource incorrectly in this case. FileResource exists exclusively for files available in the file system. Files that are packed into the JAR aren't really present in the file system.

For loading icons from JAR simply use ClassResource instead:

IconSet iconSet = new RuntimeIconSet ( "mars-sim-set" );

iconSet.addIcon ( new SvgIconSource (
        "lander",
        new ClassResource ( CrewEditor.class, LANDER_SVG ),
        new Dimension ( 16, 16 ) 
) );

IconManager.addIconSet ( iconSet );

There is also UrlResource implementation of Resource interface for loading remote icons.


And just a side note - be careful with retrieving icons like you do in the last line:

ImageIcon landerIcon = new LazyIcon ( "lander" ).getIcon ();

Because in the example above you will have SvgIcon and not an ImageIcon. And although you can cast SvgIcon to ImageIcon, there might changes coming in the future or different Icon implementations that won't be extending ImageIcon.

My general recommendation is to use Icon whenever possible and not the exact type (unless you actually need the exact type). Icon should be accepted by most Swing components, so it shouldn't cause any issues.

mgarin commented 4 years ago

Actually, maybe not all three questions, I'm not exactly sure about this one:

Q2: is there a way to reconcile the differences in terms of classpath between initializing the IconManager when running my app in Eclipse and when running it in jarfile ?

It might still work with ClassResource, but you'll need to try it - it mostly depends on how Eclipse handles resources when you're running your application in it.

mokun commented 4 years ago

For loading icons from JAR simply use ClassResource instead:

It's great that after switching to ClassResource, my app works in both Eclipse and jarfile !

Thanks a lot !

Because in the example above you will have SvgIcon and not an ImageIcon. And although you can cast SvgIcon to ImageIcon, there might changes coming in the future or different Icon implementations that won't be extending ImageIcon.

Sure. I'll just create it as an Icon instead.

mgarin commented 4 years ago

Glad that helped! I'll close this issue, but feel free to reopen if you have more related questions.