Contents Previous Next

Chapter   8

Class Loading, JAR Files, and Inflation


The KVM source code includes an implementation for reading Java class files from regular files/directories, as well as from (compressed) JAR files. Generally speaking, the KVM class loader can be divided into two parts:

  1. generic part,
  2. port-dependent part.

The generic part, defined in file VmCommon/src/loader.c is designed to be independent of the file/storage system of the target device. This part of the class loader does not require any porting efforts. The JAR file reader, defined in files VmExtra/src/jar.c, VmExtra/src/inflate.c, VmExtra/h/jar.h, VmExtra/h/inflate.h, VmExtra/h/inflateint.h, and VmExtra/h/inflatetables.h, is also written in a way that it does not necessitate any porting efforts.


Note – The generic part of the class loader implementation has been redesigned in KVM 1.1 to support error handling in a more generalized, J2SE-compliant fashion.

If you need to provide an alternative method for loading class files, you must define your own port-specific class loading mechanism. The default implementation in VmExtra/src/loaderFile.c is intended for those target systems that have a conventional file system. This implementation can be used as a starting point for alternative, platform-specific implementations.

The KVM code to read JAR files can also be used independently of reading class files. Applications that need to make their own use of JAR files can use these functions. In addition, the function that decompresses compressed JAR entries (a process called “inflation”), can also be used to decompress other information. For example, the PNG image format uses the same compression and decompression algorithms.

8.1 Porting the class file loading interface

The structures and functions required by the port-specific class file loading interface have been defined in file VmCommon/h/loader.h. If you do not intend to use the default class file loading interface provided in file VmExtra/src/loaderFile.c, you must supply your own definitions for the structures and functions listed below.

You must define the C structure filePointerStruct. The generic code uses the definitions

    struct filePointerStruct; 
    typedef struct filePointerStruct *FILEPOINTER; 

without knowing anything about the fields of this structure.

You must also define the following functions:

The class file structure returned by openClassFile must be an object allocated from the Java heap.

8.2 JAR file reader

CLDC-compliant KVM implementations are required to be able to read class files from compressed JAR files. The location of the JAR file(s) is specified in an implementation-dependent manner.

Functions are provided in jar.c for reading entries in a JAR file. If the preprocessor symbol JAR_FILE_USE_STDIO is non-zero, then these functions use C standard I/O routines to read the JAR file. If this preprocessor symbol is set to 0, this indicates that JAR files are in memory.

The JAR file reader uses the inflater, which is discussed in the next section.

8.2.1 Opening a JAR file

Before using a JAR file, you must “open” it using the function

    bool_t openJARFile(void *nameOrAddress, int length, 
                        JAR_INFO entry) 

The arguments are as follows:

If JAR_FILE_USE_STDIO is non-zero, then the first argument is the name of the JAR file and the second argument is ignored.

If JAR_FILE_USE_STDIO is zero, then the first argument is a pointer in memory to the beginning of the JAR file, and the second argument is the length, in bytes, of the JAR file.

The third argument is a pointer to a structure of type struct jarInfoStruct defined in jar.h. This structure is filled with information about the opened JAR file. This function returns TRUE if it successfully managed to open the JAR file and parse its directory; it returns FALSE otherwise.

8.2.2 Closing a JAR file

If a JAR file has been successfully opened using openJARFile, you must close the file when you are done. You must use the function:

    void closeJARFile(JAR_INFO entry) 

The argument is a pointer to the same structure that was filled in by openJARFile.

8.2.3 Reading a JAR file entry

To read a specific entry in a JAR file, you use the function

    static void * 
    loadJARFileEntryInternal(JAR_INFO entry, 
                     const unsigned char *centralInfo, 
                     long *lengthP, int extraBytes); 

The entry argument is a pointer to the structure filled in by openJARFile. The centralInfo argument is the null-terminated name of the entry.1 The extraBytes entry indicates that the JAR reader should pad the result with that many extra bytes at the beginning.

If the JAR file reader is successful, it will set the *lengthP argument to the length of JAR file entry. This length does not include padding inserted because of the extraBytes argument. The actual entry (plus padding) is returned as the result of this function.

If the JAR file reader could not find the entry, or if for some reason it was unable to read the entry, this function returns NULL.

The result of this function is a heap-allocated object. If this function is called from within the KVM, then you must protect it, if necessary, from garbage collection.

8.2.4 Reading multiple JAR file directory

To read the directory of a JAR file and possibly some of its entries, use the function

    void loadJARFileEntries(JAR_INFO jarFile, 
                       JARFileTestFunction testFunction, 
                       JARFileRunFunction runFunction, 
                       void* info); 

The jarFile argument is a pointer to the structure filled in by openJARFile. The testFunction and runFunction arguments are callback functions whose use is described below. The info argument is not used by the jar directory reader, but is passed on an argument to the testFunction and runFunction callbacks.

The testFunction argument is a callback function that is called on each (non-directory) entry in the JAR file. It is called as follows:

    typedef bool_t  
        (*JARFileTestFunction)(const char *name, 
         int nameLength, 
         int *extraBytes, 
         void *info); 

The name and nameLength argument specify the name of entry in the JAR file directory. The name argument is not null terminated. The value *extraBytes is initially zero, but you can change it to a different value to indicate that the result needs to be padded with extra bytes at the beginning. The info argument is the same as whatever was passed to loadJARFileEntries.

If this function returns TRUE, it indicates that you want to read this entry. If this function returns FALSE, you do not want to read this entry.

For every entry in which testFunction returns TRUE, the jar file reader reads the data and calls the runFunction as follows:

    typedef void  
        (*JARFileRunFunction)(const char *name, int nameLength, 
          void *value, long length, void *info); 

The name and nameLength arguments are the same as above. The value argument gives the result of reading the JAR file entry. The length argument is the length of the JAR file entry, not including any padding bytes. The info argument is the same as whatever was passed to loadJARFileEntries.

If reading the entry is unsuccessful, then the runFunction is called with the value argument set to NULL.

The value argument is allocated on the heap so it must be protected, if necessary, from garbage collection.

8.3 Inflation

The inflate function can be used to decompress streams that have been compressed using the so-called deflation algorithm. This is the compression algorithm commonly used in JAR files and in the PNG image format.

The function that inflates JAR file entries can also be used for other purposes. The function is called with the following arguments.

    typedef  int  (*JarGetByteFunctionType)(void *); 
 
    bool_t inflateData(void *compData, JarGetByteFunctionType 
                   getByte, 
                   int compLen, 
                   UNSIGNED_CHAR_HANDLE decompData, 
                   int decompLen); 

This function decompresses a stream of compLen bytes into a buffer of decompLen bytes. Successive bytes of input are obtained by repeatedly calling

    getByte(inFile) 

This function will be called up to compLen + INFLATER_EXTRA_BYTES times, where INFLATER_EXTRA_BYTES is defined in inflate.h to be the constant 4. Any values returned beyond the first compLen calls to the function are immaterial.

The argument decompData must be a pointer to a buffer handle of at least decompLen characters. When using this function, the buffer must either not be in the heap, or decompData must be registered with the garbage collector so that decompData is updated if the buffer is moved.

This function returns TRUE if the decompression is successful, and FALSE otherwise.

1Note that Jar files always use ‘/’ as the directory separator character.

 


Contents Previous Next KVM Porting Guide
, CLDC 1.1