NoClassDefFoundError when running my plugin from FIJI

Hi all,

I’m a beginner with Java and Maven so my problem is likely due to me misunderstanding something really fundamental but here it goes.

I’m developing a FIJI plugin using Eclipse. All of the classes run fine inside Eclipse: FIJI starts up and the individual command plugins do their job.

When I package using Maven (either from the Eclipse m2 plugin or from the command line) I get no errors. However, when I place my jar into the plugins folder inside FIJI and then run my program, I get a NoClassDefFoundError for org/apache/commons/csv/CSVPrinter

I directly use CSVPrinter in one my classes named ExportDataToCsv, and I use the ExportDataToCsv class in my plugin named Randomizer. Here is a screen shot of the error:

Apparently this happens when the class is there during compilation but not run time.

I do have the apache/commons/csv/CSVPrinter in my pom file:

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.6</version>
</dependency>

Using System.getProperty(“java.class.path”), I was able to confirm that the commons-csv-1.6.jar is in my class path.

Apparently this is a common problem, so I scoured stack overflow and all the answers I saw indicated that I should use maven plugins to package all my dependencies together:

Here is what that portion of my pom file looks like:

<build>
        <plugins>
            <plugin>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>SIAL.Randomizer</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

After I run “mvn clean package”, my target directory looks like this:Screenshot 2020-01-30 15.22.25.

The SIAL-1.0.0-SNAPSHOT.jar file is what I’m installing as a plugin into FIJI. The /lib directory contains all of the dependencies I use, including the commons-csv-1.6.jar

For reference, this is what my entire pom.xml looks like:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<parent>
		<groupId>org.scijava</groupId>
		<artifactId>pom-scijava</artifactId>
		<version>26.0.0</version>
		<relativePath />
	</parent>

	<groupId>com.usc.mackaylab</groupId>
	<artifactId>SIAL</artifactId>
	<version>1.0.0-SNAPSHOT</version>

   <name>Speeds up human assisted image analysis</name>
	<description>Simple Image Analysis Library</description>
	<url>https://github.com/d-tear/SIAL</url>
	<inceptionYear>2019</inceptionYear>
	<organization>
		<name>[MY-ORGANIZATION-NAME]</name>
		<url>[MY-ORGANIZATION-WEB-SITE]</url>
	</organization>
	<licenses>
		<license>
			<name>CC0 1.0 Universal License</name>
			<url>https://creativecommons.org/publicdomain/zero/1.0/</url>
			<distribution>repo</distribution>
		</license>
	</licenses>

	<developers>
		<developer>
			<id>d-tear</id>
			<name>David Tyrpak</name>
			<url>https://imagej.net/User:[MY-IMAGEJ-WIKI-ACCOUNT]</url>
		</developer>
	</developers>
	<contributors>
		<contributor>
			<name>None</name>
		</contributor>
	</contributors>

	<mailingLists>
		<mailingList>
			<name>Image.sc Forum</name>
			<archive>https://forum.image.sc/tags/imagej</archive>
		</mailingList>
	</mailingLists>

	<scm>
		<connection>scm:git:git://github.com/[MY-ORG]/[MY-REPO]</connection>
		<developerConnection>scm:git:git@github.com:[MY-ORG]/[MY-REPO]</developerConnection>
		<tag>HEAD</tag>
		<url>https://github.com/[MY-ORG]/[MY-REPO]</url>
	</scm>
	<issueManagement>
		<system>GitHub Issues</system>
		<url>http://github.com/[MY-ORG]/[MY-REPO]/issues</url>
	</issueManagement>
	<ciManagement>
		<system>None</system>
	</ciManagement>

	<properties>
		<main-class>com.usc.mackaylab.SIAL.Randomizer</main-class>
		<license.licenseName>cc0</license.licenseName>
		<license.copyrightOwners>N/A</license.copyrightOwners>
		<license.projectName>ImageJ software for multidimensional image processing and analysis.</license.projectName>
	</properties>

	<repositories>
		<repository>
			<id>scijava.public</id>
			<url>https://maven.scijava.org/content/groups/public</url>
		</repository>
	</repositories>

	<dependencies>
		<dependency>
			<groupId>net.imagej</groupId>
			<artifactId>imagej</artifactId>
		</dependency>
		
		<dependency>
    <groupId>sc.fiji</groupId>
    <artifactId>fiji</artifactId>
</dependency>
		
		
		<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-csv</artifactId>
<version>1.6</version>
</dependency>

<dependency>
    <groupId>junit</groupId>
    <artifactId>junit-dep</artifactId>
    <version>4.8.2</version>
</dependency>

<dependency>
			<groupId>io.scif</groupId>
			<artifactId>scifio-bf-compat</artifactId>
		</dependency>
		

	</dependencies>

<build>
        <plugins>
            <plugin>
                <!-- Build an executable JAR -->
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>3.1.0</version>
                <configuration>
                    <archive>
                        <manifest>
                            <addClasspath>true</addClasspath>
                            <classpathPrefix>lib/</classpathPrefix>
                            <mainClass>SIAL.Randomizer</mainClass>
                        </manifest>
                    </archive>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <executions>
                    <execution>
                        <id>copy-dependencies</id>
                        <phase>prepare-package</phase>
                        <goals>
                            <goal>copy-dependencies</goal>
                        </goals>
                        <configuration>
                            <outputDirectory>${project.build.directory}/lib</outputDirectory>
                            <overWriteReleases>false</overWriteReleases>
                            <overWriteSnapshots>false</overWriteSnapshots>
                            <overWriteIfNewer>true</overWriteIfNewer>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>


</project>

And this is my project setup inside Eclipse:
Screenshot 2020-01-30 15.28.07

I’ve spent two days trying to fix this NoClassDefFoundError, but to no avail. I can’t help but think it’s something really fundamental with how my pom is setup, or how my eclipse project is setup.

Best,

David

1 Like

As an update, I’m currently looking at this similar thread: Getting ClassNotFound Error

I’ll post an update on if I can get it to work.

Based on that thread, I tried: mvn -Dimagej.app.directory=/path/to/your/Fiji.app

That worked!

But I’m still stuck on how to distribute my plugin to other people. I tried using the maven shade plugin to produce one uber jar, but the jar was so enormous (~250MB) that FIJI wouldn’t open after I placed it into the plugins folder.

I can’t imagine that other plugins are that large. How are people distributing their plugins with all of their dependencies?

1 Like

Hi @d-tear

Welcome to the forum, and nice job figuring that out.

Can’t write in detail now so short answers - you don’t need to (and should not) distribute the “uber” jar, for reason I can explain in another post if they’re of interest.

This can get dirty as you can see below so I’d recommend you consider using an update site to distribute your plugin.

John

Getting dirty

For fast-and-dirty distribution, you just need to give people.

  1. The jar containing your code only.
  2. Jars of dependencies that:
    a) Don’t already come with Fiji.
    b) Come with Fiji, but are of different versions than you require (and will need replacing).

The output of
mvn -Dimagej.app.directory=/path/to/your/Fiji.app
should tell you what it changed regarding 2a and 2b.

1 Like

Hi @bogovicj

Thank you so much for your post! Really helpful. It looks like I need to get an update site.

Personally I would like to learn more about why using the uber jar is a bad idea, so I’d love to see a separate post detailing this issue. I think it would probably be edifying for the development community, especially beginners like me, to learn more about the do’s and don’ts of imagej plugins

@bogovicj might elaborate more on this himself, but to make sure we’re all at the same page, please read this wiki page on the pros and cons of fat JARs:

2 Likes

Thanks for linking to that page @imagejan,

Sad uber jars

@d-tear The specific thing I’m recommending against is to make an uber-jar and put the result in the Fiji folder.
Tldr; it’s likely to give you a headache.

This is because, if you’re making a plugin for Fiji, it’s likely that your dependencies overlap with Fiji’s. Meaning that Fiji has a version of a dependency in it’s own jar, and another in the uber jar that may be a different version. If you have multiple versions of the same thing on the classpath, Java could decide to load the wrong one, and you could see errors. It’ll could be difficult to debug too since the error will be a NoClassDefFoundError, or NoSuchMethodError even though you have the right version somewhere.

Happy uber jars

I do use uberjars sometimes when I write command line scripts, and don’t want to depend on my user having a particular version of Fiji installed.

Would love to hear others’ thoughts too,
John

Some reading:


2 Likes

Hi @imagejan,

Thanks for posting that link! Very informative.

1 Like

Thanks @bogovicj! Along with that Uber-jar wiki, your post has really helped me understand more about properly sharing my code.

2 Likes