Browse Source

Implement jar index for referenced jars, PR: 14255

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@276389 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 21 years ago
parent
commit
c00d5a6977
4 changed files with 298 additions and 27 deletions
  1. +2
    -0
      build.xml
  2. +201
    -27
      src/main/org/apache/tools/ant/taskdefs/Jar.java
  3. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/Java.java
  4. +94
    -0
      src/testcases/org/apache/tools/ant/taskdefs/ProtectedJarMethodsTest.java

+ 2
- 0
build.xml View File

@@ -1551,6 +1551,8 @@
unless="tests.and.ant.share.classloader"/>
<exclude name="${ant.package}/taskdefs/ProcessDestroyerTest.java"
unless="tests.and.ant.share.classloader"/>
<exclude name="${ant.package}/taskdefs/ProtectedJarMethodsTest.java"
unless="tests.and.ant.share.classloader"/>

<!-- can only run if cvs is installed on your machine
enable by setting the property have.cvs


+ 201
- 27
src/main/org/apache/tools/ant/taskdefs/Jar.java View File

@@ -29,7 +29,15 @@ import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
@@ -37,10 +45,10 @@ import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.ZipFileSet;
import org.apache.tools.zip.ZipOutputStream;


/**
* Creates a JAR archive.
*
@@ -115,6 +123,13 @@ public class Jar extends Zip {
*/
private Vector rootEntries;

/**
* Path containing jars that shall be indexed in addition to this archive.
*
* @since Ant 1.7
*/
private Path indexJars;

/** constructor */
public Jar() {
super();
@@ -304,6 +319,16 @@ public class Jar extends Zip {
super.addFileset(fs);
}

/**
* @since Ant 1.7
*/
public void addConfiguredIndexJars(Path p) {
if (indexJars == null) {
indexJars = new Path(getProject());
}
indexJars.append(p);
}

protected void initZipOutputStream(ZipOutputStream zOut)
throws IOException, BuildException {

@@ -384,7 +409,7 @@ public class Jar extends Zip {
/**
* Create the index list to speed up classloading.
* This is a JDK 1.3+ specific feature and is enabled by default. See
* <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR+Index">
* <a href="http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index">
* the JAR index specification</a> for more details.
*
* @param zOut the zip stream representing the jar being built.
@@ -404,34 +429,38 @@ public class Jar extends Zip {
// header newline
writer.println(zipFile.getName());

// JarIndex is sorting the directories by ascending order.
// it's painful to do in JDK 1.1 and it has no value but cosmetic
// since it will be read into a hashtable by the classloader.
Enumeration e = addedDirs.keys();
while (e.hasMoreElements()) {
String dir = (String) e.nextElement();
writeIndexLikeList(new ArrayList(addedDirs.keySet()),
rootEntries, writer);
writer.println();

// try to be smart, not to be fooled by a weird directory name
// @fixme do we need to check for directories starting by ./ ?
dir = dir.replace('\\', '/');
int pos = dir.lastIndexOf('/');
if (pos != -1) {
dir = dir.substring(0, pos);
if (indexJars != null) {
Manifest mf = createManifest();
Manifest.Attribute classpath =
mf.getMainSection().getAttribute(Manifest.ATTRIBUTE_CLASSPATH);
String[] cpEntries = null;
if (classpath != null) {
StringTokenizer tok = new StringTokenizer(classpath.getValue(),
" ");
cpEntries = new String[tok.countTokens()];
int c = 0;
while (tok.hasMoreTokens()) {
cpEntries[c++] = tok.nextToken();
}
}

// 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;
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();
}
}
}
// name newline
writer.println(dir);
}

e = rootEntries.elements();
while (e.hasMoreElements()) {
writer.println(e.nextElement());
}

writer.flush();
@@ -672,4 +701,149 @@ public class Jar extends Zip {
return new String[] {"skip", "merge", "mergewithoutmain"};
}
}

/**
* Writes the directory entries from the first and the filenames
* from the second list to the given writer, one entry per line.
*
* @since Ant 1.7
*/
protected final void writeIndexLikeList(List dirs, List files,
PrintWriter writer)
throws IOException {
// 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());
}
}

/**
* try to guess the name of the given file.
*
* <p>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.</p>
*
* <p>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.</p>
*
* <p>if there is a classpath and the given file doesn't match any
* of its entries, return null.</p>
*
* @since Ant 1.7
*/
protected static final String findJarName(String fileName,
String[] classpath) {
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());
}

/**
* Grab lists of all root-level files and all directories
* contained in the given archive.
*
* @since Ant 1.7
*/
protected static final void grabFilesAndDirs(String file, List dirs,
List files)
throws IOException {
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();
}
}
}
}

+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/Java.java View File

@@ -417,6 +417,7 @@ public class Java extends Task {
failOnError = fail;
incompatibleWithSpawn |= fail;
}

/**
* The working directory of the process
*


+ 94
- 0
src/testcases/org/apache/tools/ant/taskdefs/ProtectedJarMethodsTest.java View File

@@ -0,0 +1,94 @@
/*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

package org.apache.tools.ant.taskdefs;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import org.apache.tools.ant.BuildFileTest;

/**
*/
public class ProtectedJarMethodsTest extends BuildFileTest {

private static String tempJar = "tmp.jar";

public ProtectedJarMethodsTest(String name) {
super(name);
}

public void setUp() {
configureProject("src/etc/testcases/taskdefs/jar.xml");
}

public void tearDown() {
executeTarget("cleanup");
}

public void testGrabFilesAndDirs() throws IOException {
executeTarget("testIndexTests");
String archive = getProject().resolveFile(tempJar).getAbsolutePath();
ArrayList dirs = new ArrayList();
ArrayList files = new ArrayList();
String[] expectedDirs = new String[] {
"sub/",
};
String[] expectedFiles = new String[] {
"foo",
};
Jar.grabFilesAndDirs(archive, dirs, files);
assertEquals(expectedDirs.length, dirs.size());
for (int i = 0; i < expectedDirs.length; i++) {
assertTrue("Found " + expectedDirs[i],
dirs.contains(expectedDirs[i]));
}
assertEquals(expectedFiles.length, files.size());
for (int i = 0; i < expectedFiles.length; i++) {
assertTrue("Found " + expectedFiles[i],
files.contains(expectedFiles[i]));
}
}

public void testFindJarNameNoClasspath() {
assertEquals("foo", Jar.findJarName("foo", null));
assertEquals("foo", Jar.findJarName("lib" + File.separatorChar + "foo",
null));
}

public void testFindJarNameNoMatch() {
assertNull(Jar.findJarName("foo", new String[] {"bar"}));
}

public void testFindJarNameSimpleMatches() {
assertEquals("foo", Jar.findJarName("foo", new String[] {"foo"}));
assertEquals("lib/foo", Jar.findJarName("foo",
new String[] {"lib/foo"}));
assertEquals("foo", Jar.findJarName("bar" + File.separatorChar + "foo",
new String[] {"foo"}));
assertEquals("lib/foo",
Jar.findJarName("bar" + File.separatorChar + "foo",
new String[] {"lib/foo"}));
}

public void testFindJarNameLongestMatchWins() {
assertEquals("lib/foo",
Jar.findJarName("lib/foo",
new String[] {"foo", "lib/foo",
"lib/bar/foo"}));
}
}

Loading…
Cancel
Save