Issue
I built this project: https://github.com/Charles92011/WebRTCJavaTest
Using the WebRTC library for java from: https://github.com/devopvoid/webrtc-java
When I execute the project in eclipse, it runs fine and I can make connections.
But when I export it as an executable jar file it runs, but as soon as I attempt a connection I get the exception:
java.lang.NoClassDefFoundError: Could not initialize class dev.onvoid.webrtc.PeerConnectionFactory
I'm running on windows 10, the windows native library is being imported into my jar file
Any suggestions?
I'm building it using the Export Jar file feature in Eclipse, and having it package all required libraries into the jar file. Here is the script it creates:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar" name="Create Runnable Jar for Project WebRTCJavaTest">
<!--this file was created by Eclipse Runnable JAR Export Wizard-->
<!--ANT 1.7 is required -->
<!--define folder properties-->
<property name="dir.buildfile" value="."/>
<property name="dir.workspace" value="C:/Users/Charles Johnson/Documents/Workspaces/HelloWorld"/>
<property name="dir.jarfile" value="${dir.buildfile}"/>
<target name="create_run_jar">
<jar destfile="${dir.jarfile}/javartc1.jar" filesetmanifest="mergewithoutmain">
<manifest>
<attribute name="Main-Class" value="main.Main"/>
<attribute name="Class-Path" value="."/>
</manifest>
<fileset dir="C:/Users/Charles Johnson/Documents/GitLab/WebRTCJavaTest/target/classes"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-server/1.17/tyrus-server-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-client/1.17/tyrus-client-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-core/1.17/tyrus-core-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-spi/1.17/tyrus-spi-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.2/jakarta.xml.bind-api-2.3.2.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.1/jakarta.activation-api-1.2.1.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-container-grizzly-client/1.17/tyrus-container-grizzly-client-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-container-grizzly-server/1.17/tyrus-container-grizzly-server-1.17.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-framework/2.3.22/grizzly-framework-2.3.22.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-http-server/2.3.22/grizzly-http-server-2.3.22.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-http/2.3.22/grizzly-http-2.3.22.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/javax/websocket/javax.websocket-api/1.0/javax.websocket-api-1.0.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/dev/onvoid/webrtc/webrtc-java/0.3.0/webrtc-java-0.3.0.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/dev/onvoid/webrtc/webrtc-java/0.3.0/webrtc-java-0.3.0-windows-x86_64.jar"/>
<zipfileset excludes="META-INF/*.SF" src="C:/Users/Charles Johnson/.m2/repository/com/googlecode/json-simple/json-simple/1.1/json-simple-1.1.jar"/>
</jar>
</target>
</project>
Solution
It took me 3 minutes to get your project running after cloning your repository. An MCVE is always helpful. 🙂
Simply add Maven Shade Plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.3.0</version>
<executions>
<execution>
<id>shade</id>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>main.Main</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
Then you will notice:
$ mvn clean package
(...)
$ java -jar target/WebRTCJavaTest-0.0.1-SNAPSHOT.jar
Error: Unable to initialize main class main.Main
Caused by: java.lang.NoClassDefFoundError: javax/websocket/DeploymentException
Fix this by not setting the web socket API's scope to provided
, because you need it in the JAR:
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
<!--<scope>provided</scope>-->
</dependency>
Rebuild, and it works like a charm. I tried on my Windows 10 box, opening two browser tabs in Google Chrome, making a connection to myself. Of course, one browser tab and mirror mode works too. There is no problem with the DLL, it is found and used normally.
I leave the rest up to you, i.e. getting rid of shade warnings due to duplicate manifest and licence files while creating the uber JAR. This can be achieved using <filters>
in the Maven Shade configuration, if you simply want to delete the files you do not want, or by using transformers in order to merge them, where applicable. Otherwise, you can ignore the warnings, the manifest created by the transformer in the plugin configuration wins.
Update: Here is my pull request for your repository. You can simply accept it to fix your project.
Update 2: I was working in IntelliJ with Maven as the leading build tool before. Now, I also opened Eclipse 2022-03 and imported the project from the file system, but did not use your Eclipse project settings. Instead I used the Maven project import wizard, so I got a clean start, knowing that my fixes work when using Maven from command line or from IntelliJ IDEA. So I wanted to do the same in Eclipse.
Then I used "File", "Export", "Runnable JAR file". I selected "Package required libraries into generated JAR" and "Save as ANT script". This is the result (I replaced my user name by yours, so you can more easily compare):
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project default="create_run_jar" name="Create Runnable Jar for Project SO_Java_WebRTCClassNotFound_71798010 with Jar-in-Jar Loader">
<!--this file was created by Eclipse Runnable JAR file Export Wizard-->
<!--ANT 1.7 is required-->
<!--define folder properties-->
<property name="dir.buildfile" value="."/>
<property name="dir.workspace" value="${dir.buildfile}/.."/>
<property name="dir.jarfile" value="${dir.buildfile}"/>
<target name="create_run_jar">
<jar destfile="${dir.jarfile}/MyExecutable.jar">
<manifest>
<attribute name="Main-Class" value="org.eclipse.jdt.internal.jarinjarloader.JarRsrcLoader"/>
<attribute name="Rsrc-Main-Class" value="main.Main"/>
<attribute name="Class-Path" value="."/>
<attribute name="Rsrc-Class-Path" value="./ tyrus-server-1.17.jar tyrus-client-1.17.jar tyrus-core-1.17.jar tyrus-spi-1.17.jar jakarta.xml.bind-api-2.3.2.jar jakarta.activation-api-1.2.1.jar tyrus-container-grizzly-client-1.17.jar tyrus-container-grizzly-server-1.17.jar grizzly-framework-2.3.22.jar grizzly-http-server-2.3.22.jar grizzly-http-2.3.22.jar javax.websocket-api-1.0.jar webrtc-java-0.3.0.jar webrtc-java-0.3.0-windows-x86_64.jar json-simple-1.1.jar"/>
</manifest>
<zipfileset src="jar-in-jar-loader.zip"/>
<fileset dir="${dir.jarfile}/target/classes"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-server/1.17" includes="tyrus-server-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-client/1.17" includes="tyrus-client-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-core/1.17" includes="tyrus-core-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-spi/1.17" includes="tyrus-spi-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/jakarta/xml/bind/jakarta.xml.bind-api/2.3.2" includes="jakarta.xml.bind-api-2.3.2.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/jakarta/activation/jakarta.activation-api/1.2.1" includes="jakarta.activation-api-1.2.1.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-container-grizzly-client/1.17" includes="tyrus-container-grizzly-client-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/tyrus/tyrus-container-grizzly-server/1.17" includes="tyrus-container-grizzly-server-1.17.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-framework/2.3.22" includes="grizzly-framework-2.3.22.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-http-server/2.3.22" includes="grizzly-http-server-2.3.22.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/org/glassfish/grizzly/grizzly-http/2.3.22" includes="grizzly-http-2.3.22.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/javax/websocket/javax.websocket-api/1.0" includes="javax.websocket-api-1.0.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/dev/onvoid/webrtc/webrtc-java/0.3.0" includes="webrtc-java-0.3.0.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/dev/onvoid/webrtc/webrtc-java/0.3.0" includes="webrtc-java-0.3.0-windows-x86_64.jar"/>
<zipfileset dir="C:/Users/Charles Johnson/.m2/repository/com/googlecode/json-simple/json-simple/1.1" includes="json-simple-1.1.jar"/>
</jar>
</target>
</project>
You see, the libraries are exactly the same, I only created an executable JAR, so I also have a manifest with a main class for being able to run the program with java -jar MyExecutable.jar
easily. And in my variant, there is a jar-in-jar class loader. Maven Shade does it differently, unpacking all dependencies into the target JAR, mixing them together rather than packaging JARs into the uber JAR. In contrast, Eclipse's executable JARs are more similar to what you would get when using the Maven One Jar Plugin. Anyway, both ways work. The resulting JAR I exported from Eclipse works perfectly, I can connect to another session or mirror my own camera just fine.
Answered By - kriegaex
Answer Checked By - Katrina (JavaFixing Volunteer)