Ivy, ant and start scripts - java

I have a project that uses ant to build and ivy for dependencies. I would like to generate the start scripts for my project, with the classpath, based on the dependencies configured in Ivy, especially as the order of dependencies may be important and needs to be preserved from the order in the ivy config.
Has anyone done this before? I also need to generate relative paths in the classpath so I can't use absolute paths as this will only work for the machine on which the build is done.
EDIT: Based on feedback if we cut Ivy out the equation (do the resolve to a directory of my choice) I can then probably resolve the list of libs ok. But how would I generate a classpath suitable for a start script, especially with the relative paths (relative to my bin directory)?
e.g.
install
/bin <-- scripts here
/lib <-- jars here
So in my bin/start.sh I need to have ../lib/ in front of every jar reference rather than a full absolute path.
Thanks.

Since many years (2000?), we had this small script in path ("make_cp")
#!/usr/bin/perl
my $CLASSPATH="";
my $DIR=shift;
$DIR||="lib";
opendir(LIBDIR, $DIR);
while ($file = readdir(LIBDIR)) {
$CLASSPATH.=":$DIR/$file" if ($file =~ /\.jar$|\.zip$/);
}
closedir(LIBDIR);
$CLASSPATH=~ s/^://g;
print "$CLASSPATH";
Used like this:
export CLASSPATH=`make_cp lib`:`make_cp external-lib`

Since Ivy evicts overlapping dependencies and tries to find the best common dependency for all the projects I don't really understand how the order of dependencies would matter at all.
However you should make a standard JAR/WAR/other with Ant for your project and include Ivy dependencies inside that JAR. Basically all you should need to do is to make Ivy's Ant task to resolve the dependencies to a folder, then build tha classes using those dependencies and then consruct the JAR so that you include the library JAR:s to newly created JAR's /lib/ folder.

Like Esko said, you should create a JAR including all required JAR archives:
<zip destfile="abc.jar">
<zipgroupfileset dir="lib/distributed" includes="*.jar"/>
<manifest>
<attribute name="Main-Class" value="com.acme.MyClass"/>
</manifest>
</zip>
After that, your start script is simply:
java -jar abc.jar

If you're using java 1.6 you can use file globs (i.e. java -cp "../lib/*"). If you're using an earlier version of java and you don't want to use Vladimir's solution, you'll need to write a script that figures out what the classpath should be.
So launch.sh looks something like:
cd dirname %0 # change to the bin directory, use %0/.. instead and you can replace ../lib with just /lib
sh set_classpath.sh # set the classpath
java -cp $CLASSPATH some.package.Main
and set_classpath.sh will have some linux magic that sets CLASSPATH equal to something like "../lib/abc.jar:../lib/def.jar"
export CLASSPATH=`ls *.jar | sed 's/[^.jar].jar/..\/lib\/\0:/'`

Related

Ant: target "install" does not exist in the project

I have to build two modules of the project using Ant. I have two xml files which I should use to build: project.xml and integration.xml. I use following command to build them:
ant install -f <filename>.xml
But the building (or installing) process ends successfully only for file project.xml. When I try command mentioned above with the integration.xml I receive:
Target "install" does not exist in the project "integration"
How should I handle this?
Look through your project.xml file and there will be a line like this there:
<target name="install"...>
Which is missing in the integration.xml, so in order to run task with the name "install" you need to add it to your buildfile.
You can look up further info here: https://ant.apache.org/manual/targets.html
Or you can just run
ant -p ${your_buildfile_name} to list all build targets in the build file.
The target that you give to ant (install in your case) refers to a definition in the build file.
In other words: one of your XML files contains an install target, the other does not.
So the solution is that you compare the two files to understand their differences. Either you have to add an install target to the second XML file, or you might simply have to use a different target with that one.

Translate jardesc content into jar-command

I am making some WebFilters for our WebLogic server and I've got everything up and running.
The problem is how I package the filters into jar-files. If I use the following jardesc-file and create the jar-file from Eclipse, everything is working fine and WebLogic has no problems loading the webfilter class. But as soon as I try to manually create the jar-file using just jar.exe I am hitting ClassNotFoundExceptions when loading the webcontainer in WebLogic.
Working jardesc-file:
<?xml version="1.0" encoding="WINDOWS-1252" standalone="no"?>
<jardesc>
<jar path="C:/Workspace/Java/Jars/jars/corsfilter.jar"/>
<options buildIfNeeded="true" compress="true" descriptionLocation="/CorsFilter/corsfilter.jardesc" exportErrors="false" exportWarnings="true" includeDirectoryEntries="false" overwrite="false" saveDescription="true" storeRefactorings="false" useSourceFolders="false"/>
<storedRefactorings deprecationInfo="true" structuralOnly="false"/>
<selectedProjects/>
<manifest generateManifest="true" manifestLocation="" manifestVersion="1.0" reuseManifest="false" saveManifest="false" usesManifest="true">
<sealing sealJar="false">
<packagesToSeal/>
<packagesToUnSeal/>
</sealing>
</manifest>
<selectedElements exportClassFiles="true" exportJavaFiles="false" exportOutputFolder="false">
<file path="/CorsFilter/.project"/>
<file path="/CorsFilter/.classpath"/>
<javaElement handleIdentifier="=CorsFilter/src"/>
</selectedElements>
</jardesc>
Current jar-command:
jar -cvf corsfilter.jar .project .classpath -C .\bin dk\akait\filters\cors\CorsFilter.class
The jar-command creates a jar-file that seems to be equivalent to the one generated using the jardesc-file in Eclipse, except for what looks like some kind of symbolic link to the META-INF-folder in the root of the jar-file.
Content of working jar-file:
Content of non-working jar-file
Can anyone explain what the right command for executing jar.exe is, given the jardesc-file?
Or
Can anyone explain what the META-INF file in the not working jar-file is?
Updated with output of jar-command run using jdk-1.8.0_111
As already mentioned in the comments. I would think this is more like a problem with WinRar as with the actually created jar-file. Probably eclipse doesn't use the jar command internally to create jar-files based on the jardesc descriptions. And the jar-files differ somehow in their internal structure.
However I'd suggest not to rely on the eclipse output, if you want to create a jar-file that you are going to distribute somewhere. I personally like gradle a lot and it is pretty easy to use.
Simply create a build.gradle file in you project root directory with the following content:
apply plugin: 'java'
// this is only needed, if you want to include the single file only
// by default all compiled files will be added to the output jar
jar {
include "dk/akait/filters/cors/CorsFilter.class"
}
task wrapper(type: Wrapper) {
gradleVersion = '3.3'
}
Run
%PATH_TO_YOUR_GRADLE_BINARIES%/gradle wrapper
This will generate a local wrapper bat file that is used to ensure you are using the desired gradle version.
Run
%PROJECT_ROOT%/gradlew.bat build
and locate the jar-file under
%PROJECT_ROOT%/build/libs
You will however need to follow some conventions and place your java sources under a folder called src/main/java for this to work out-of-the-box. Or follow this documentation to setup different source folders.
I don't think your command line is even correct.
As according to jar output
Usage: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point]
[-C dir] files ...
And also http://docs.oracle.com/javase/7/docs/technotes/tools/windows/jar.html
It is expect to be [manifest-file] and [entry-point] after [jar-file] name.
Try
jar -cvf corsfilter.jar -C . .project .classpath -C .\bin dk\akait\filters\cors\CorsFilter.class

How to construct a custom class path in Maven2

I'm executing an ant task inside my maven pom files to code generation purposes. (the code-generation tool is wsdl2java). during the process I fork new java process and pass the class path along with wsdl.
this works fine in the unix environment. but windows gives error saying ;
CreateProcess error=87, The parameter is incorrect.
this is because I'm passing the parameter maven.dependency.classpath as the classpath of the codegen tool. I have figured out that if there is a way to construct my custom classpath, using maven, that would solve my problem. (instead of giving a classpath includes all the jars in the maven repo).
maven-dependency-pluing:build-path goal seems to be promising, but i can specify the exact jars i want to include in my new classpath.
i have to include the class path variable in a root, pom so that other modules can use it.(lots of them)
I could find a way with maven-dependency-plugin:copy goal. but that it is a performance hit.
appreciate any help.
thanks in advance.
I could not find a way to build a classPath using dependency or any other maven plugin. The only option was to create a custom classPath and use it while java execution.
<path id="custom.classpath">
<pathelement location="location of the jar file"/>
</path>
And you refer the constructed classpath later in the java process execution.
<java className = "org.myClass">
<classpath refid="custom.classpath"/>
</java>
Hope this helps.

Nested jar files

a question on building executable jar files. Suppose we have a jar file a.jar that contains UI.class. I have used a metafile in archiving a.jar that says
Main-Class: UI
Class-Path: . b.jar c.jar
Now, if I want to ship this executable a.jar to somebody, I have to ship 3 files - a.jar, b.jar and c.jar. The user has to put all 3 in the same folder, and then double clicking on a.jar would work. It would be simpler if I could ship a single file (a.jar), and the JVM could figure out to extract b.jar and c.jar from in there to use the classes. These 2 are external libraries and I dont want to expand them and re-jar the class files in a.jar.
Can this be done? If so, what is the jar command and what should go in the metafile? Currently the command is
jar cmf metafile a.jar UI.class
Thank you.
Have you looked at this tool OneJar? Also this is a good article to read on OneJar.
If you decide to not use an external tool then your alternative is to put the library/helper jars in the classpath in the manifest, and then copy the jars themselves in a directory path relative to your main jar.
EDIT: OP asked for an example MANIFEST.MF. I coped this from the example One-Jar example jar file.
Manifest-Version: 1.0
Main-Class: com.simontuffs.onejar.Boot
One-Jar-Expand: expand,doc
There is OneJar as people have mentioned but also (especially if you are using eclipse) you can consider fatjar.
ALternatively, you can also achieve this by using an ANT Task here's an example:
<target name="dist" depends="compile,static" description="Compiles and builds jar files">
<mkdir dir="${dist}"/>
<jar destfile="${dist}/MYAPP.jar">
<zipfileset src="${dist}/MY_OTHER_APP.jar"/>
<zipfileset src="${lib}/commons-io-1.4/commons-io-1.4.jar"/>
<zipfileset src="${lib}/commons-math-2.1/commons-math-2.1.jar"/>
<fileset dir="${res}" includes="*"/>
<manifest>
<attribute name="Main-Class" value=<where your main class is>/>
</manifest>
</jar>
</target>
OneJar is good for just bundling a few things together, but it's generally quite slow and inflexible. This isn't a dig at OneJar, it's great for what it does but unless I wanted a quick and easy distributable for a tiny program I wouldn't use it.
I did use it for a while but then switched to izpack. It provides an installer as a single jar which I've found to generally work very well, and supports things such as windows specific shortcuts as well. It's also really easy to integrate it with ant.
Another way can be use one of several available tools to create a setup deliverable and your setup pack will unpack your application when installed.
Maven users can also use the assembly plugin for this:
http://maven.apache.org/plugins/maven-assembly-plugin/usage.html

Classpath including JAR within a JAR

Is it possible to specify a Java classpath that includes a JAR file contained within another JAR file?
If you're trying to create a single jar that contains your application and its required libraries, there are two ways (that I know of) to do that. The first is One-Jar, which uses a special classloader to allow the nesting of jars. The second is UberJar, (or Shade), which explodes the included libraries and puts all the classes in the top-level jar.
I should also mention that UberJar and Shade are plugins for Maven1 and Maven2 respectively. As mentioned below, you can also use the assembly plugin (which in reality is much more powerful, but much harder to properly configure).
You do NOT want to use those "explode JAR contents" solutions. They definitely make it harder to see stuff (since everything is exploded at the same level). Furthermore, there could be naming conflicts (should not happen if people use proper packages, but you cannot always control this).
The feature that you want is one of the top 25 Sun RFEs: RFE 4648386, which Sun, in their infinite wisdom, has designated as being of low priority. We can only hope that Sun wakes up...
In the meanwhile, the best solution that I have come across (which I wish that Sun would copy in the JDK) is to use the custom class loader JarClassLoader.
After some research I have found method that doesn't require maven or any 3rd party extension/program.
You can use "Class-Path" in your manifest file.
For example:
Create manifest file MANIFEST.MF
Manifest-Version: 1.0
Created-By: Bundle
Class-Path: ./custom_lib.jar
Main-Class: YourMainClass
Compile all your classes and run jar cfm Testing.jar MANIFEST.MF *.class custom_lib.jar
c stands for create archive
f indicates that you want to specify file
v is for verbose input
m means that we will pass custom manifest file
Be sure that you included lib in jar package. You should be able to run jar in the normal way.
based on: http://www.ibm.com/developerworks/library/j-5things6/
all other information you need about the class-path do you find here
Use the zipgroupfileset tag (uses same attributes as a fileset tag); it will unzip all files in the directory and add to your new archive file.
More information: http://ant.apache.org/manual/Tasks/zip.html
This is a very useful way to get around the jar-in-a-jar problem -- I know because I have googled this exact StackOverflow question while trying to figure out what to do. If you want to package a jar or a folder of jars into your one built jar with Ant, then forget about all this classpath or third-party plugin stuff, all you gotta do is this (in Ant):
<jar destfile="your.jar" basedir="java/dir">
...
<zipgroupfileset dir="dir/of/jars" />
</jar>
If you are building with ant (I am using ant from eclipse), you can just add the extra jar files
by saying to ant to add them...
Not necessarily the best method if you have a project maintained by multiple people but it works for one person project and is easy.
for example my target that was building the .jar file was:
<jar destfile="${plugin.jar}" basedir="${plugin.build.dir}">
<manifest>
<attribute name="Author" value="ntg"/>
................................
<attribute name="Plugin-Version" value="${version.entry.commit.revision}"/>
</manifest>
</jar>
I just added one line to make it:
<jar ....">
<zipgroupfileset dir="${external-lib-dir}" includes="*.jar"/>
<manifest>
................................
</manifest>
</jar>
where
<property name="external-lib-dir"
value="C:\...\eclipseWorkspace\Filter\external\...\lib" />
was the dir with the external jars.
And that's it...
Not without writing your own class loader. You can add jars to the jar's classpath, but they must be co-located, not contained in the main jar.
You need to build a custom class-loader to do this or a third-party library that supports this. Your best bet is to extract the jar from the runtime and add them to the classpath (or have them already added to the classpath).
I use maven for my java builds which has a plugin called the maven assembly plugin.
It does what your asking, but like some of the other suggestions describe - essentially exploding all the dependent jars and recombining them into a single jar
If you have eclpise IDE, you just need to export your JAR and choose "Package Required libraries into generated JAR". eclipse will automatically add the required dependant JARs into the generated JAR as well as generated some eclipse custom class loader that load these JARs automatically.
I was about to advise to extract all the files at the same level, then to make a jar out of the result, since the package system should keep them neatly separated.
That would be the manual way, I suppose the tools indicated by Steve will do that nicely.
Winstone is pretty good http://blog.jayway.com/2008/11/28/executable-war-with-winstone-maven-plugin/. But not for complex sites. And that's a shame because all it takes is to include the plugin.
Well, there is a very easy way if you're using Eclipse.
Export your project as a "Runnable" Jar file (right-click project folder from within Eclipse, select "Export..."). When you configure the export settings, be sure to select "Extract required libraries into generated Jar." Keep in mind, select "Extract..." and not "Package required libraries...".
Additionally: You must select a run-configuration in your export settings. So, you could always create an empty main( ) in some class and use it for your run configuration.
Anyway, it isn't guaranteed to work 100% of the time - as you will notice a pop-up message telling you to make sure you check the licenses of the Jar files you're including and something about not copying signature files. However, I have been doing this for years and have never encountered a problem.
Extracting into an Uber-dir works for me as we s should all be using root:\java and have outlets code in packages with versioning. Ie ca.tecreations-1.0.0. Signing is okay because the jars are intact from their downloaded location. 3rd party signatures intact, extract to c:\java. There’s my project dir. run from launcher so java -cp c:\java Launcher
In case you are using Spring Boot, you may want to have a look at this documentation: The Executable Jar Format
Java does not provide any standard way to load nested jar files (that
is, jar files that are themselves contained within a jar). This can be
problematic if you need to distribute a self-contained application
that can be run from the command line without unpacking.
To solve this problem, many developers use “shaded” jars. A shaded jar
packages all classes, from all jars, into a single “uber jar”. The
problem with shaded jars is that it becomes hard to see which
libraries are actually in your application. It can also be problematic
if the same filename is used (but with different content) in multiple
jars. Spring Boot takes a different approach and lets you actually
nest jars directly.
The Spring documentation also lists some alternative single Jar solutions:
Apache Maven Shade Plugin
JDotSoft JarClassLoader
One-JAR
Shadow Plugin (Gradle)
I would advise to use one jar and many libraries in separate jars, not in a single jar. Use separate jar from jar libraries.
Suppose you have such a folder structure:
path/yourApp/yourApp.jar
path/yourApp/lib/lib1.jar
path/yourApp/lib/megalib1.jar
path/yourApp/lib/supermegalib1.jar
All you have to do, add in MANIFEST.MF each of used jar.
Manifest-Version: 1.0
Main-Class: com.company.MyProgram
Class-Path: ./lib/lib1.jar ./lib/megalib1.jar ./lib/supermegalib1.jar
From within the manifest, you grant usage to each library.
Single all in one jar file might be easier to share and distribute, but in fact this doesn't give significant advantages over distributing as an archive and unpack it in some folder where you want to deploy. This will not make your program easier to maintain, faster. It will not make significant hdd usage difference.

Categories