By Sean C. Sullivan
This article introduces two new packages from Microsoft’s Java Virtual Machine (VM): com.ms.util.cab and java.util.zip. The com.ms.util.cab package provides facilities for reading and writing CAB files. Similarly, the java.util.zip package provides facilities for reading and writing JAR files. We demonstrate how to use both of these packages by building a utility that converts CAB files into JAR files.
A CAB (cabinet) file is a file with the suffix .cab that acts as a container for other files. A CAB file is conceptually similar to a ZIP file: it serves as a compressed archive for a group of files. CAB files can contain any kind of file. For example, you might store Java classes, DLL’s, images, or text files in a CAB file.
CAB files can be digitally signed. The digital signature is used to identify the creator of the file.
You create a CAB file with the CABARC program. CABARC is a command-line utility in the Microsoft CAB SDK. Typically, you pass a list of options, a CAB filename, and a list of files to CABARC:
cabarc n mycab.cab Foo.class Bar.class bananas.jpg
This command creates the file mycab.cab. In this example, mycab.cab contains three files: Foo.class, Bar.class, and bananas.jpg. For more information on CABARC, see the Microsoft CAB SDK.
You digitally sign a CAB file with the SIGNCODE program. A cabinet file can be signed at three different security levels: high, medium, or low. For example, this command will sign mycab.cab with the low security setting:
signcode -j javasign.dll -jp low -spc publish.spc -k MyKeyName mycab.cab
For more information on SIGNCODE, see the Microsoft CAB SDK.
CAB files are typically used in two ways: packaging Java applets and packaging ActiveX controls. A Java applet’s class files and images files can be packaged into a single CAB file. This results in more efficient downloads because client PC’s will only need to fetch one file from the Web server. After packaging your applet in a CAB, you must set the CABBASE parameter in the <APPLET> tag. Microsoft Internet Explorer uses the CABBASE parameter to find the CAB file. For example:
<APPLET CODE = MyApplet.class
WIDTH = 460
HEIGHT = 160>
<PARAM NAME = CABBASE
VALUE = "myapplet.cab">
</APPLET>
Internet Explorer will read the CABBASE parameter and download myapplet.cab. Netscape Navigator and other Web browsers will ignore the CABBASE parameter.
ActiveX controls are also packaged in CAB files. When an ActiveX control is placed on a Web page, the <OBJECT> tag’s CODEBASE attribute specifies the CAB filename:
<OBJECT
CLASSID = "clsid:9AB5C123-1234-11d1-8516-00A0C921F7F3"
CODEBASE = "mycontrol.cab"
WIDTH = 200
HEIGHT = 200
ID = mycontrol>
<PARAM NAME = "param1"
VALUE = "value1">
</OBJECT>
When Internet Explorer loads the Web page, it reads the CODEBASE attribute to determine the ActiveX control’s CAB filename.
Similar to CAB files, a JAR (Java Archive) file is a file format that compresses a set of files into a single file. JAR files have the suffix .jar. JAR files also provide compression and support for digital signatures. The optional digital signatures can be used to authenticate the JAR file’s origin. You can put any kind of file in a JAR file. For example, you may package a set of Java class files and image files into a single JAR file.
You create JAR files with Sun’s JAR utility. This command-line utility is part of Sun JDK 1.1.
You can package your Java applets in a single JAR file. As with CAB files, this results in more efficient downloads because only one file needs to be downloaded from the Web server. Additionally, if you sign the JAR file, your applet can get access to system resources outside of the Java security sandbox. After packaging the applet class files in a JAR, you must add the ARCHIVE attribute to the <APPLET> tag:
<APPLET CODE = MyApplet.class
ARCHIVE = "jars/MyApplet.jar"
WIDTH = 460
HEIGHT = 160>
<PARAM NAME = foo
VALUE = "bar">
</APPLET>
Both Microsoft Internet Explorer 4.0 and Netscape Navigator 4.0 will read the ARCHIVE attribute and download the specified JAR file. Microsoft Internet Explorer 3.0 will ignore the ARCHIVE attribute.
JAR files are also used to package JavaBeans. A bean’s Java class files, icon files, and text files can be bundled into a single JAR file.
The CAB utility classes are located in a package called com.ms.util.cab. This package allows Java programmers to create CAB files as well as read the contents of an existing CAB file. The Sun and Netscape Java VMs do not provide this package; it’s included only with the Microsoft Java VM. In the Cab2Jar application, we import this package:
import com.ms.util.cab.*;
The Cab2Jar application will use these classes from com.ms.util.cab:
CabCorruptException
CabCreator
CabDecoder
CabEnumerator
CabException
CabFileEntry
CabFolderEntry
CabConstants
CabDecoderInterface
CabProgressInterface
JAR files can be manipulated using the classes in java.util.zip. The package provides classes for checksum calculations, reading/writing ZIP files, and reading/writing gzip files. In the Cab2Jar application, we will use java.util.zip.ZipOutputStream and java.util.zip.ZipEntry classes to create a JAR file.
Cab2Jar is a command-line utility that converts a CAB file into a JAR file. It’s a Java application that consists of one class, Cab2Jar.class.
Like any Java application, the main method is invoked first. In main, we examine the command-line parameters to get the name of the CAB file. Once we have the CAB filename, we determine the name of our output file. For example, if the CAB filename is myapplet.cab, then the output filename is myapplet.jar. Next, we use a FileOutputStream to create the ZipOutputStream. Any bytes written to the ZipOutputStream go to the JAR file.
A CabDecoder object is created to decode the CAB file. We call CabDecoder.extract to start extracting the files from the CAB file. For each file in the CAB, we create a ZipEntry object. The ZipEntry is a special entry in a JAR that specifies the name and size of the file. For each ZipEntry, we write the corresponding bytes to the ZipOutputStream.
After the contents of the CAB file have been processed, we close the ZipOutputStream. This closes the JAR file. Voilà; the JAR file has been created.
To compile Cab2Jar.java (see Listing One beginning on page XX), you must have the Microsoft SDK for Java 2.0 installed on your system.
To compile, use this command:
C:\WORK> c:\sdk-java.20\bin\jvc.exe Cab2Jar.java
To run the application, use this command:
C:\WORK> c:\sdk-java.20\bin\jview.exe Cab2Jar mycab.cab
CAB files and JAR files are similar; they both serve as compressed file archives. Both file formats can also accommodate digital signatures. The com.ms.util.cab package provides facilities for reading and writing CAB files. The java.util.zip package provides facilities for reading and writing JAR files. The Cab2Jar application uses both of these packages to convert a CAB file into a JAR file.
Begin Listing One — Cab2Jar Class
/*
* Copyright (C) 1997-1998 Intel Corporation
* All Rights Reserved
*
* A royalty-free copyright license to copy, modify and
* compile this source code and to distribute it in your
* own products in source and binary forms is hereby
* granted, so long as this copyright notice and this
* copyright license appear in all source code copies. No
* other rights or licenses are granted. The Intel name or
* other trademarks may not be used to endorse or promote
* products using this header file without Intel's prior
* written permission.
*
* THIS CODE IS PROVIDED "AS IS" WITH NO WARRANTIES
* WHATSOEVER, INCLUDING ANY WARRANTY OF MERCHANTABILITY,
* NONINFRINGEMENT, OR FITNESS FOR ANY PARTICULAR PURPOSE.
* Intel disclaims all liability, including liability for
* infringement of any proprietary rights, relating to use
* of this source code.
*
* * - Other brands and names are the property of their
* respective owners.
*/
import com.ms.util.cab.*;
import java.util.zip.*;
import java.io.*;
/**
* This Java application is a command-line utility
* that converts CAB files into JAR files.
*
* This application will only run on the Microsoft
* Java* VM. You will need the MS SDK for Java 2.0
* to run it.
*
* <PRE>
* Usage:
* jview Cab2Jar myfile.cab
* </PRE>
*
* Limitations
* + Cab2Jar does not handle nested CAB files
* + Digitally signed CAB's are not converted into
* digitally signed JAR's. Instead, an unsigned
* JAR file is created.
*
* @author Sean C Sullivan
*/
public final class Cab2Jar
implements com.ms.util.cab.CabDecoderInterface {
private transient ZipOutputStream jarOutStream = null;
/**
* @param jos - The output stream for the JAR file
*/
public Cab2Jar(ZipOutputStream jos) {
this.jarOutStream = jos;
}
/**
* This method is part of the CabDecoderInterface.
*
* @param disk_name - The disk name of the cabinet
* @param cabinet_name - The cabinet's name
*
* @return - Returns an InputStream for the cabinet.
* If the specified cabinet is not available,
* null is returned.
*/
public InputStream openCabinet(String disk_name,
String cabinet_name) {
// just return null
return null;
}
/**
* This method is part of the CabDecoderInterface
*
* @param centry - The CabFileEntry that
* will be extracted.
*
* @return - Returns an OutputStream into which the
* file should be written or returns null if
* the file should not be extracted.
*/
public OutputStream
openOutputStream(CabFileEntry centry) {
OutputStream outstream = null;
String entryName = centry.getName();
System.out.println(
"openOutputStream called for " + entryName);
ZipEntry zEntry = new ZipEntry(entryName);
try {
this.jarOutStream.putNextEntry(zEntry);
outstream = this.jarOutStream;
}
catch (java.io.IOException ex) {
System.out.println(ex);
outstream = null;
}
return outstream;
}
/**
* This method is part of the CabDecoderInterface.
*
* @param os - The output stream for the file
* extracted from the cab
* @param centry - Contains information about the file
* extracted from the cab
* @param success - Was file extracted successfully?
*
* @return - Returns true if processing should continue
* for files in this cabinet, or false if
* processing should end immediately.
*/
public boolean closeOutputStream(OutputStream os,
CabFileEntry centry, boolean success) {
// For simplicity, we will assume that success is true
try {
this.jarOutStream.closeEntry();
}
catch (java.io.IOException ex) {
System.out.println(ex);
}
return true;
}
/**
* This method is part of the CabDecoderInterface.
*
* @param type - Type of reserved area. (see
* CabConstants for possible values)
* @param reserved_data - Array containing the reserved
* data. If this parameter is
* null, then this is only a
* message to indicate that the
* reserved area exists.
* @param reserved_data_size - The size of the reserved
* data area. If the size
* is not known, this
* parameter will be zero.
* @param other_data - Used only when type is
* RESERVED_CFDATA
* @param other_data_size - Used only when type is
* RESERVED_CFDATA
*
* @return - Returns true if this type of reserved
* entry should be read and passed back to
* subsequent calls of this method. Otherwise
* false is returned to indicate that no more
* calls should be made for this type of
* reserved data.
*/
public boolean reservedAreaData(int type,
byte[] reserved_data, int reserved_data_size,
byte[] other_data, int other_data_size) {
// Return false to indicate that no more calls should
// be made for this type of reserved data.
return false;
}
/**
* This method is part of the CabDecoderInterface.
*
* This method serves as a handler for any
* progress messages.
*
* @param progress_type - The progress type (see
* CabConstants for possible
* values)
* @param val1 - A value related to the specific
* progress type
* @param val2 - A value related to the specific
* progress type
* @param progress_data - Progress data for the
* specific progress type.
*
* @return - Returns a code specific to the message
* type. Returns null for messages that are
* not handled.
*/
public Object progress(int progress_type, long val1,
long val2, Object[] progress_data) {
// return null for all progress messages
return null;
}
/**
* The main method for this Java application.
*
* @param args - the program's command line arguments
*
* @return none
*/
public static void main (String args[]) {
CabDecoder decoder = null;
String jarFileName = null;
String cabFileName = null;
File cabFile = null;
FileInputStream input = null;
CabDecoderInterface cdi = null;
if (args.length < 1) {
System.out.println(
"Need a CAB file as an argument.");
return;
}
cabFileName = args[args.length-1];
cabFile = new File(cabFileName);
if (cabFile.canRead() != true) {
System.out.println("Can't read " + cabFileName);
return;
}
try {
input = new FileInputStream(cabFile);
}
catch (FileNotFoundException ex) {
System.out.println("File out found: " + cabFileName);
return;
}
boolean bEndsWithDotCab =
cabFileName.toLowerCase().endsWith(".cab");
int cabFileNameLength = cabFileName.length();
if (bEndsWithDotCab && (cabFileNameLength > 4)) {
jarFileName = cabFileName.substring(
0,cabFileNameLength - 4);
jarFileName = jarFileName + ".jar";
}
else {
jarFileName = cabFileName + ".jar";
}
FileOutputStream fos = null;
ZipOutputStream jarOutStream = null;
try {
fos = new FileOutputStream(jarFileName);
jarOutStream = new ZipOutputStream(fos);
}
catch (java.io.IOException ex) {
System.out.println(ex);
}
Cab2Jar converter = new Cab2Jar(jarOutStream);
try {
decoder = new CabDecoder(input, converter);
decoder.extract();
}
catch (CabException ex) {
System.out.println("A CabException occurred: " + ex);
}
catch (IOException ex) {
System.out.println("An IOException occurred: " + ex);
}
finally {
try {
input.close();
jarOutStream.close();
}
catch (IOException ex) {
// ignore
}
}
converter = null;
input = null;
jarOutStream = null;
} // End of main.
} // End of Cab2Jar.
End Listing One
The file referenced in this article is available for download from the Informant Web site at http://www.informant.com/ji/jinewupl.htm. File name: JI9801CJ.ZIP.
The Microsoft SDK for Java 2.0 can be downloaded from http://www.microsoft.com/java/sdk/
The Microsoft CAB SDK contains utilities and libraries for creating and digitally signing CAB files. You can download it at http://www.microsoft.com/workshop/prog/cab/
The JAR file specification is online at http://www.javasoft.com/products/jdk/1.1/docs/guide/jar/
Sean C. Sullivan is a Software Engineer in Intel’s Architecture Lab. Prior to Intel, Sean worked at IBM and Image Systems Technology. He can be reached at sean_sullivan@ccm.jf.intel.com.