1

I have created a JavaFX application and have a generated Jar file (generated with Gradle) that will launch.

When the application does launch, it doesn't connect to the embedded database though. I feel as though I am missing something very simple but after a lot of research, I haven't been able to figure it out. When running the jar file from the command prompt, I get the following error: java.lang.ClassNotFoundException: org.apache.derby.jdbc.EmbeddedDriver

From the reading I have done, I understand I may be able to add this to my classpath but I have not been successful with this after multiple attempts and I have made this application to be run on another computer. This is possible, right?

If possilbe, I would love to change something in my build.gradle file or surround the .jar in a folder or something like that that would make this simple for the person running the program. Program size is not a concern short of multiple gigabytes.

Olaf Kock
  • 46,930
  • 8
  • 59
  • 90

2 Answers2

1

You've got two problems

  • The driver class is not in your app jar
  • If you embed that db into the app jar, you're not going to be able to write to it

You can make a 'fat jar' but the isn't going to solved the second problem. You really need to make an installer to do this, in order to leave the db in the file system, so it can be written to.

g00se
  • 3,207
  • 2
  • 5
  • 9
  • I'm not trying to embed the database in the .jar file; I know that jar files are not editable. I'm just hoping to put the database in the documents folder or something like that. I don't care where the database is created on startup. An even better solution would to just have a folder with the jar file and the database in it. I have seen this done before but I just don't know how to do this myself – Jacob Bernard Jul 19 '21 at 20:23
  • 1
    >I'm not trying to embed the database in the .jar file< That's good ;) If you're not interested in ease of distribution, just stick the jar file and db in a directory off your home directory. If it's not a fat jar, you will have to specify the classpath (because of the driver) in the startup command to your app – g00se Jul 19 '21 at 20:33
  • How would I go about specifying the classpath? I'm not sure exactly what you mean there. Would this be something I have in my main class or something to do with gradle? – Jacob Bernard Jul 19 '21 at 20:38
  • 1
    How do you normally run the programs you create? – g00se Jul 19 '21 at 23:51
  • I've been running them from the command line since I have Minecraft downloaded and its a little hard to just double click. – Jacob Bernard Jul 20 '21 at 01:26
0

Problem

Part of what Gradle does is dependency management. That means it knows what dependencies you need and how to find them (based on configurations in the build script). When you execute/build your application via Gradle the tool will automatically search repositories, download+cache dependencies, and place those dependencies on the class-path/module-path. Once you deploy your JAR file Gradle is no longer involved, so your deployment is responsible for including the needed dependencies.

In other words, you need to ship your application's dependencies with your application JAR file.


Solutions

You basically just need to make sure you include your application's dependencies with your application. Here are at least three ways to do that.

Copy Dependencies

Copy the dependencies into a build folder as part of the build process. Here's an example of such a task, using the Kotlin DSL:

tasks {
    val jar by existing(Jar::class)

    val copyDependencies by registering(Copy::class) {
        from(configurations.runtimeClasspath)
        into(jar.get().destinationDirectory)
    }

    jar.configure {
        finalizedBy(copyDependencies)
    }
}

Now if you execute ./gradlew jar Gradle will create the JAR file and then copy the dependencies into the same directory as the JAR file. Then you just need to make sure all the JAR files are deployed together.

If I remember correctly, the default class-path is the working directory. But to specify the class-path you would use -cp, -classpath, or --class-path when executing your application. The module-path, if needed, is set with -p or --module-path.

Fat JAR

Create a so-called "fat" or "uber" JAR file. That's a JAR file that includes not just your own application code but all your application's dependencies as well. You could configure the jar task for this, but it would probably be easier for you to simply apply the Gradle Shadow Plugin.

// Kotlin DSL
plugins {
    id("com.github.johnrengelman.shadow") version "<version>"
    // other plugins...
}

And then you'd create the fat JAR with ./gradlew shadowJar. See the user guide for more information.

Self-Contained Application

Create a self-contained executable using a tool like jpackage. This tool gives you an application that has all its code and the JRE embedded, and then gives you an installer or native executable (e.g. exe on Windows). Here's the user guide for jpackage. There are Gradle plugins to make using jpackage from Gradle easier, such as The Badass JLink Plugin.

Note jpackage was added in Java 14 and was incubating until Java 16. Also note that jpackage can't "cross-package". That is to say, if you build your application on Windows then you can only create installers/executables for Windows; same for MacOS and Linux. If you need to package for multiple platforms then you'll need access to each platform.


JavaFX

Since you've tagged this question with JavaFX I want to give a note of caution. Though if you're not using JavaFX 9+ then this is not relevant to you.

Technically JavaFX only supports being loaded as named modules. That means it needs to be placed on the module-path, either via --module-path or by including it in the custom-runtime image built by jlink / jpackage. As of JavaFX 16 a warning is now emitted if JavaFX is loaded from unnamed modules (i.e. the class-path).

Executable JAR files are placed on the class-path. That includes fat JARs. And if you are not using a JDK that includes JavaFX—meaning you have Gradle pull in the JavaFX dependencies—then JavaFX will be included in your fat JAR and be placed on the class-path. Now, despite not being supported and now emitting a warning, nothing seems to currently break if JavaFX is on the class-path. Except for one caveat: Your main class must not be a subclass of javafx.application.Application. You'd have to create a separate main class that simply launches JavaFX.

Because of all this, I would highly recommend using jpackage to deploy JavaFX applications. You may want to read this Q&A as well.

Slaw
  • 37,820
  • 8
  • 53
  • 80
  • Thank you, this is very helpful in many ways. I would definitely like to use the first solution outlined: copying dependencies. I am not sure exactly what to throw into my build.gradle though. I copied the Kotlin part and replaced with org.apache.derby.jdbc.EmbeddedDriver which is the missing class. Since it doesn't build now, I believe I did that wrong. What should I be changing? Thanks again! – Jacob Bernard Jul 20 '21 at 02:51
  • Replacd `` where? I don't have that anywhere in my answer. And note the Kotlin DSL examples I gave go in your `build.gradle.kts` file. If you have a `build.gradle` file then you're using the Groovy DSL and so you'll need to translate the Kotlin to Groovy (side note: Kotlin and Groovy are two different programming languages that can run on the JVM, like Java). I'm unfortunately not that well versed in the Groovy DSL so I'm not sure what it will look like. – Slaw Jul 20 '21 at 04:59
  • I'm not a gradle person, but more than happy to assist you in getting your (what I'm guessing is a reasonably simple) app runnable without 3rd party libraries/tools. I'd need a link to your files – g00se Jul 20 '21 at 10:05
  • @g00se In Gradle, using third-party plugins is often much simpler than trying to configure everything yourself. Especially when using well-established plugins. And note the "Copy Dependencies" option that the OP seems to want to use does not use third-party plugins. Though I'd still recommend `jpackage` (which is not itself a third-party tool, it's part of the JDK). – Slaw Jul 20 '21 at 12:50
  • I see. Actually my last comment would probably be better moved to directly below the question, no? – g00se Jul 20 '21 at 13:07