Browse Source

New task <jlink>.

Many thanks to Patrick C. Beard <beard@netscape.com> for allowing his
original jlink code to be placed under the Apache Software License.

Submitted by:	Matthew Kuperus Heun <matthew.k.heun@gaerospace.com>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267982 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 25 years ago
parent
commit
f6745b24f5
6 changed files with 932 additions and 0 deletions
  1. +1
    -0
      docs/index.html
  2. +138
    -0
      docs/jlink.html
  3. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/defaults.properties
  4. +161
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java
  5. +211
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java
  6. +420
    -0
      src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java

+ 1
- 0
docs/index.html View File

@@ -3865,6 +3865,7 @@ it had been located at <code>htdocs/manual/ChangeLog.txt</code>.</p>
<ul>
<li><a href="#cab">Cab</a></li>
<li><a href="#ftp">FTP</a></li>
<li><a href="jlink.html">Jlink</a></li>
<li><a href="junit.html">JUnit</a></li>
<li><a href="#netrexxc">NetRexxC</a></li>
<li><a href="#renameexts">RenameExtensions</a></li>


+ 138
- 0
docs/jlink.html View File

@@ -0,0 +1,138 @@
<html>
<head>
</head>
<body>

<h2><a name="jlink">Jlink</a></h2>
<h3><b>Description:</b></h3>
<p>Links entries from sub-builds and libraries.</p>

<p>The jlink task can be used to build jar and zip files, similar to
the <i>jar</i> task.
However, jlink provides options for controlling the way entries from
input files
are added to the output file. Specifically, capabilities for merging
entries from
multiple zip or jar files is available.</p>

<p>If a mergefile is specified directly (eg. at the top level of a
<i>mergefiles</i>
pathelement) <i>and</i> the mergefile ends in &quot;.zip&quot; or
&quot;.jar&quot;,
entries in the mergefile will be merged into the outfile. A file with
any other extension
will be added to the output file, even if it is specified in the
mergefiles element.
Directories specified in either the mergefiles or addfiles element
are added to the
output file as you would expect: all files in subdirectories are
recursively added to
the output file with appropriate prefixes in the output file
(without merging).
</p>

<p>
In the case where duplicate entries and/or files are found among the
files to be merged or
added, jlink merges or adds the first entry and ignores all subsequent entries.
</p>

<p>
jlink ignores META-INF directories in mergefiles. Users should supply their
own manifest information for the output file.
</p>

<p>It is possible to refine the set of files that are being jlinked.
This can be
done with the <i>includes</i>, <i>includesfile</i>, <i>excludes</i>,
<i>excludesfile</i>,
and <i>defaultexcludes</i> attributes on the <i>addfiles</i> and
<i>mergefiles</i>
nested elements. With the <i>includes</i> or <i>includesfile</i>
attribute you specify the files you want to have included by using patterns.
The <i>exclude</i> or <i>excludesfile</i> attribute is used to specify
the files you want to have excluded. This is also done with patterns. And
finally with the <i>defaultexcludes</i> attribute, you can specify whether you
want to use default exclusions or not. See the section on <a
href="#directorybasedtasks">directory based tasks</a>, on how the
inclusion/exclusion of files works, and how to write patterns. The patterns are
relative to the <i>base</i> directory.</p>



<h3>Parameters:</h3>
<table border="1" cellpadding="2" cellspacing="0">
<tr>
<td valign="top"><b>Attribute</b></td>
<td valign="top"><b>Description</b></td>
<td align="center" valign="top"><b>Required</b></td>
</tr>
<tr>
<td valign="top">outfile</td>
<td valign="top">the path of the output file.</td>
<td valign="top" align="center">Yes</td>
</tr>
<tr>
<td valign="top">compress</td>
<td valign="top">whether or not the output should be compressed.
<i>true</i>,
<i>yes</i>, or <i>on</i> result in compressed output.
If omitted, output will be uncompressed (inflated).</td>
<td valign="top" align="center">No</td>
</tr>
<tr>
<td valign="top">mergefiles</td>
<td valign="top">files to be merged into the output, if possible.</td>
<td valign="middle" align="middle" rowspan="2">At least one of
mergefiles or addfiles</td>
</tr>
<tr>
<td valign="top">addfiles</td>
<td valign="top">files to be added to the output.</td>
</tr>
</table>

<h3>Examples</h3>

The following will merge the entries in mergefoo.jar and mergebar.jar
into out.jar.
mac.jar and pc.jar will be added as single entries to out.jar.
<pre>
&lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
&lt;mergefiles&gt;
&lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
&lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
&lt;/mergefiles&gt;
&lt;addfiles&gt;
&lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
&lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
&lt;/addfiles&gt;
&lt;/jlink&gt;
</pre>

Suppose the file foo.jar contains two entries: bar.class and
barnone/myClass.zip.
Suppose the path for file foo.jar is build/tempbuild/foo.jar. The
following example
will provide the entry tempbuild/foo.jar in the out.jar.
<pre>
&lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
&lt;mergefiles&gt;
&lt;pathelement path=&quot;build/tempbuild&quot;/&gt;
&lt;/mergefiles&gt;
&lt;/jlink&gt;
</pre>

However, the next example would result in two top-level entries in out.jar,
namely bar.class and barnone/myClass.zip
<pre>
&lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
&lt;mergefiles&gt;
&lt;pathelement path=&quot;build/tempbuild/foo.jar&quot;/&gt;
&lt;/mergefiles&gt;
&lt;/jlink&gt;
</pre>

</body>

</html>

+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/defaults.properties View File

@@ -60,6 +60,7 @@ ftp=org.apache.tools.ant.taskdefs.optional.FTP
javacc=org.apache.tools.ant.taskdefs.optional.javacc.JavaCC
jjtree=org.apache.tools.ant.taskdefs.optional.javacc.JJTree
starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut
jlink=org.apache.tools.ant.taskdefs.optional.jlink.JlinkTask

# deprecated ant tasks (kept for back compatibility)
javadoc2=org.apache.tools.ant.taskdefs.Javadoc

+ 161
- 0
src/main/org/apache/tools/ant/taskdefs/optional/jlink/ClassNameReader.java View File

@@ -0,0 +1,161 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant.taskdefs.optional.jlink;

import java.io .*;

/**
* Reads just enough of a class file to determine the class' full name.
*
* <p>Extremely minimal constant pool implementation, mainly to support extracting
* strings from a class file.
* @author <a href="mailto:beard@netscape.com">Patrick C. Beard</a>.
*/
class ConstantPool extends Object{

static final
byte UTF8 = 1, UNUSED = 2, INTEGER = 3, FLOAT = 4, LONG = 5, DOUBLE = 6,
CLASS = 7, STRING = 8, FIELDREF = 9, METHODREF = 10,
INTERFACEMETHODREF = 11, NAMEANDTYPE = 12;

byte[] types;

Object[] values;

ConstantPool( DataInput data ) throws IOException {
super();

int count = data .readUnsignedShort();
types = new byte [ count ];
values = new Object [ count ];
// read in all constant pool entries.
for ( int i = 1; i < count; i++ ) {
byte type = data .readByte();
types[i] = type;
switch (type)
{
case UTF8 :
values[i] = data .readUTF();
break;
case UNUSED :
break;
case INTEGER :
values[i] = new Integer( data .readInt() );
break;
case FLOAT :
values[i] = new Float( data .readFloat() );
break;
case LONG :
values[i] = new Long( data .readLong() );
++i;
break;
case DOUBLE :
values[i] = new Double( data .readDouble() );
++i;
break;
case CLASS :
case STRING :
values[i] = new Integer( data .readUnsignedShort() );
break;
case FIELDREF :
case METHODREF :
case INTERFACEMETHODREF :
case NAMEANDTYPE :
values[i] = new Integer( data .readInt() );
break;
}
}
}


}
/**
* Provides a quick and dirty way to determine the true name of a class
* given just an InputStream. Reads in just enough to perform this
* minimal task only.
*/
public class ClassNameReader extends Object{

public static
String getClassName( InputStream input ) throws IOException {
DataInputStream data = new DataInputStream( input );
// verify this is a valid class file.
int cookie = data .readInt();
if ( cookie != 0xCAFEBABE ) {
return null;
}
int version = data .readInt();
// read the constant pool.
ConstantPool constants = new ConstantPool( data );
Object[] values = constants .values;
// read access flags and class index.
int accessFlags = data .readUnsignedShort();
int classIndex = data .readUnsignedShort();
Integer stringIndex = (Integer) values[classIndex];
String className = (String) values[stringIndex .intValue()];
return className;
}


}



+ 211
- 0
src/main/org/apache/tools/ant/taskdefs/optional/jlink/JlinkTask.java View File

@@ -0,0 +1,211 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.taskdefs.optional.jlink;

import org.apache.tools.ant .*;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types .*;
import java.io.File;

/**
* This class defines objects that can link together various jar and
* zip files.
*
* <p>It is basically a wrapper for the jlink code written originally
* by <a href="mailto:beard@netscape.com">Patrick Beard</a>. The
* classes org.apache.tools.ant.taskdefs.optional.jlink.Jlink and
* org.apache.tools.ant.taskdefs.optional.jlink.ClassNameReader
* support this class.</p>
*
* <p>For example:
* <code>
* <pre>
* &lt;jlink compress=&quot;false&quot; outfile=&quot;out.jar&quot;/&gt;
* &lt;mergefiles&gt;
* &lt;pathelement path=&quot;${build.dir}/mergefoo.jar&quot;/&gt;
* &lt;pathelement path=&quot;${build.dir}/mergebar.jar&quot;/&gt;
* &lt;/mergefiles&gt;
* &lt;addfiles&gt;
* &lt;pathelement path=&quot;${build.dir}/mac.jar&quot;/&gt;
* &lt;pathelement path=&quot;${build.dir}/pc.zip&quot;/&gt;
* &lt;/addfiles&gt;
* &lt;/jlink&gt;
* </pre>
* </code>
*
* @author <a href="mailto:matthew.k.heun@gaerospace.com">Matthew Kuperus Heun</a> */
public class JlinkTask extends MatchingTask {

/**
* The output file for this run of jlink. Usually a jar or zip file.
*/
public void setOutfile( File outfile ) {
this.outfile = outfile;
}

/**
* Establishes the object that contains the files to
* be merged into the output.
*/
public Path createMergefiles() {
if ( this .mergefiles == null ) {
this .mergefiles = new Path(getProject());
}
return this .mergefiles.createPath();
}

/**
* Sets the files to be merged into the output.
*/
public void setMergefiles( Path mergefiles ) {
if ( this .mergefiles == null ) {
this .mergefiles = mergefiles;
}
else {
this .mergefiles .append( mergefiles );
}
}

/**
* Establishes the object that contains the files to
* be added to the output.
*/
public Path createAddfiles() {
if ( this .addfiles == null ) {
this .addfiles = new Path(getProject());
}
return this .addfiles .createPath();
}

/**
* Sets the files to be added into the output.
*/
public void setAddfiles( Path addfiles ) {
if ( this .addfiles == null ) {
this .addfiles = addfiles;
}
else {
this .addfiles .append( addfiles );
}
}

/**
* Defines whether or not the output should be compacted.
*/
public void setCompress( boolean compress ) {
this .compress = compress;
}

/**
* Does the adding and merging.
*/
public void execute() throws BuildException {
//Be sure everything has been set.
if ( outfile == null ) {
throw new BuildException( "outfile attribute is required! Please set." );
}
if (!haveAddFiles() && !haveMergeFiles()) {
throw new BuildException( "addfiles or mergefiles required! Please set." );
}
log( "linking: " + outfile.getPath() );
log( "compression: " + compress, Project.MSG_VERBOSE );
jlink linker = new jlink();
linker .setOutfile( outfile.getPath() );
linker .setCompression( compress );
if (haveMergeFiles()){
log( "merge files: " + mergefiles .toString(), Project .MSG_VERBOSE );
linker .addMergeFiles( mergefiles .list() );
}
if (haveAddFiles()){
log( "add files: " + addfiles .toString(), Project .MSG_VERBOSE );
linker .addAddFiles( addfiles .list() );
}
try {
linker .link();
} catch( Exception ex ) {
throw new BuildException( ex, location );
}
}
private boolean haveAddFiles(){
return haveEntries(addfiles);
}

private boolean haveMergeFiles(){
return haveEntries(mergefiles);
}
private boolean haveEntries(Path p){
if (p == null){
return false;
}
if (p.size() > 0){
return true;
}
return false;
}

private File outfile = null;

private Path mergefiles = null;

private Path addfiles = null;

private boolean compress = false;

private String ps = System .getProperty( "path.separator" );

}



+ 420
- 0
src/main/org/apache/tools/ant/taskdefs/optional/jlink/jlink.java View File

@@ -0,0 +1,420 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

/**
* jlink.java
* links together multiple .jar files
*
* Original code by Patrick Beard. Modifications to work
* with ANT by Matthew Kuperus Heun.
*
* @author <a href="mailto:beard@netscape.com>Patrick C. Beard</a>.
* @author <a href="mailto:matthew.k.heun@gaerospace.com>Matthew Kuperus Heun</a>
*/
package org.apache.tools.ant.taskdefs.optional.jlink;

import java.io .*;
import java.util.zip .*;
import java.util .Vector;
import java.util .Enumeration;

public class jlink extends Object{

/**
* The file that will be created by this instance of jlink.
*/
public void setOutfile( String outfile ) {
if ( outfile == null ) {
return ;
}
this .outfile = outfile;
}

/**
* Adds a file to be merged into the output.
*/
public void addMergeFile( String mergefile ) {
if ( mergefile == null ) {
return ;
}
mergefiles .addElement( mergefile );
}

/**
* Adds a file to be added into the output.
*/
public void addAddFile( String addfile ) {
if ( addfile == null ) {
return ;
}
addfiles .addElement( addfile );
}

/**
* Adds several files to be merged into the output.
*/
public void addMergeFiles( String[] mergefiles ) {
if ( mergefiles == null ) {
return ;
}
for ( int i = 0; i < mergefiles .length; i++ ) {
addMergeFile( mergefiles[i] );
}
}

/**
* Adds several file to be added into the output.
*/
public void addAddFiles( String[] addfiles ) {
if ( addfiles == null ) {
return ;
}
for ( int i = 0; i < addfiles .length; i++ ) {
addAddFile( addfiles[i] );
}
}

/**
* Determines whether output will be compressed.
*/
public void setCompression( boolean compress ) {
this .compression = compress;
}

/**
* Performs the linking of files.
* Addfiles are added to the output as-is. For example, a
* jar file is added to the output as a jar file.
* However, mergefiles are first examined for their type.
* If it is a jar or zip file, the contents will be extracted
* from the mergefile and entered into the output.
* If a zip or jar file is encountered in a subdirectory
* it will be added, not merged.
* If a directory is encountered, it becomes the root
* entry of all the files below it. Thus, you can
* provide multiple, disjoint directories, as
* addfiles: they will all be added in a rational
* manner to outfile.
*/
public void link() throws Exception {
ZipOutputStream output = new ZipOutputStream( new FileOutputStream( outfile ) );
if ( compression ) {
output .setMethod( ZipOutputStream .DEFLATED );
output .setLevel( Deflater .DEFAULT_COMPRESSION );
} else {
output .setMethod( ZipOutputStream .STORED );
}
Enumeration merges = mergefiles .elements();
while ( merges .hasMoreElements() ) {
String path = (String) merges .nextElement();
File f = new File( path );
if ( f.getName().endsWith( ".jar" ) || f.getName().endsWith( ".zip" ) ) {
//Do the merge
mergeZipJarContents( output, f );
}
else {
//Add this file to the addfiles Vector and add it later at the top level of the output file.
addAddFile( path );
}
}
Enumeration adds = addfiles .elements();
while ( adds .hasMoreElements() ) {
String name = (String) adds .nextElement();
File f = new File( name );
if ( f .isDirectory() ) {
//System.out.println("in jlink: adding directory contents of " + f.getPath());
addDirContents( output, f, f.getName() + '/', compression );
}
else {
addFile( output, f, "", compression );
}
}
if ( output != null ) {
try {
output .close();
} catch( IOException ioe ) {}
}
}

public static void main( String[] args ) {
// jlink output input1 ... inputN
if ( args .length < 2 ) {
System .out .println( "usage: jlink output input1 ... inputN" );
System .exit( 1 );
}
jlink linker = new jlink();
linker .setOutfile( args[0] );
//To maintain compatibility with the command-line version, we will only add files to be merged.
for ( int i = 1; i < args .length; i++ ) {
linker .addMergeFile( args[i] );
}
try {
linker .link();
} catch( Exception ex ) {
System .err .print( ex .getMessage() );
}
}

/*
* Actually performs the merging of f into the output.
* f should be a zip or jar file.
*/
private void mergeZipJarContents( ZipOutputStream output, File f ) throws IOException {
//Check to see that the file with name "name" exists.
if ( ! f .exists() ) {
return ;
}
ZipFile zipf = new ZipFile( f );
Enumeration entries = zipf.entries();
while (entries.hasMoreElements()){
ZipEntry inputEntry = (ZipEntry) entries.nextElement();
//Ignore manifest entries. They're bound to cause conflicts between
//files that are being merged. User should supply their own
//manifest file when doing the merge.
String inputEntryName = inputEntry.getName();
int index = inputEntryName.indexOf("META-INF");
if (index < 0){
//META-INF not found in the name of the entry. Go ahead and process it.
try {
output.putNextEntry(processEntry(zipf, inputEntry));
} catch (ZipException ex){
//If we get here, it could be because we are trying to put a
//directory entry that already exists.
//For example, we're trying to write "com", but a previous
//entry from another mergefile was called "com".
//In that case, just ignore the error and go on to the
//next entry.
String mess = ex.getMessage();
if (mess.indexOf("duplicate") > 0){
//It was the duplicate entry.
continue;
} else {
//I hate to admit it, but we don't know what happened here. Throw the Exception.
throw ex;
}
}
InputStream in = zipf.getInputStream(inputEntry);
int len = buffer.length;
int count = -1;
while ((count = in.read(buffer, 0, len)) > 0){
output.write(buffer, 0, count);
}
in.close();
output.closeEntry();
}
}
zipf .close();
}

/*
* Adds contents of a directory to the output.
*/
private void addDirContents( ZipOutputStream output, File dir, String prefix, boolean compress ) throws IOException {
String[] contents = dir .list();
for ( int i = 0; i < contents .length; ++i ) {
String name = contents[i];
File file = new File( dir, name );
if ( file .isDirectory() ) {
addDirContents( output, file, prefix + name + '/', compress );
}
else {
addFile( output, file, prefix, compress );
}
}
}

/*
* Gets the name of an entry in the file. This is the real name
* which for a class is the name of the package with the class
* name appended.
*/
private String getEntryName( File file, String prefix ) {
String name = file .getName();
if ( ! name .endsWith( ".class" ) ) {
// see if the file is in fact a .class file, and determine its actual name.
try {
InputStream input = new FileInputStream( file );
String className = ClassNameReader .getClassName( input );
input .close();
if ( className != null ) {
return className .replace( '.', '/' ) + ".class";
}
} catch( IOException ioe ) {}
}
System.out.println("From " + file.getPath() + " and prefix " + prefix + ", creating entry " + prefix+name);
return (prefix + name);
}

/*
* Adds a file to the output stream.
*/
private void addFile( ZipOutputStream output, File file, String prefix, boolean compress) throws IOException {
//Make sure file exists
long checksum = 0;
if ( ! file .exists() ) {
return ;
}
ZipEntry entry = new ZipEntry( getEntryName( file, prefix ) );
entry .setTime( file .lastModified() );
entry .setSize( file .length() );
if (! compress){
entry.setCrc(calcChecksum(file));
}
FileInputStream input = new FileInputStream( file );
addToOutputStream(output, input, entry);
}
/*
* A convenience method that several other methods might call.
*/
private void addToOutputStream(ZipOutputStream output, InputStream input, ZipEntry ze) throws IOException{
try {
output.putNextEntry(ze);
} catch (ZipException zipEx) {
//This entry already exists. So, go with the first one.
input.close();
return;
}
int numBytes = -1;
while((numBytes = input.read(buffer)) > 0){
output.write(buffer, 0, numBytes);
}
output.closeEntry();
input.close();
}

/*
* A method that does the work on a given entry in a mergefile.
* The big deal is to set the right parameters in the ZipEntry
* on the output stream.
*/
private ZipEntry processEntry( ZipFile zip, ZipEntry inputEntry ) throws IOException{
/*
First, some notes.
On MRJ 2.2.2, getting the size, compressed size, and CRC32 from the
ZipInputStream does not work for compressed (deflated) files. Those calls return -1.
For uncompressed (stored) files, those calls do work.
However, using ZipFile.getEntries() works for both compressed and
uncompressed files.
Now, from some simple testing I did, it seems that the value of CRC-32 is
independent of the compression setting. So, it should be easy to pass this
information on to the output entry.
*/
String name = inputEntry .getName();
if ( ! (inputEntry .isDirectory() || name .endsWith( ".class" )) ) {
try {
InputStream input = zip.getInputStream( zip .getEntry( name ) );
String className = ClassNameReader .getClassName( input );
input .close();
if ( className != null ) {
name = className .replace( '.', '/' ) + ".class";
}
} catch( IOException ioe ) {}
}
ZipEntry outputEntry = new ZipEntry( name );
outputEntry.setTime(inputEntry .getTime() );
outputEntry.setExtra(inputEntry.getExtra());
outputEntry.setComment(inputEntry.getComment());
outputEntry.setTime(inputEntry.getTime());
if (compression){
outputEntry.setMethod(ZipEntry.DEFLATED);
//Note, don't need to specify size or crc for compressed files.
} else {
outputEntry.setMethod(ZipEntry.STORED);
outputEntry.setCrc(inputEntry.getCrc());
outputEntry.setSize(inputEntry.getSize());
}
return outputEntry;
}
/*
* Necessary in the case where you add a entry that
* is not compressed.
*/
private long calcChecksum(File f) throws IOException {
BufferedInputStream in = new BufferedInputStream(new FileInputStream(f));
return calcChecksum(in, f.length());
}

/*
* Necessary in the case where you add a entry that
* is not compressed.
*/
private long calcChecksum(InputStream in, long size) throws IOException{
CRC32 crc = new CRC32();
int len = buffer.length;
int count = -1;
int haveRead = 0;
while((count=in.read(buffer, 0, len)) > 0){
haveRead += count;
crc.update(buffer, 0, count);
}
in.close();
return crc.getValue();
}

private String outfile = null;

private Vector mergefiles = new Vector( 10 );

private Vector addfiles = new Vector( 10 );

private boolean compression = false;
byte[] buffer = new byte[8192];

}



Loading…
Cancel
Save