Jarpublic class Jar extends Zip
Fields Summary |
---|
private static final String | INDEX_NAMEThe index file name. | private static final String | MANIFEST_NAMEThe manifest file name. | private List | serviceListList of all known SPI Services | private Manifest | configuredManifestmerged manifests added through addConfiguredManifest | private Manifest | savedConfiguredManifestshadow of the above if upToDate check alters the value | private Manifest | filesetManifestmerged manifests added through filesets | private Manifest | originalManifestManifest of original archive, will be set to null if not in
update mode. | private FilesetManifestConfig | filesetManifestConfigwhether to merge fileset manifests;
value is true if filesetmanifest is 'merge' or 'mergewithoutmain' | private boolean | mergeManifestsMainwhether to merge the main section of fileset manifests;
value is true if filesetmanifest is 'merge' | private Manifest | manifestthe manifest specified by the 'manifest' attribute | private String | manifestEncodingThe encoding to use when reading in a manifest file | private File | manifestFileThe file found from the 'manifest' attribute. This can be
either the location of a manifest, or the name of a jar added
through a fileset. If its the name of an added jar, the
manifest is looked for in META-INF/MANIFEST.MF | private boolean | indexjar index is JDK 1.3+ only | private boolean | createEmptywhether to really create the archive in createEmptyZip, will
get set in getResourcesToAdd. | private Vector | rootEntriesStores all files that are in the root of the archive (i.e. that
have a name that doesn't contain a slash) so they can get
listed in the index.
Will not be filled unless the user has asked for an index. | private org.apache.tools.ant.types.Path | indexJarsPath containing jars that shall be indexed in addition to this archive. | private static final org.apache.tools.zip.ZipExtraField[] | JAR_MARKERExtra fields needed to make Solaris recognize the archive as a jar file. | protected String | emptyBehavior |
Constructors Summary |
---|
public Jar()constructor
// CheckStyle:VisibilityModifier ON
super();
archiveType = "jar";
emptyBehavior = "create";
setEncoding("UTF8");
rootEntries = new Vector();
|
Methods Summary |
---|
public void | addConfiguredIndexJars(org.apache.tools.ant.types.Path p)Add a path to index jars.
if (indexJars == null) {
indexJars = new Path(getProject());
}
indexJars.append(p);
| public void | addConfiguredManifest(Manifest newManifest)Allows the manifest for the archive file to be provided inline
in the build file rather than in an external file.
if (configuredManifest == null) {
configuredManifest = newManifest;
} else {
configuredManifest.merge(newManifest);
}
savedConfiguredManifest = configuredManifest;
| public void | addConfiguredService(org.apache.tools.ant.types.spi.Service service)A nested SPI service element.
// Check if the service is configured correctly
service.check();
serviceList.add(service);
| public void | addMetainf(org.apache.tools.ant.types.ZipFileSet fs)Adds a zipfileset to include in the META-INF directory.
// We just set the prefix for this fileset, and pass it up.
fs.setPrefix("META-INF/");
super.addFileset(fs);
| protected void | cleanUp()Make sure we don't think we already have a MANIFEST next time this task
gets executed.
super.cleanUp();
// we want to save this info if we are going to make another pass
if (!doubleFilePass || (doubleFilePass && !skipWriting)) {
manifest = null;
configuredManifest = savedConfiguredManifest;
filesetManifest = null;
originalManifest = null;
}
rootEntries.removeAllElements();
| protected boolean | createEmptyZip(java.io.File zipFile)Create an empty jar file.
if (!createEmpty) {
return true;
}
if (emptyBehavior.equals("skip")) {
log("Warning: skipping " + archiveType + " archive "
+ zipFile + " because no files were included.",
Project.MSG_WARN);
return true;
} else if (emptyBehavior.equals("fail")) {
throw new BuildException("Cannot create " + archiveType
+ " archive " + zipFile
+ ": no files were included.",
getLocation());
}
ZipOutputStream zOut = null;
try {
log("Building MANIFEST-only jar: "
+ getDestFile().getAbsolutePath());
zOut = new ZipOutputStream(new FileOutputStream(getDestFile()));
zOut.setEncoding(getEncoding());
if (isCompress()) {
zOut.setMethod(ZipOutputStream.DEFLATED);
} else {
zOut.setMethod(ZipOutputStream.STORED);
}
initZipOutputStream(zOut);
finalizeZipOutputStream(zOut);
} catch (IOException ioe) {
throw new BuildException("Could not create almost empty JAR archive"
+ " (" + ioe.getMessage() + ")", ioe,
getLocation());
} finally {
// Close the output stream.
try {
if (zOut != null) {
zOut.close();
}
} catch (IOException ex) {
// Ignore close exception
}
createEmpty = false;
}
return true;
| private void | createIndexList(org.apache.tools.zip.ZipOutputStream zOut)Create the index list to speed up classloading.
This is a JDK 1.3+ specific feature and is enabled by default. See
the JAR index specification for more details.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// encoding must be UTF8 as specified in the specs.
PrintWriter writer = new PrintWriter(new OutputStreamWriter(baos,
"UTF8"));
// version-info blankline
writer.println("JarIndex-Version: 1.0");
writer.println();
// header newline
writer.println(zipFile.getName());
writeIndexLikeList(new ArrayList(addedDirs.keySet()),
rootEntries, writer);
writer.println();
if (indexJars != null) {
Manifest mf = createManifest();
Manifest.Attribute classpath =
mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH);
String[] cpEntries = null;
if (classpath != null && classpath.getValue() != null) {
StringTokenizer tok = new StringTokenizer(classpath.getValue(),
" ");
cpEntries = new String[tok.countTokens()];
int c = 0;
while (tok.hasMoreTokens()) {
cpEntries[c++] = tok.nextToken();
}
}
String[] indexJarEntries = indexJars.list();
for (int i = 0; i < indexJarEntries.length; i++) {
String name = findJarName(indexJarEntries[i], cpEntries);
if (name != null) {
ArrayList dirs = new ArrayList();
ArrayList files = new ArrayList();
grabFilesAndDirs(indexJarEntries[i], dirs, files);
if (dirs.size() + files.size() > 0) {
writer.println(name);
writeIndexLikeList(dirs, files, writer);
writer.println();
}
}
}
}
writer.flush();
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
super.zipFile(bais, zOut, INDEX_NAME, System.currentTimeMillis(), null,
ZipFileSet.DEFAULT_FILE_MODE);
| private Manifest | createManifest()
try {
Manifest finalManifest = Manifest.getDefaultManifest();
if (manifest == null) {
if (manifestFile != null) {
// if we haven't got the manifest yet, attempt to
// get it now and have manifest be the final merge
manifest = getManifest(manifestFile);
}
}
/*
* Precedence: manifestFile wins over inline manifest,
* over manifests read from the filesets over the original
* manifest.
*
* merge with null argument is a no-op
*/
if (isInUpdateMode()) {
finalManifest.merge(originalManifest);
}
finalManifest.merge(filesetManifest);
finalManifest.merge(configuredManifest);
finalManifest.merge(manifest, !mergeManifestsMain);
return finalManifest;
} catch (ManifestException e) {
log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
throw new BuildException("Invalid Manifest", e, getLocation());
}
| private void | filesetManifest(java.io.File file, java.io.InputStream is)
if (manifestFile != null && manifestFile.equals(file)) {
// If this is the same name specified in 'manifest', this
// is the manifest to use
log("Found manifest " + file, Project.MSG_VERBOSE);
try {
if (is != null) {
InputStreamReader isr;
if (manifestEncoding == null) {
isr = new InputStreamReader(is);
} else {
isr = new InputStreamReader(is, manifestEncoding);
}
manifest = getManifest(isr);
} else {
manifest = getManifest(file);
}
} catch (UnsupportedEncodingException e) {
throw new BuildException("Unsupported encoding while reading "
+ "manifest: " + e.getMessage(), e);
}
} else if (filesetManifestConfig != null
&& !filesetManifestConfig.getValue().equals("skip")) {
// we add this to our group of fileset manifests
log("Found manifest to merge in file " + file,
Project.MSG_VERBOSE);
try {
Manifest newManifest = null;
if (is != null) {
InputStreamReader isr;
if (manifestEncoding == null) {
isr = new InputStreamReader(is);
} else {
isr = new InputStreamReader(is, manifestEncoding);
}
newManifest = getManifest(isr);
} else {
newManifest = getManifest(file);
}
if (filesetManifest == null) {
filesetManifest = newManifest;
} else {
filesetManifest.merge(newManifest);
}
} catch (UnsupportedEncodingException e) {
throw new BuildException("Unsupported encoding while reading "
+ "manifest: " + e.getMessage(), e);
} catch (ManifestException e) {
log("Manifest in file " + file + " is invalid: "
+ e.getMessage(), Project.MSG_ERR);
throw new BuildException("Invalid Manifest", e, getLocation());
}
} else {
// assuming 'skip' otherwise
// don't warn if skip has been requested explicitly, warn if user
// didn't set the attribute
// Hide warning also as it makes no sense since
// the filesetmanifest attribute itself has been
// hidden
//int logLevel = filesetManifestConfig == null ?
// Project.MSG_WARN : Project.MSG_VERBOSE;
//log("File " + file
// + " includes a META-INF/MANIFEST.MF which will be ignored. "
// + "To include this file, set filesetManifest to a value other "
// + "than 'skip'.", logLevel);
}
| protected void | finalizeZipOutputStream(org.apache.tools.zip.ZipOutputStream zOut)Finalize the zip output stream.
This creates an index list if the index attribute is true.
if (index) {
createIndexList(zOut);
}
| protected static final java.lang.String | findJarName(java.lang.String fileName, java.lang.String[] classpath)try to guess the name of the given file.
If this jar has a classpath attribute in its manifest, we
can assume that it will only require an index of jars listed
there. try to find which classpath entry is most likely the
one the given file name points to.
In the absence of a classpath attribute, assume the other
files will be placed inside the same directory as this jar and
use their basename.
if there is a classpath and the given file doesn't match any
of its entries, return null.
if (classpath == null) {
return (new File(fileName)).getName();
}
fileName = fileName.replace(File.separatorChar, '/");
TreeMap matches = new TreeMap(new Comparator() {
// longest match comes first
public int compare(Object o1, Object o2) {
if (o1 instanceof String && o2 instanceof String) {
return ((String) o2).length()
- ((String) o1).length();
}
return 0;
}
});
for (int i = 0; i < classpath.length; i++) {
if (fileName.endsWith(classpath[i])) {
matches.put(classpath[i], classpath[i]);
} else {
int slash = classpath[i].indexOf("/");
String candidate = classpath[i];
while (slash > -1) {
candidate = candidate.substring(slash + 1);
if (fileName.endsWith(candidate)) {
matches.put(candidate, classpath[i]);
break;
}
slash = candidate.indexOf("/");
}
}
}
return matches.size() == 0
? null : (String) matches.get(matches.firstKey());
| private Manifest | getManifest(java.io.Reader r)
Manifest newManifest = null;
try {
newManifest = new Manifest(r);
} catch (ManifestException e) {
log("Manifest is invalid: " + e.getMessage(), Project.MSG_ERR);
throw new BuildException("Invalid Manifest: " + manifestFile,
e, getLocation());
} catch (IOException e) {
throw new BuildException("Unable to read manifest file"
+ " (" + e.getMessage() + ")", e);
}
return newManifest;
| private Manifest | getManifest(java.io.File manifestFile)
Manifest newManifest = null;
FileInputStream fis = null;
InputStreamReader isr = null;
try {
fis = new FileInputStream(manifestFile);
if (manifestEncoding == null) {
isr = new InputStreamReader(fis);
} else {
isr = new InputStreamReader(fis, manifestEncoding);
}
newManifest = getManifest(isr);
} catch (UnsupportedEncodingException e) {
throw new BuildException("Unsupported encoding while reading manifest: "
+ e.getMessage(), e);
} catch (IOException e) {
throw new BuildException("Unable to read manifest file: "
+ manifestFile
+ " (" + e.getMessage() + ")", e);
} finally {
if (isr != null) {
try {
isr.close();
} catch (IOException e) {
// do nothing
}
}
}
return newManifest;
| private Manifest | getManifestFromJar(java.io.File jarFile)
ZipFile zf = null;
try {
zf = new ZipFile(jarFile);
// must not use getEntry as "well behaving" applications
// must accept the manifest in any capitalization
Enumeration e = zf.entries();
while (e.hasMoreElements()) {
ZipEntry ze = (ZipEntry) e.nextElement();
if (ze.getName().equalsIgnoreCase(MANIFEST_NAME)) {
InputStreamReader isr =
new InputStreamReader(zf.getInputStream(ze), "UTF-8");
return getManifest(isr);
}
}
return null;
} finally {
if (zf != null) {
try {
zf.close();
} catch (IOException e) {
// XXX - log an error? throw an exception?
}
}
}
| protected ArchiveState | getResourcesToAdd(org.apache.tools.ant.types.ResourceCollection[] rcs, java.io.File zipFile, boolean needsUpdate)Collect the resources that are newer than the corresponding
entries (or missing) in the original archive.
If we are going to recreate the archive instead of updating
it, all resources should be considered as new, if a single one
is. Because of this, subclasses overriding this method must
call super.getResourcesToAdd and indicate with the
third arg if they already know that the archive is
out-of-date.
// need to handle manifest as a special check
if (zipFile.exists()) {
// if it doesn't exist, it will get created anyway, don't
// bother with any up-to-date checks.
try {
originalManifest = getManifestFromJar(zipFile);
if (originalManifest == null) {
log("Updating jar since the current jar has no manifest",
Project.MSG_VERBOSE);
needsUpdate = true;
} else {
Manifest mf = createManifest();
if (!mf.equals(originalManifest)) {
log("Updating jar since jar manifest has changed",
Project.MSG_VERBOSE);
needsUpdate = true;
}
}
} catch (Throwable t) {
log("error while reading original manifest in file: "
+ zipFile.toString() + t.getMessage(),
Project.MSG_WARN);
needsUpdate = true;
}
} else {
// no existing archive
needsUpdate = true;
}
createEmpty = needsUpdate;
return super.getResourcesToAdd(rcs, zipFile, needsUpdate);
| protected static final void | grabFilesAndDirs(java.lang.String file, java.util.List dirs, java.util.List files)Grab lists of all root-level files and all directories
contained in the given archive.
org.apache.tools.zip.ZipFile zf = null;
try {
zf = new org.apache.tools.zip.ZipFile(file, "utf-8");
Enumeration entries = zf.getEntries();
HashSet dirSet = new HashSet();
while (entries.hasMoreElements()) {
org.apache.tools.zip.ZipEntry ze =
(org.apache.tools.zip.ZipEntry) entries.nextElement();
String name = ze.getName();
// META-INF would be skipped anyway, avoid index for
// manifest-only jars.
if (!name.startsWith("META-INF/")) {
if (ze.isDirectory()) {
dirSet.add(name);
} else if (name.indexOf("/") == -1) {
files.add(name);
} else {
// a file, not in the root
// since the jar may be one without directory
// entries, add the parent dir of this file as
// well.
dirSet.add(name.substring(0,
name.lastIndexOf("/") + 1));
}
}
}
dirs.addAll(dirSet);
} finally {
if (zf != null) {
zf.close();
}
}
| protected void | initZipOutputStream(org.apache.tools.zip.ZipOutputStream zOut)Initialize the zip output stream.
if (!skipWriting) {
Manifest jarManifest = createManifest();
writeManifest(zOut, jarManifest);
writeServices(zOut);
}
| public void | reset()reset to default values.
super.reset();
emptyBehavior = "create";
configuredManifest = null;
filesetManifestConfig = null;
mergeManifestsMain = false;
manifestFile = null;
index = false;
| public void | setFilesetmanifest(org.apache.tools.ant.taskdefs.Jar$FilesetManifestConfig config)Behavior when a Manifest is found in a zipfileset or zipgroupfileset file.
Valid values are "skip", "merge", and "mergewithoutmain".
"merge" will merge all of manifests together, and merge this into any
other specified manifests.
"mergewithoutmain" merges everything but the Main section of the manifests.
Default value is "skip".
Note: if this attribute's value is not "skip", the created jar will not
be readable by using java.util.jar.JarInputStream
filesetManifestConfig = config;
mergeManifestsMain = "merge".equals(config.getValue());
if (filesetManifestConfig != null
&& !filesetManifestConfig.getValue().equals("skip")) {
doubleFilePass = true;
}
| public void | setIndex(boolean flag)Set whether or not to create an index list for classes.
This may speed up classloading in some cases.
index = flag;
| public void | setJarfile(java.io.File jarFile)Set the destination file.
setDestFile(jarFile);
| public void | setManifest(java.io.File manifestFile)The manifest file to use. This can be either the location of a manifest,
or the name of a jar added through a fileset. If its the name of an added
jar, the task expects the manifest to be in the jar at META-INF/MANIFEST.MF.
if (!manifestFile.exists()) {
throw new BuildException("Manifest file: " + manifestFile
+ " does not exist.", getLocation());
}
this.manifestFile = manifestFile;
| public void | setManifestEncoding(java.lang.String manifestEncoding)The character encoding to use in the manifest file.
this.manifestEncoding = manifestEncoding;
| public void | setWhenempty(WhenEmpty we)Not used for jar files.
log("JARs are never empty, they contain at least a manifest file",
Project.MSG_WARN);
| public void | setWhenmanifestonly(WhenEmpty we)Indicates if a jar file should be created when it would only contain a
manifest file.
Possible values are: fail (throw an exception
and halt the build); skip (do not create
any archive, but issue a warning); create
(make an archive with only a manifest file).
Default is create ;
emptyBehavior = we.getValue();
| protected final void | writeIndexLikeList(java.util.List dirs, java.util.List files, java.io.PrintWriter writer)Writes the directory entries from the first and the filenames
from the second list to the given writer, one entry per line.
// JarIndex is sorting the directories by ascending order.
// it has no value but cosmetic since it will be read into a
// hashtable by the classloader, but we'll do so anyway.
Collections.sort(dirs);
Collections.sort(files);
Iterator iter = dirs.iterator();
while (iter.hasNext()) {
String dir = (String) iter.next();
// try to be smart, not to be fooled by a weird directory name
dir = dir.replace('\\", '/");
if (dir.startsWith("./")) {
dir = dir.substring(2);
}
while (dir.startsWith("/")) {
dir = dir.substring(1);
}
int pos = dir.lastIndexOf('/");
if (pos != -1) {
dir = dir.substring(0, pos);
}
// looks like nothing from META-INF should be added
// and the check is not case insensitive.
// see sun.misc.JarIndex
if (dir.startsWith("META-INF")) {
continue;
}
// name newline
writer.println(dir);
}
iter = files.iterator();
while (iter.hasNext()) {
writer.println(iter.next());
}
| private void | writeManifest(org.apache.tools.zip.ZipOutputStream zOut, Manifest manifest)
for (Enumeration e = manifest.getWarnings();
e.hasMoreElements();) {
log("Manifest warning: " + (String) e.nextElement(),
Project.MSG_WARN);
}
zipDir(null, zOut, "META-INF/", ZipFileSet.DEFAULT_DIR_MODE,
JAR_MARKER);
// time to write the manifest
ByteArrayOutputStream baos = new ByteArrayOutputStream();
OutputStreamWriter osw = new OutputStreamWriter(baos, Manifest.JAR_ENCODING);
PrintWriter writer = new PrintWriter(osw);
manifest.write(writer);
writer.flush();
ByteArrayInputStream bais =
new ByteArrayInputStream(baos.toByteArray());
super.zipFile(bais, zOut, MANIFEST_NAME,
System.currentTimeMillis(), null,
ZipFileSet.DEFAULT_FILE_MODE);
super.initZipOutputStream(zOut);
| private void | writeServices(org.apache.tools.zip.ZipOutputStream zOut)Write SPI Information to JAR
Iterator serviceIterator;
Service service;
serviceIterator = serviceList.iterator();
while (serviceIterator.hasNext()) {
service = (Service) serviceIterator.next();
//stolen from writeManifest
super.zipFile(service.getAsStream(), zOut,
"META-INF/service/" + service.getType(),
System.currentTimeMillis(), null,
ZipFileSet.DEFAULT_FILE_MODE);
}
| protected void | zipFile(java.io.InputStream is, org.apache.tools.zip.ZipOutputStream zOut, java.lang.String vPath, long lastModified, java.io.File fromArchive, int mode)Overridden from Zip class to deal with manifests and index lists.
if (MANIFEST_NAME.equalsIgnoreCase(vPath)) {
if (!doubleFilePass || (doubleFilePass && skipWriting)) {
filesetManifest(fromArchive, is);
}
} else if (INDEX_NAME.equalsIgnoreCase(vPath) && index) {
log("Warning: selected " + archiveType
+ " files include a META-INF/INDEX.LIST which will"
+ " be replaced by a newly generated one.", Project.MSG_WARN);
} else {
if (index && vPath.indexOf("/") == -1) {
rootEntries.addElement(vPath);
}
super.zipFile(is, zOut, vPath, lastModified, fromArchive, mode);
}
|
|