I have a project in Scala (a kind of test utility) which is currently used only in sbt run way. However for certain demo I want to prepare it in a form which does not require sbt or scala preinstalled (only JVM).
First I've tried to use sbt-assembly plugin but soon get lost fighting with duplicate entries. So now I'm curious whether I can simply compile it to:
single jar-file containing application itself;
and lib directory containing raw set of dependency jars.
I hope that in such case it would be easy to run with the help of Main-Class and Class-Path: ./lib/* fields in the manifest - am I wrong? If this is correct, how can I achieve this?
Update: at last I conquered (it seems so) the sbt-assembly approach, so now the question is not as urgent (though I'm still curious to extend my knowledge of using sbt).
When execute sbt-assembly, all depedencies, App and resources will package into a single jar file.
You can override config properties in runtime by:
java -cp conf/:myAppDemo.jar App.run.mainClass
put your config properties files in conf folder.
Sbt one jar plugin can resolve more dependency conflicts, then assembly plugin.
Also take a look on merge section of assembly plugin, that can help you to fix problems like log4j.xml duplication. If you have problems with two classes with the same classpath having different content, try to exclude some duplicated dependencies (library management)
Related
I have a Java application that uses Maven, so the Maven POM file (pom.xml) lists all of the dependencies needed to compile and run the application.
I'd like to use groovy during my development and testing and then plug in the code into the Java files when I'm done. I haven't done this yet so it might not be feasible but if it is, that is what I'd like to try and do.
I would like to know if there is a function I can call that will read my pom.xml file and add all of the dependencies to my classpath so I can just import the packages that I need and goovy will already have them loaded (or at least know where to load them).
The groovy script might look like this:
addAllPomDependenciesToMyClasspath();
import my.code.pkg1;
import my.more.code.pkg2;
(new ClassInMyCode()).doCoolStuff();
Without this, I need to specify all my jars on the CLASSPATH and it would look something like this (I haven't yet done this because there are too many dependencies).
#!/bin/sh
groovy -cp "../lib1/target/MyJar1.jar:../lib2/target/MyJar2.jar:$HOME/.m2/repository/com/google/guava/guava/30.1.1-jre/guava-30.1.1-jre.jar: KEEP LISTING THESE UNTIL ALL DEPENDENCIES ARE INCLUDED"
Perhaps a better solution would be just to create a gradle file. Let me know if that is the preferred way (if there is no way to autoload all of the maven dependencies).
What have I tried
I've tried searching for solutions and found several ways to add your dependencies to the class path but they don't answer my question.
Some results were:
How to add multiple jars to the classpath of groovyConole/groovysh?
How do I auto load a database jar in Groovy without using the -cp switch?
Dynamically load jar in groovy
Including all the jars in a directory within the Java classpath
I'm guessing there is a "groovy" way to do this that approaches the problem differently and that is why I haven't found an answer to this question.
Misc Notes
(Q) How can I add multiple jar files to my groovy classpath? (A) Use "-cp jar1;jar2" (in Windows use semi-colons to separate the names of the jar files, and in Unix use colons to separate the filenames. See https://stackoverflow.com/a/219801/3281336
I'm curious to know how Intellij can resolve the dependency conflicts? Let me explain my situation. I should work on the spring boot application. It uses Maven. IntelliJ can build and run the application without any problem, but when I make a jar file,
mvn clean package
and run the jar file
java -jar xxx.jar
I faced a java.lang.NoSuchMethodError. Some conflicts on dependencies caused it, and my application uses the wrong version of a jar file.
I want to know how IntelliJ can find the correct jar file which contains the method, while it uses the same pom.xml, which I face error while using with the mvn command.
And is it possible to find that which version of every jar file used by IntelliJ? (I want to use this for correcting the pom file)
Thanks
There is a big difference between runtime and compile time when it comes down to such things.
compiling code (such as mvn package), and writing code (as in, what IntelliJ is doing to ensure that your editing experience is nice; auto-complete dialogs for all the libraries you use, errors if you try to invoke non-existent methods, etcetera) are one thing (let's call it 'write time').
Running your code, as with java -jar xxx.jar is something completely different.
maven and your dependency list is a write time thing. Thus, maven and intellij know where to look for your dependencies, but when you run java -jar xxx.jar, that does not know where to look, and thus, your dependencies aren't found, and thus, NoClassDefFoundError occurs.
That's because that jar file that maven makes just contains your code, it is completely disconnected from your maven file (your maven stuff is not looked up when you run your code at all), and it does not contain your dependencies.
You'd have to ship them separately. There are 3 solutions to this problem:
Preferred solution: jar files contain a so-called manifest, which tells the JVM for example what the name of the main class is within that jar file. It can also contain the class-path (this is in fact the only class-path checked when using java -jar to run jar files; you can't specify a -classpath parameter). This lets you deploy your app such that your app installation looks like:
/usr/local/projects/YourApp/main-app.jar
/usr/local/projects/YourApp/dep/guava.jar
/usr/local/projects/YourApp/dep/mysql-jdbc-connector.jar
/usr/local/projects/YourApp/dep/jdbi.jar
etcetera, and to run this application, just run main-app.jar. This requires the manifest of main-app.jar to contain the entry:
Class-Path: dep/guava.jar dep/mysql-jdbc-connector.jar dep/jdbi.jar
When running the jar, the Class-Path entry is split on spaces, and then each entry is looked up relative to the directory that contains main-app.jar. By shipping the jars separately, it's easy to separately update them or replace them, and deploying a new version is much faster (you just ship the jar(s) that were changed, not all of them. Many apps have hundreds of MBs of deps, whereas their own jar is a few MB at most, makes a big difference for example when pushing deps from your dev machine to the test server!)
This leads to the question: How do you make maven put that Class-Path entry in the output jar file's manifest? The maven-jar-plugin can do the job - see this answer for more details.
Shade in your deps (also called striping, or fatjar, or bigjar)
This is the notion of taking all your deps, rewriting their name to avoid version conflicts, and then making one humongous jar file that contains everything. It has the considerable downside of being a much slower process, especially if you need to push this out to another system for testing. Use the shade plugin to do so.
Don't use java -jar.
java -jar x.jar cannot work unless the jar file either has a Class-Path entry in its manifest, or contains every dep it needs. However, you can also run your java code like so: java -cp main-app.jar:dep/guava.jar:dep/jdbi.jar:/dep/other-deps-here com.foo.YourMainApp. This is.. not convenient, but you could presumably write a shell script or some such. This isn't a very java-like solution, I don't recommend it.
Today at work I came across something interesting. Say i have an old java project that were compiled with an ant build file and we have converted this project into a maven project. So now to build this project, we only need to do a mvn install.
When i do call
mvn install
I get a myproject.jar under the target folder, along with all the dependencies under a lib folder inside the target folder.
To run the executable of this jar I need to do something like :
java -classpath $classpath com.myproject.Mainclass $myArgs
Where $classpath is the path to all of my external libs and where $myArgs is the arguments that is passed to the main function.
I came across this website and I'm really considering to use the spring boot maven plugin to package my executable jar.
Wouldn't it be easier to execute it if all the dependencies are packaged in a single jar file ?
Why would I use the manual configuration vs the Spring Boot Maven Plugin for the executable jar ?
What are the pro and the cons of doing this ?
As the article you had linked covers with pros and cons how to do such single jar file packaging, I'll write out things that you need to consider if you want to use this approach.
Pros:
1. Simplicity of deployment
Users don't have to maintain any dependencies. All they need to do to run the app is get the jar file and execute java -jar file.jar.
2. No easy way to update dependencies by user
If your app uses some external dependencies, you can be sure they are in version that you have chosen. Using "classic" approach user can easily update it by himself to the version that may require some migration steps in your app.
Cons:
1. Size of final package
If your app has large dependencies, every update will require users to download the whole package,
even if dependencies haven't changed.
2. No easy way to update dependencies by user
To change a version of any dependency you will need to update the whole package, where using the old way you could update only the dependency jar.
Summarizing, if your app doesn't have any heavy (in sense of file size) dependencies, I'll personally use single jar file approach. Even if your dependencies changes frequently. It's a lot easier to change a single file, no matter if your app has to be updated or some of its dependency.
I have read numerous posts regarding this, and I was still not able to find a clear-cut answer.
We have the need to use a proprietary SDK in our maven project and this SDK contains ~315 jar files that are needed for around 30 lines of code (SAP product). Every answer I read dealt with adding individual jars to your local maven repo. That is fine and I understand that, but is it possible to add an entire directory of libraries. These libraries are only needed for compiling the project since they are already on the classpath of the target server (They would all be scoped as provided in a pom).
I've tagged Netbeans 8 since that is the IDE I am using, so if anyone knows a hack to get a maven project in netbeans compiled using libraries on Netbeans classpath that would be a good solution as well...
JAR's are just java .class organized in folders and Zipped. Extract all those 315 JARs to somewhere, thus merging all of their content, and then Zip it again to one single fat JAR file. Add this fat JAR to your local repository as you have read elsewhere.
This other question can help you with the JAR merging thing: How to combine two Jar files
Although there are many messy workarounds for this, the ideal would be to let the compilation fail and search for the missing compile jars using a search utility like agent ransack you can search within the jars in that directory for the missing classes referenced in the compiler errors. As you find the jars you need, add them as dependencies with the scope of provided.
A less clean option would be to zip all of the jars, use the dependency plugin to unpack them to a folder and add that folder to the classpath of the build, then remove them or exclude them from the final package.
I have to write a java application which I'm putting together using eclipse and it relies on open source code. This application needs to be self-contained, meaning that I'm supposed to create a jar file that has no external dependencies.
I can use the open source code when I reference the jar files in the project's build path, but the idea is to have the actual source code as part of the eclipse project, side-by-side with my code.
The source code can be found here: http://hc.apache.org/, but when I import an existing file system into my project I can't quite get things to work. The packages end up with the wrong names, breaking references, and I can't do anything. Notice that the folder containing the source code has this structure:
httpcomponents-client-4.2.3\
src\
httpmime\
httpclient-osgi
httpclient-contrib
httpclient-cache
httpclient-benchmark
httpclient
fluent-hc
each of those subfolders has src/main/java/org/apache subfolders.
Can someone please explain how to do this? Am I supposed to import everything one java file at a time?
Use a tool like OneJar, FatJar, JarJar, etc. to create a single-jar application.
As Charlie mentioned, the Maven Shade plugin is another choice, particularly if you're already using Maven. If you're not, consider it or another transitive dependency management tool.
Some tool should be used, IMO, and it's more important the more dependencies you have.
Alternatively you could use a jar class loader and include the jar file in your artifact.
I would most definitely not include the source of dependencies in your own project.