1

I have a jar file that is giving errors that have led to this question. How do I know if a jar is self contained or if it is loading external resources?

The jar references a license file (*.lic) in the main constructor. Shouldn't this be included in the final jar build?

Here is the main class:

public static void main(String[] args) {
    checkLicense();
}

private static void checkLicense() {
    InputStream licenseStream = null;

    try {
        licenseStream = Main.class.getClassLoader().getResourceAsStream("Vendor.lic");
        if (licenseStream == null) {
            throw new NullPointerException();
        }
    } catch (Exception e) {
        System.err.println("Missing Vendor license resource");
        System.exit(1);
    }

    try {
        License license = new License();
        license.setLicense(licenseStream);
    } catch (Exception e) {
        System.err.println("Invalid Vendor license");
        System.exit(1);
    }

If the Vendor.lic is not there then there is an error, "Missing Vendor license resource".

If the Vendor.lic is there but the classes folder is not there I get another error for "java.lang.ClassNotFoundException".

I see two other .jar files in the same directory.

Can the license be included? Does the jar need other files to work correctly? It seems like it won't work if run independently outside of the folder structure it is in that looks something like this:

/myproject.jar
/classes/generatedsources
/classes/com/mydomain/myproject/Main.class

I create the jar using mvn clean package.

FYI I'd like it to be self contained.

There is a POM.XML and that lists two entries in the dependencies node. There is also a copy dependencies in the plugin\executions node.

Jar Manifest

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 19
Class-Path: vendor-1.0.0-jdk16.jar helper-library-1.0.0.jar
Specification-Title: myproject
Specification-Version: 1.0
Implementation-Title: myproject
Implementation-Version: 1.0.0
Main-Class: com.domain.project.Main

The pom.xml:

Original:

  <properties>
    <maven.compiler.source>11</maven.compiler.source>
    <maven.compiler.target>11</maven.compiler.target>
  </properties>

Adjusted to relieve the problems errors:

  <properties>
    <maven.compiler.source>12</maven.compiler.source>
    <maven.compiler.target>12</maven.compiler.target>
  </properties>

Problems inspector prior to changing value:
enter image description here

More of the pom.xml:

  <groupId>com.myproject</groupId>
  <artifactId>myproject</artifactId>
  <packaging>jar</packaging>
  <version>1.0.0</version>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifest>
              <mainClass>com.mydomain.myproject.Main</mainClass>
              <addClasspath>true</addClasspath>
              <addDefaultImplementationEntries>true</addDefaultImplementationEntries>
              <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>
            </manifest>
          </archive>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <version>2.10</version>
        <executions>
          <execution>
            <id>copy-dependencies</id>
            <phase>package</phase>
            <goals>
              <goal>copy-dependencies</goal>
            </goals>
            <configuration>
              <outputDirectory>${project.build.directory}</outputDirectory>
              <includeScope>compile</includeScope>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
...
  <dependencies>
    <dependency>
      <groupId>com.3rdPartyVendorDomain</groupId>
      <artifactId>3rdPartyVendorDomain-library</artifactId>
      <version>10</version>
      <classifier>jdk16</classifier>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.14.2</version>
    </dependency>
  </dependencies>

The Jar is run from the command line using:

java -jar myproject.jar --parameter1 value --parameter2 value

My Java version:

> java --version
java 12.0.1 2019-04-16
Java(TM) SE Runtime Environment (build 12.0.1+12)
Java HotSpot(TM) 64-Bit Server VM (build 12.0.1+12, mixed mode, sharing)
1.21 gigawatts
  • 16,517
  • 32
  • 123
  • 231
  • 4
    You need to check the source and documentation - even while looking to the jars content you can not say if it is slim or fat jar. You can only guess by the amount of jars included as well or not. – LenglBoy Apr 14 '23 at 21:44
  • @LenglBoy A fat jar is everything bundled into the jar? And a slim jar would be the opposite? – 1.21 gigawatts Apr 14 '23 at 21:46
  • 2
    Thats true. Keep in mind that the build can be configures to build a fat jar BUT changing/renaming some basic folders. That's why very often only relative paths are used in code since you dont need to care about the final results then. – LenglBoy Apr 14 '23 at 21:48
  • So, if I delete the `/target` (output?) directory and then compile, if there is more than the single jar I can assume it would be a slim jar accessing external resources? Is there a way to discover the target output folder or is that always a default directory (like "/target")? – 1.21 gigawatts Apr 14 '23 at 21:49
  • 1
    Since you have a pom maven by default creates fat-jars. So no worries you can always execute it where ever JVM is installed BUT even on docker/.-.... it is always better to only use slim jar and make the server downloading somehow the needed libs. Saves time, space and is more efficient on all performances. – LenglBoy Apr 14 '23 at 22:02
  • Folders inside the jar and folder where your jar is created can be configured at the POM so applied to your build in order to controll the full process – LenglBoy Apr 14 '23 at 22:06
  • When you extract the contents of the jar file, there should be a META-INF folder. Inside that is a manifest file. Can you paste the contents of that file in the question? That should tell you what was included when the jar was built. https://stackoverflow.com/questions/8453543/extracting-jar-file-with-command-line – JustBeingHelpful Apr 14 '23 at 22:42
  • @MacGyver I've added the manifest. The names have been changed changed obviously. – 1.21 gigawatts Apr 14 '23 at 23:33
  • There are optional parameters when calling the command, but this is the bare minimum way to rebuild the jar WITH the manifest file. http://web.eecs.utk.edu/~bvanderz/cs461/notes/jar.html – JustBeingHelpful Apr 14 '23 at 23:45
  • https://stackoverflow.com/questions/304268/getting-a-files-md5-checksum-in-java – JustBeingHelpful Apr 14 '23 at 23:47
  • "A JAR file can have one entry point set in its manifest file. In this case, the JAR file is an executable JAR. The main class has to be included in that JAR file. A nonexecutable JAR is simply a JAR file that doesn't have a Main-Class defined in the manifest file." – JustBeingHelpful Apr 14 '23 at 23:53
  • 1
    So according to the manifest, this is an executable JAR file. – JustBeingHelpful Apr 14 '23 at 23:54
  • If you rebuild the jar using the manifest file and everything it references, does the new jar (from the rebuilding jar) file checksum come out the same or different as the jar file you have now? – JustBeingHelpful Apr 14 '23 at 23:57
  • Another thing to mention. There are MANY versions of the Java Virtual Machine (and corresponding JDK/SE/whatever it was called for each release). Make sure the JVM version on your machine EXACTLY matches the version of the JAR!!!!!!! https://en.wikipedia.org/wiki/Java_version_history – JustBeingHelpful Apr 15 '23 at 00:02
  • @MacGyver I included the Java version I have installed. I don't remember the reasons for why I have the current version but something about compatibility with some software and other issues and I think that no longer matter. Although, there are some odd notes that say maybe to use Java 11 if some specified problems exist. IOW I can upgrade my java if need be. – 1.21 gigawatts Apr 15 '23 at 22:07

1 Answers1

1

When the source code checks for an external file, and throws an exception when the external file is missing, this has no correlation with the executable JAR file during runtime with the JRE. The file should still execute fine, but will clearly throw an exception and error out without the *.lic file. According to the JAR manifest file, this *.lic file was not included as a reference file.

If you cannot get the JAR to run at all, that is likely because you are executing with an incompatible JRE version. The vendor is likely creating their own JDK based on version 16 from the looks of the referenced JAR.

The JDK <major>.<minor>.<patch>+<build> version and the JRE <major>.<minor>.<patch>+<build> from the java --version command are usually different because they were developed by different teams at Sun Microsystems and Oracle. The JDK compiles the source code and creates the JAR (version is not always shown in the JAR). The JRE executes the JAR.

The best you can do is look for (or make) a software compatibility matrix when dealing with multiple programming frameworks. With this code, you have versions for the Maven plugin, JDK (from built JAR), JRE (from built JAR--for evidence), CLASSPATH packages (for built JAR), and JRE (your environment--for execution).

Think twice before changing the source code. Tampering with a license file like this is against the law if it's not open source.

JustBeingHelpful
  • 18,332
  • 38
  • 160
  • 245
  • I don't know if it's related but when I received the project the problems inspector didn't like the compiler source value at value 11. I had to change it to value 12. I'll update the main post – 1.21 gigawatts Apr 15 '23 at 21:43
  • I have not edited the *.lic file but a full license was purchased. Without the *.lic the license errors will appear. – 1.21 gigawatts Apr 15 '23 at 21:47