Java uses the JAR file format for packaging application components. Originally used simply for packaging classes and their associated resources, it is now used for package types that allow embedding of other packages such as:
- Web applications (WAR files) that may contain JAR files with classes and/or web fragments
- Resource adapters (J2CA RAR files) that may contain JAR files with classes or native libraries
- Enterprise archives (EAR files) that may contain JAR files, WARs or RARs (with their embedded JARs and libraries)
This nesting is typically handled by expanding the packages onto the filesystem where they can be accessed using the standard JDK APIs; however, this requires a writable filesystem with space to hold the extracted packages and takes time to perform the extractions. This has the advantage that every resource contained in the package can be identified by a URL using a scheme supported directly by the JDK (using either the "file" protocol or the "jar" protocol).
To avoid unpacking the archive, alternative mechanisms have been build that use custom URLs and ClassLoader implementations to access their content. Examples of these are the "jndi" scheme used in previous versions of Tomcat or the "onejar" scheme used by the One-Jar project. These custom schemes may not be recognized by framework libraries and may be handled incorrectly or inefficiently. This is compounded by schemes deriving from the "jar" scheme with its use of non-hierarchical URIs that require special handling.
This proposal explores an alternative implementation based on the use of the NIO FileSystem library introduced in Java 7.
A prototype implementation is available in Tomcat's sandbox at http://svn.apache.org/viewvc/tomcat/sandbox/niofs/
The design is predicated on the ability to create FileSystem to provide a fully-functional view of an archive's content from a !Path referring to an archive. !Paths to entries in that FileSystem may be used as the basis for other archive FileSystems. Essentially, an archive can be mounted as a FileSystem and any archives it contains can in turn be mounted to form a nested hierarchy of FileSystems.
A FileSystem view of an archive may be created by calling the newFileSystem(Path) method on the provider.
The FileSystem underlying the Path must support random access via the SeekableByteChannel returned from newByteChannel()
The provider's newByteChannel() operation must return a SeekableByteChannel that supports random access
A FileSystem view of an archive may be created by calling the newFileSystem(URI) method on the provider.
- The URI must be able to be converted to a Path using the Paths.get(URI) API.
The FileSystem backing such a Path must meet the constraints defined for newFileSystem(Path)
- The URIs for Paths returned by the provider must use standard URI syntax and support resolving of relative references
- The provider will be identified by the URI scheme "archive"
- The provider should avoid unnecessary buffering of data in memory or on disk
- Buffering modes should be configurable by the user
- Performance should be comparable to that achievable by extracting the archive to disk
- Mount performance should be comparable to the time and resources taken to extract the archive's content
- File open performance should be comparable to the time taken to open a file on the default filesystem
- File read performance should be comparable to the time taken to read from a file on the default filesystem
- File seek performance should be comparable to the time taken to position within a file on the default filesystem
PKWARE's documentation on the format can be found at http://www.pkware.com/documents/casestudies/APPNOTE.TXT