[SOLVED] Missing JAR in desktop deployment

I don’t usually ask for help on the Forum, but this one is weird, and it has to do with yet another precinct of jMonkeyEngine with which I’m unfamiliar. Maybe someone can help me, or maybe just documenting my issue will lead me to a solution.

I’m building a new release of Maud using the desktop deployment feature in the IDE. Maud is normally built using Gradle, but for deployment purposes I created an Ant-built project (named “AuntMaud”) based on the BasicGameTemplate. This was a non-trivial task because Maud depends on many external JARs and occasionally overrides classes/resources in JARs/libraries (which Ant apparently doesn’t allow). After much trial-and-error, I got AuntMaud running in the IDE (v3.2.2-stable-sdk1 on Windows7/x64).

After configuring desktop deployment and doing a build clean, there are 5 ZIP files in the dist folder. I do an “Extract All…” on the one for my platform (Maud-Windows-x64.zip). But then I’m stuck; I can’t execute it:

  • Double clicking on Maud-Windows-x64\Maud\Maud.exe (or executing it from the command line) results in 2 alerts: “no main class.” and “Failed to launch JVM”, even though the correct main class (maud.Maud) is specified in the project’s run properties.
  • Double clicking on Maud-Windows-x64\Maud\app\Maud.jar results in an “A Java exception has occurred” alert from the Java Virtual Machine Launcher.
  • Running java -jar Maud.jar from the command line in Maud-Windows-x64\Maud\app results in the following:
java.lang.NoClassDefFoundError: com/jme3/bullet/collision/PhysicsCollisionObject
        at maud.model.cgm.Cgm.<init>(Cgm.java:91)
        at maud.model.cgm.LoadedCgm.<init>(LoadedCgm.java:57)
        at maud.model.cgm.EditableCgm.<init>(EditableCgm.java:98)
        at maud.model.EditorModel.<init>(EditorModel.java:117)
        at maud.Maud.<clinit>(Maud.java:109)
Caused by: java.lang.ClassNotFoundException: com.jme3.bullet.collision.PhysicsCollisionObject
        at java.net.URLClassLoader.findClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
        at java.lang.ClassLoader.loadClass(Unknown Source)
        ... 5 more
Exception in thread "main"

The JAR that contains the PhysicsCollisionObject class is named Minie-0.6.5.jar. Sure enough, that JAR is missing from the Maud-Windows-x64\Maud\app\lib folder. All the other libraries seem to be there, but not Minie-0.6.5.jar.

Minie-0.6.5.jar is listed as a compile-time library in the project properties. It’s present in the project’s dist\lib folder, but it was apparently skipped (or deleted) by desktop deployment. And not just for Windows-x64: the Linux and MacOSX ZIP files are also lacking this crucial JAR.

How should I proceed?

Additional: If I copy Minie-0.6.5.jar from dist\lib to Maud-Windows-x64\Maud\app\lib, the exceptions and alerts vanish, and the application runs fine from the JAR or the EXE.

not sure if can help, but will try

not sure what you exactly mean, but you can add external jar in Ant build. so you just build Minie jar and add it via this panel, every external libs were working for me like this. (even ones i build myself) maybe Ant got problem it you load gradle build jar, not ant, idk.

can you try also build Minie as Ant and then include it into AuntMaud?

if you have external project and you build it, just include its dist folder Jar into AuntMaud libs.

main class you can set in Ant settings (IDE/netbeans have panel for it) - maybe you just dont have it setup?
edit: ah you said its setup properly

reading more carefully issues i belive there must be something stupid.

i already had 6 external projects, where 4 of them required other 2 anyway and i were using Ant project before. Everything was working fine for Desktop Linux and Windows distribution as i checked.

I belive it might be some issue related to generate Jar-lib via gradle instead of Ant. (because Minie-0.6.5.jar you create via gradle right?)

Yes, Minie was built using Gradle, but so were most of the other JARs that go into Maud. So I don’t see how building Minie with Gradle could be the root cause of my issue.

For assets, I know there’s some filtering that goes on, to ensure that .dae and .mesh.xml assets aren’t deployed. (I had to defeat that filtering in order to make Maud work.) Perhaps there’s something similar for class JARs?

I’ve found a workaround. I created a copy of Minie-0.6.5.jar with the name A1.jar and added it to the AuntMaud project. Minie-0.6.5.jar still wasn’t included in the ZIP file, but A1.jar was, and it provided the missing classes.

It appears the issue has more to do with the name of the JAR than its content. Still weird and puzzling, but with this workaround I can proceed with the new release.

1 Like

Including A1.jar without Minie-0.6.5.jar doesn’t work. It seems necessary to include the JAR twice. I don’t understand why.

not sure, i were not looking inside JARs of subprojects that have also dependency on other JAR.

thats correct, i always needed include all JARs anyway, but i did not investigate why.(did not know its included twice)

I’d added all my external JAR dependencies (including Minie) via the project’s Libraries properties. With the exception of Minie, they all worked as expected.

By “overrides” I was referring to classes and resources that are defined both in Maud and in libraries or external JARs that Maud uses.

For example, there’s an issue with the com.jme3.scene.plugins.gltf.GlbLoader class in the jme3-plugins-3.2.2-stable.jar in the jme3-plugins library. The fix is a one-line change. For the Gradle build of Maud, I simply created a src/main/java/com/jme3/scene/plugins/gltf folder in my project and added a corrected GlbLoader.java file to it; the corrected class file overrode the one in the JAR, conveniently resolving the issue.

In Ant builds, however, library classes override those local to the project, so I had to remove the library from AuntMaud, build a jme3-plugins-3.2.2-v3.2-SNAPSHOT.jar that includes the fix, and add that to the build. This is inconvenient, but I don’t see any way around it.

Could it be that this “override” is what ant simply does not support as opposed to gradle?

Yes, Ant doesn’t support overrides. I know. That’s part of why creating an Ant build for Maud was difficult: I had to build custom JARs to eliminate all the overrides in the Gradle build.

I was just explaining to oxplay2 what I meant by “overrides”.

What’s interesting is that after eliminating all the overrides, I discovered the (still unexplained) “missing JAR” issue, which I worked around by trial and error.

I don’t think that’s a normal ANT feature but likely just how JME SDK’s ANT builds are setup. It all depends on classpath ordering in the end and maybe JME SDK’s ANT scripts always add the local classes to the end or something? (broken, in my opinion)

But if override means “I have two classes with the same package and name on my classpath and I want to decide which one the classloader sees” then you must either create your own classloader or actually fix your classpath to not have duplicates.
But maybe you mean something else with “override”?

I haven’t looked at the code, but I imagine Gradle extracts the classes from the JARs and resolves any duplicates. However the trick is accomplished, it’s always worked for me so far.

Java does this. No magic.

jar1 class com.foo.Bar
jar2 class com.foo.Bar

…if jar1 is earlier in the class path then the first one is loaded. If jar2 is earlier in the classpath then the second one is loaded.

And the fun part is that on application servers on different operating systems you might get a different order. Fun tracking down problems where com.foo.Bar from one jar is ancient and only loads first on Linux… caused by rampant ‘mavenitus’, aka: “I need this string util method so I’ll just add this dependency to apache-the-whole-world…”

2 Likes

Oh come on, don’t be so hard on those poor guys. Doing a Base64 encode/decode by hand might take like 20 lines of code or something! :wink:

What’s even worse is when there IDE suggests some util method for them that is from some 9 level deep dependency… now we’re stuck with that until we fix it. (Probably to use the util method that was already in Guava.)

…it’s a constant struggle.

1 Like

In that case, what I want is simply to put project-defined classes (and assets) before external JARs in all my classpaths. Gradle does this by default; in fact, I don’t know a way around it.

For reasons I don’t understand, the SDK’s BasicGame project template sets up the classpaths like so:

run.classpath=\
    ${javac.classpath}:\
    ${build.classes.dir}:\
    ${assets.folder.name}

putting external JARs and libraries (javac.classpath) before project-defined classes (build.classes.dir) and assets (assets.folder.name). My Ant build, which was based on the BasicGame template, inherited this disease.

Redefining javac.test.classpath and run.classpath to use the desired order enables the use of standard JME libraries in my Ant build, which will simplify the process of deploying Maud in the future.

Desktop deployment is still skipping over Minie-0.6.5.jar, however.

I wonder if that issue relates to the presence of native libraries in the JAR. There’s some code in desktop-deployment-impl.xml that seems intended to extract native libraries from JARs. As far as I know, JME’s libraries never combine classes and native libraries in the same JAR, but Minie-0.6.5.jar does. What if finding new native libraries in a JAR causes desktop deployment to discard the remainder of the JAR? That might explain the behavior I’m seeing.

No it does not, just because it happens to work on your computer does not mean it works on anyone elses. It’s as pspeed says, it is undefined and varies between machines what order the JVM encounters classes on the classpath. There are only two ways to make sure, either write your own classloader hierarchy or get rid of the duplicates on the classpath.

If the jars are specified in the manifest.mf then they should be loaded in the order in the manifest.

The problem with app servers (like JBoss) in my example is that they just expand the lib directory to make the classpath. Then it just depends on what order the files are in the directory… which is OS dependent.

The classpath references in a jar’s manifest should be loaded in order, though.

@jmaasing,

  1. Have you tried building Maud on your computer? And if so, did it work, or didn’t it?
  2. Do you have any insight into why I have to specify 2 copies of Minie-0.6.5.jar in order to deploy it to the desktop?