|
5.11. Bundling a Java Program: Put it in a JARDistributing a Java application can be a pain. All but the simplest of applications will have many public classesand since there can only be one public Java class per source file, each Java source file becomes a class file, and the elements of a package name become directory nodes in the path to the class, you end up with a fairly complex collection of directories and files. Wouldn't it be nice to be able to roll the whole mess up into a single binary file for distribution? Well, you can. The tool to do the job is called jar, which stands for Java ARchive.[26] The files produced by this utility are called JAR files. The JAR format is the common DOS/Windows ZIP file format, with a few special files to support some special features we will explain as they come up.
Example 5.19. javap output for FetchURL.classCompiled from FetchURL.java public class FetchURL extends java.lang.Object { private java.net.URL requestedURL; public FetchURL(java.lang.String); public java.lang.String toString(); public static void main(java.lang.String[]); } Method FetchURL(java.lang.String) 0 aload_0 1 invokespecial #1 <Method java.lang.Object()> 4 aload_0 5 new #2 <Class java.net.URL> 8 dup 9 aload_1 10 invokespecial #3 <Method java.net.URL(java.lang.String)> 13 putfield #4 <Field java.net.URL requestedURL> 16 goto 27 19 astore_2 20 aload_2 21 invokevirtual #6 <Method null> 24 goto 27 27 return Exception table: from to target type 4 16 19 <Class java.lang.Exception> Method java.lang.String toString() 0 ldc #7 <String ""> 2 astore_1 3 new #8 <Class java.io.BufferedReader> 6 dup 7 new #9 <Class java.io.InputStreamReader> 10 dup 11 aload_0 12 getfield #4 <Field java.net.URL requestedURL> 15 invokevirtual #10 <Method java.net.URLConnection openConnection()> 18 invokevirtual #11 <Method java.io.InputStream getInputStream()> 21 invokespecial #12 <Method java.io.InputStreamReader(java.io.InputStream)> 24 invokespecial #13 <Method java.io.BufferedReader(java.io.Reader)> 27 astore_3 28 goto 55 31 new #14 <Class java.lang.StringBuffer> 34 dup 35 invokespecial #15 <Method java.lang.StringBuffer()> 38 aload_1 39 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)> 42 aload_2 43 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)> 46 ldc #17 <String " "> 48 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)> 51 invokevirtual #18 <Method java.lang.String toString()> 54 astore_1 55 aload_3 56 invokevirtual #19 <Method java.lang.String readLine()> 59 dup 60 astore_2 61 ifnonnull 31 64 goto 79 67 astore 4 69 aload 4 71 invokevirtual #20 <Method null> 74 aconst_null 75 astore_1 76 goto 79 79 aload_1 80 areturn Exception table: from to target type 3 64 67 <Class java.lang.Exception> Method void main(java.lang.String[]) 0 iconst_0 1 istore_1 2 goto 51 5 getstatic #21 <Field java.io.PrintStream out> 8 new #14 <Class java.lang.StringBuffer> 11 dup 12 invokespecial #15 <Method java.lang.StringBuffer()> 15 aload_0 16 iload_1 17 aaload 18 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)> 21 ldc #22 <String ":"> 23 invokevirtual #16 <Method java.lang.StringBuffer append(java.lang.String)> 26 invokevirtual #18 <Method java.lang.String toString()> 29 invokevirtual #23 <Method void println(java.lang.String)> 32 getstatic #21 <Field java.io.PrintStream out> 35 new #24 <Class FetchURL> 38 dup 39 aload_0 40 iload_1 41 aaload 42 invokespecial #25 <Method FetchURL(java.lang.String)> 45 invokevirtual #26 <Method void println(java.lang.Object)> 48 iinc 1 1 51 iload_1 52 aload_0 53 arraylength 54 if_icmplt 5 57 return A JAR file packages a subdirectory and its descendants into a single file. A Java CLASSPATH specification may contain a JAR filename everywhere it might contain a directory name. Let's say you use the GPL'ed Java personal finance program called jgnash and you've compiled it from source, so you have a directory off your home directory called jgnash/bin. Suppose you run the program by directly invoking java to run the class jgnashMain and you have $HOME/jgnash/bin on your CLASSPATH. You could clean up the mess on your hard drive by using the jar command to squash all the files in jgnash/bin together into a single JAR file, as shown in Example 5.20. Example 5.20. Making a JAR file$ cd ; mkdir jars $ jar cvf jars/jgnash.jar jgnash/bin You could then replace the $HOME/jgnash/bin enTRy in your CLASSPATH with $HOME/jars/jgnash.jar. After that you would still run jgnash with exactly the same java command you always did, but now you got rid of the cluttered pile of files. This is only the most basic purpose of jar, however. Its uses extend well beyond merely concatenating and compressing collections of .class files. 5.11.1. Deploying ApplicationsOne of the best uses of jar is to package applications for distribution. You can put a large Java application into a single file with jar, and by using a manifest (which we are about to discuss) you can nominate the main class to run in that JAR file. You can then provide a shell script (and a batch file, if you are also deploying to Microsoft Windows) that will set the CLASSPATH to point to the JAR file and run java against it. With this simple setup, users need not even know they are using a Java applicationit runs like any other application. 5.11.1.1 The Manifest FileThe only way in which jar really differs from any other ZIP archive utility is in the automatic creation and use of a manifest file, by default named META-INF/MANIFEST in the archive. Even if you do not specify a manifest file of your own, the jar utility creates one for you. Let's take a moment to look at what goes into the manifest. A manifest is basically a list of key/value pairs. The key comes at the start of a line and the value comes at the end of the line with a colon separating the two. Example 5.21 shows a sample manifest. Example 5.21. Manifest from the Payback sample applicationManifest-Version: 1.0 Ant-Version: Apache Ant 1.5.3 Created-By: 1.4.1_02-b06 (Sun Microsystems Inc.) Version: 1.0 Main-Class: net.multitool.Payback.Payback All of these entries were produced automatically by ant or the jar utility itself, except for Main-Class, which we specified (albeit with ant, as you will see in Chapter 9). The manifest has certain values that are always filled in by jar, but two that you might commonly specify are
There are keys specific to applets, to signed applications, to beans, and so forth. We will address these as it becomes necessary. Full details can, of course, be found in the Sun's documentation for jar.[27]
5.11.1.2 Putting a Compiled Application in a JAR FileLet's assume we are going to manually put a Java application in a JAR file. We will want to specify the name of the class that contains the main() method of the application. First off, you want the JAR's directory hierarchy to begin at the folder that contains the first node of each package's name. Our sample application here is in the package net.multitool.Payback, so we want our present working directory to be the one which contains the net subdirectory. Here's a dump of the directory tree from that point after compilation of our sample application: $ find . -print . ./net ./net/multitool ./net/multitool/Payback ./net/multitool/Payback/Account.class ./net/multitool/Payback/Purchase.class ./net/multitool/Payback/Cost.class ./net/multitool/Payback/DebtAccount.class ./net/multitool/Payback/Payback.class ./net/multitool/Payback/SavingsAccount.class ./net/multitool/util ./net/multitool/util/SAMoney.class ./net/multitool/util/SAMoneyTest$1.class ./net/multitool/util/SAMoneyTest$2.class ./net/multitool/util/SAMoneyTest.class $ We now want to specify which class contains the application's main() method. It happens to be the Payback class, so we create a file called manifest[28] with the following contents:
$ cat manifest Main-Class: net.multitool.Payback.Payback Next, we use the jar utility to create the JAR file: $ jar cmf manifest payback.jar net $ ls -la total 20 drwxrwxr-x 3 mschwarz mschwarz 4096 Aug 4 18:19 . drwxrwxr-x 7 mschwarz mschwarz 4096 Aug 4 17:57 .. -rw-rw-r-- 1 mschwarz mschwarz 43 Aug 4 18:17 manifest drwxrwxr-x 3 mschwarz mschwarz 4096 Jul 28 16:16 net -rw-rw-r-- 1 mschwarz mschwarz 7506 Aug 4 18:21 payback.jar The options to jar tell it what to do. In our case, -c instructs to create a JAR file, -m adds the contents of the file named in the next parameter to the META-INF/MANIFEST file, -f and the next parameter is the filename of the JAR file being created. If we had not specified -f, the JAR file would have been written to standard out and an I/O redirect would be needed, but the result would have been the same: $ jar cvm manifest net > payback.jar $ ls -la total 24 drwxrwxr-x 3 mschwarz mschwarz 4096 Aug 4 18:24 . drwxrwxr-x 7 mschwarz mschwarz 4096 Aug 4 17:57 .. -rw-rw-r-- 1 mschwarz mschwarz 43 Aug 4 18:17 manifest drwxrwxr-x 3 mschwarz mschwarz 4096 Jul 28 16:16 net -rw-rw-r-- 1 mschwarz mschwarz 7506 Aug 4 18:27 payback.jar Everything that follows parameters required by option letters is considered to be a file or directory that is to be added to the JAR file. The option syntax for jar is similar to that for pkzip in the DOS/Windows world and the tar utility in the UNIX world. As elsewhere in this chapter, we are just getting you started. See Sun's documentation for details. 5.11.2. Basic jar OperationWe have already covered the most common case, using jar to create a "rolled-up" Java application. jar has many command options besides -c and we'll document a few of them. -c Create a JAR file. -u Update a JAR filereplace updated files, add missing files. -x Extract files from a JAR file. -t List files in a JAR. -f Specify the JAR filename. -v Be verbosedisplay descriptions of what the jar utility is doing as it does it. -m Add the contents of the named file to the manifest. |
|