custom VM pack yields non-viable installer program? - java

I was experimenting with the recipe for creating a custom VM pack, the motivation was to put our 3rd party libs in some "blessed" JRE distro's /lib/ext folder and zip it up as vm.zip (no compression, no paths) and then package that up with a properties file into another zip.
A VM file created that way shows up in the IA options for building, but the resulting installer.exe launches with a message that it could not find main class. The same project built with one of the vanilla JVM packs from IA works fine, so it must be something in the way I am creating my custom VM.
I also tried the File -> Create VM pack option from the UI, which likewise, gives me an error. Has anyone done this successfully, and if so, what zip tool did you use? (7z here..)
thanks for any insight!

You can use ant to build vm pack.
<?xml version="1.0" encoding="UTF-8"?>
<project name="build_vm" default="default">
<property name="buildTmpDir" value="Path_to_your_jre"/>
<target name="default">
<zip destfile="${buildTmpDir}/vm.zip"
compress="false">
<fileset dir="${buildTmpDir}/jre/"/>
</zip>
<zip destfile="${buildTmpDir}/jre1.8.0.vm">
<fileset dir="${buildTmpDir}/" includes="vm.zip"/>
<fileset dir="${buildTmpDir}/" includes="vm.properties"/>
</zip>
</target>
</project>
Pay attention that ${buildTmpDir}/jre must contain bin and lib folders.
The contents of vm.properties file have to look like this
vm.platform=windows
vm.platform.flavor=win32
vm.name=v1.8.0 Windows i386
vm.exe.path=bin\\java.exe

Related

Custom manifest added multiple times in -post-jar in a common dependency

I have a multi-module native Netbeans Java EE project. In it I have a Java Class Library project that is used by multiple other projects which in turn are packaged into the root .ear project.
I'm adding the "build timestamp" and the "build user" attributes to a custom manifest using the library's build.xml:
<target name="-post-jar">
<jar destfile="${dist.jar}" update="true">
<manifest>
When I "clean and build" the root project, each project that refers the library calls:
<ant antfile="${call.script}" target="jar">
And my -post-jar target is called multiple times. This wouldn't be a problem, but sometimes the second invocation of the <jar> task fails with Unable to rename old file (probably due to Netbeans scanning the files in background, but I can't tell for sure).
There are repeating pairs of Building jar and Updating jar messages in Ant's output. However, if I remove my -post-jar target, the second invocation of the jar target does nothing, because it thinks that the jar is up to date and I see only one Building jar message.
How do I mark the updated jar up to date, so the second invocation of the jar target does nothing?
There's a github repo that demonstrates the problem.
I haven't found a way to not re-generate the manifest every time, but I found a way to make the generated file look the same as the zipped file (and we know that the <jar> task doesn't repack when the contents are the same).
Instead of updating the zipped manifest in -post-jar I now update the source file in -pre-jar. This way the final version of the manifest is zipped and since its contents don't change during build, subsequent <jar> invocations update nothing.
It worth mentioning that before adding the attributes Main-Class, Profile, etc. the build-impl.xml of Netbeans creates an empy manifest template, if the user doesn't provide a valid path in the manifest.file= property. The addition happens after -pre-jar, however the existence of the user-provided manifest is checked much earlier, during the init target and the result is saved to the manifest.available property.
My manifest template is not a static file. It contains the "build timestamp" and the "build user" attributes. Therefore the file doesn't exist during the init target, so I had to add the following line at the beginning of my build.xml:
<property name="manifest.available" value="true"/><!-- It will be available by the time we need it -->
Secondly, manifest.file still has to be set and I set it in project.properties (there's no UI for that setting yet and I wonder how it would behave in the presence of the variable in path)
manifest.file=${build.dir}/manifest.tmp
Next, I overwrite the manifest template in the -pre-jar target:
<tstamp>
<format property="current.time" pattern="HH:mm:ss" locale="de,DE"/>
</tstamp>
<target name="-pre-jar" >
<manifest file="${manifest.file}">
<attribute name="MyApp-built-time" value="${current.time}"/>
<attribute name="MyApp-built-by" value="${user.name}"/>
</manifest>
After that, the new problem became obvious: the timestamp was different for each invocation of
<ant antfile="mylib/build.xml" target="jar">
in the multi-module project and Ant had to repack the jar with the new timestamp in the manifest. I solved this by defining the timestamp property in every project's build.xml. Although the properties are not inherited due to inheritall="false", Netbeans allows for overcoming that:
<property name="transfer.current.time" value="${current.time}"/>
This mechanism is broken in Java EE projects, but the workaround is simple:
<presetdef name="ant">
<!-- workaround transfer.* not working in Java EE projects -->
<ant>
<propertyset>
<propertyref prefix="transfer."/>
<mapper from="transfer.*" to="*" type="glob"/>
</propertyset>
</ant>
</presetdef>

Java Packager Tool : custom install location

I'm working around "Self-Contained Application" generation using Java Packager Tool. By default, the '.exe' bundle is installed under "C:\Program Files (x86)" but I would like install it to a custom location : "C:\MyApp" for example.
To generate my bundle, I'm using an Ant Task inside a Maven build :
<target xmlns:fx="javafx:com.sun.javafx.tools.ant">
<property name="jre.dir" value="${env.JAVA_HOME}/jre" />
<property name="version" value="0.0.3" />
<taskdef resource="com/sun/javafx/tools/ant/antlib.xml"
uri="javafx:com.sun.javafx.tools.ant" classpath="${env.JAVA_HOME}/lib/ant-javafx.jar" />
<echo message="// ----------------------------------- //" />
<echo message="// START JAVAPACKAGER ANT TASK //" />
<echo message="// ----------------------------------- //" />
<fx:deploy nativeBundles="exe" outdir="${basedir}/packager"
outfile="MyApp_${version}">
<fx:application name="MyApp" mainClass="com.myfirm.myapp.bootstrap.BootstrapMain">
<fx:argument>-bundlesDir=./bundles/</fx:argument>
</fx:application>
<fx:resources>
<fx:fileset dir="${project.basedir}/target"
includes="${project.name}-${project.version}-jar-with-dependencies.jar" />
<fx:fileset dir="${project.basedir}" includes="bundles/*.jar" />
</fx:resources>
<fx:info title="MyApp ${version}" vendor="MyFirm">
<fx:icon href="${project.basedir}/myapp.ico" kind="default" width="32" height="32" depth="8" />
</fx:info>
<fx:preferences install="true" shortcut="true" />
<fx:platform basedir="${jre.dir}"/>
</fx:deploy>
</target>
Has anybody work around this ? And could tell me more about how to configure more precisely the generated native bundle ?
Thanks by advance.
EDIT
Under Windows, I have found a way to do it : by editing file com\oracle\tools\packager\windows\template.iss in jar %JAVA_HOME%\lib\ant-javafx.jar. But this solution seems to be ugly and not portable ! So I'm now looking for a way to override it in my ant task...
For extra documentation, what Tib Us did was edit %JAVA_HOME%\lib\ant-javafx.jar. You can use 7-Zip (or others) to open that jar file and update it's contents.
In com\oracle\tools\packager\windows\template.iss, change this line:
DefaultDirName=APPLICATION_INSTALL_ROOT\APPLICATION_NAME
To:
DefaultDirName={pf}\APPLICATION_NAME
{pf} is a Inno Setup constant pointing to 32-bit or 64-bit Program Files folder. See Inno Setup Help.
If you'd like to install in Program Files, then it is helpful to change:
PrivilegesRequired=APPLICATION_INSTALL_PRIVILEGE
To:
PrivilegesRequired=admin
Also, if your program is going to be used by non-admin users and will be writing to its folder in Program Files, then you'll need some special folder permissions. Here is some background on permissions for an app running in Program Files.
You might also like to add this, to ensure that the new install location is used:
UsePreviousAppDir=No
This solution isn't ideal, but is better than nothing.
Getting the template from the jar file is fine—or download it here—but you don't need to edit it where it is.
Once you have that template, you can just use it as a drop-in resource. All the variables that look like SOME_VARIABLE, that is, upper case and which use underscores, will still be replaced by the javapackager.
This solution is much more portable because it doesn't involve editing the JDK; just include your template in package/windows/ as YourAppName.iss.
User Option -BinstalldirChooser=true

Running ant file from its own directory

I have a big ANT script that I use to build-up my environment: total-build.xml.
It calls a bunch of little build.xml files.
However, each of these build.xml files is designed to run in its directory.
For example:
(Project)Build: total-build.xml
(Project)A: build.xml
(Project)B: build.xml
(Project)C: build.xml
total-build.xml looks a bit like:
<ant file="A\build.xml"/>
<ant file="B\build.xml"/>
<ant file="C\build.xml"/>
A\build.xml looks like this:
<copy dir="src" todir="dest"/>
That is, it contains relative paths assumed to be under A. In the example, I expect src == A\src.
I don't want to write absolute paths, as they make things inelegant.
So is there a way to tell ant to run the build.xml file from its own directory.
Suprisingly, I found nothing about this issue using google.
You could use the base dir property for the project tag of each build, like this:
<project name="main" default="help" basedir="..">
...
</project>
and use all relative paths.

create jar with custom manifest with multiple libraries in classpath

I'm trying to create a jar from my eclipse and in order to be able to use the external .jars, I'm using this manifest with multiple .jars in the classpath:
Manifest-Version: 1.0
Sealed: true
Main-Class: src.BatchTester
Class-Path: . P:/Tools/xstream/1.4.2/lib/kxml2-2.3.0.jar P:/Tools/xstream/1.4.2/lib/xstream-1.4.2.jar P:/Tools/StringTemplate/4.0.5/lib/antlr-3.3-complete.jar P:/Tools/StringTemplate/4.0.5/lib/ST-4.0.5.jar P:/Tools/Jdbc/lib/sqljdbc4.jar
Obviously if I don't put the libraries in the classpath the following error appears:
java.lang.NoClassDefFoundError: com/thoughtworks/xstream/XStream
But when I put them in the classpath the error changes to:
java.lang.NoClassDefFoundError: src/BatchTester
So it seemps that it can't found my main class. I've tryed with several possibilities in the classpath, like adding or removing . to the classpath, but can't make it work.
Any idea of how can I solve this???
Thanks for your time and effort,
PS: After creating the .jar the classpath in the manifest inside looks like:
Class-Path: . P:/Tools/xstream/1.4.2/lib/kxml2-2.3.0.jar P:/Tools/xstr
eam/1.4.2/lib/xstream-1.4.2.jar P:/Tools/StringTemplate/4.0.5/lib/ant
lr-3.3-complete.jar P:/Tools/StringTemplate/4.0.5/lib/ST-4.0.5.jar P:
/Tools/Jdbc/lib/sqljdbc4.jar
with new lines and spaces, but even after changing it to the "right" format, I got the same problems.
PS2: I know that with some plugins like Fat-Jar you can make it work, but I don't want to insert more data than needed in my .jar
Finally I've copied all the libs into the /lib folder and add them into the .jar with an ant target since seems to be OK with the IT guys (because it is a small application).
Here is the ant(in case is useful for someone):
<?xml version="1.0" encoding="UTF-8"?>
<project name="BatchTester" default="compile" basedir=".">
<property name="external" value="lib/external-libs.jar"/>
<target name="compile">
<javac srcdir="jav"
source="1.6"
/>
<echo>Creating jar File</echo>
<!--create a new .jar with all the external jars in /lib-->
<jar jarfile="${external}">
<zipgroupfileset dir="lib/">
<include name="**/*.jar"/>
</zipgroupfileset>
</jar>
<!--<sleep seconds="1"/>-->
<!--create .jar file-->
<jar jarfile="BatchTester.jar" index="true" filesetmanifest="mergewithoutmain">
<fileset dir=".">
<include name="**/jav/**/*.class"/>
<exclude name="**/jav/**/*.java"/>
</fileset>
<zipfileset src="${external}">
<exclude name="META-INF/*.SF"/>
</zipfileset>
<manifest>
<attribute name="Main-Class" value="jav.BatchTester"/>
</manifest>
</jar>
<!--delete previously created extern .jar-->
<delete file="${external}"/>
</target>
</project>
Sorry If my questions sounds obvious for you.
*Launch Command *
In order to exclude any doubt, didn't you tried to launch your jar with this kind of command ?
java -jar myJar.jar -cp ./lib
If you use classpath option, you probably didn't ;). Option --classpath (or -cp) and -jar can't be uses together.
Prefer the use of relative path too, like ./lib instead of P:/Tools/... But, anyway, that won't solve your problem.
*Package Location *
As brimborium said, what is you real package ? src sounds very strange. We suspect an error around this.
In your BatchTester class, what have you written for package directive ? Nothing (i.e default package which isnot recommanded ?)?
Does you class begin with (get rid off comments)
public class BatchTester {
In that case, for sure, src should not be mentionned.
Here an example of manifest which work for me.
Manifest-Version: 1.0
Archiver-Version: Plexus Archiver
Created-By: Apache Maven
Built-By: jrRevy
Build-Jdk: 1.6.0_31
Main-Class: com.sopragroup.training.dojo1.MainSwingApp
Class-Path: dojo1-0.5.0-SNAPSHOT-lib/spring-core-3.1.1.RELEASE.jar doj
o1-0.5.0-SNAPSHOT-lib/spring-asm-3.1.1.RELEASE.jar [blablabla]
with the following execution structure
/
|
+ --dojo1-0.5.0-SNAPSHOT.jar
|
+ --dojo1-0.5.0-SNAPSHOT-lib/
|
+ --spring-core-3.1.1.RELEASE.jar
Obviously, I'm using maven for build my app, but the main idea is in.
The manifest doesn't allow absolute paths in the Class-Path: tag. You have two alternatives:
Use relative paths as you already mention in your own answer
Use absolute paths via file protocol. This has been answered elsewhere too and it works absolute versus relative path names in jar manifest
Class-Path: file:///P:/Tools/xstream/1.4.2/lib/kxml2-2.3.0.jar
In addition, you should not edit the manifest.mf manually without being aware of several limitations:
line maximum length must not exceed 72 characters, and after breaking one line you must insert an space in the first column.
There must be a carriage return after the last line otherwise, the file can't be parsed correctly

Filtering out service provider files from META-INF

My application needs to run both from the command-line and a web app. The way I've chosen to implement this is to have the entire application in a single jar file (i.e. my application-specific classes coexist with the classes from the jars my app uses). This makes the command-line use case simple, as the user only has to type java -jar JARNAME. For the web app use case, I simply include the jar in WEB-INF/lib and all is well, almost.
The problem I have is that a few of the jars I'm slurping into the single jar define providers in META-INF/services for the same service, so the single jar ends up with multiple entries in META-INF/services with the same name. (For the curious, these are Jersey jars, and the services are javax.ws.rs.ext.MessageBodyReader and javax.ws.rs.ext.MessageBodyWriter.) So I'm trying to prevent the MessageBody* service files from being slurped into my jar file. Here's how I'm trying (and failing) to accomplish that:
<jar destfile="build/jammies.jar">
<archives>
<zips>
<restrict>
<fileset dir="lib">
<include name="*.jar"/>
<exclude name="servlet-api.jar"/>
</fileset>
<rsel:not>
<rsel:name regex="META-INF/services/javax.ws.rs.ext.*"/>
</rsel:not>
</restrict>
</zips>
</archives>
</jar>
I do have the rsel namespace defined at the top of build.xml.
<project basedir="." default="compile"
xmlns:rsel="antlib:org.apache.tools.ant.types.resources.selectors">
So I don't understand why the restrict task isn't filtering out those particular service provider files.
I think its because the regex starts matching the filename strings from the start however no file begins with META-INF, they would begin with ${basedir}/WEB-INF/lib/META-INF...

Categories

Resources