Repacking jar files...

S

Simon Brooke

To configure webapps for delivery, I'm working on a configurer which

1: makes a copy of the distribution war file and unpacks it
2: makes substitutions in key configuration files, usually WEB-INF/web.xml
3: repacks the war file.

Unfortunately I'm having problems with repacking the war file. Quick reminder
- a war file is essentially a jar file containing a webapp, so if you've
exerience of repacking jar files you can probably help.

The symproms of the problem are

I do create a file, but

* When unpacked with ark (the KDE archiver) v2.1.9 all the contents can
be read and the archive appears perfect.

* When unpacked with jar as distributed in Sun j2sdk1.4.0, nothing is
unpacked and I get

java.util.zip.ZipException: missing entry name
at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:223)
at java.util.zip.ZipInputStream.getNextEntry(ZipInputStream.java:72)
at sun.tools.jar.Main.extract(Main.java:676)
at sun.tools.jar.Main.run(Main.java:190)
at sun.tools.jar.Main.main(Main.java:904)

* When unpacked with Info-ZIP's zip v2.3, it seems to unpack alright but
things which should be directories come out as empty files.

* When unpacked with Win-ZIP

* When installed into tomcat 4, things in the root directory of the
webapp can be accessed, but nothing else can be, so I'm assuming that
it's doing very much the same thing as Info-ZIP is doing.

Anyone got any helpful insights? I can't see anything in the API which
allows me to mark a JarEntry as a directory, and I can't account for the
'missing entry name' problem.

The code I am using to repack the war file is as follows:

/**
* repack the contents of the specified directory into a WAR archive file
*
* @param dir the directory to pack
*
* @throws IOException if anything goes wrong
*/
protected void repack( Context context, File dir )
throws IOException
{
File oldMeta =
new File( dir, "META-INF" /* is this JarFile.MANIFEST_NAME? */ );

if ( oldMeta.exists( ) )
{
// TODO: should we do aything to carry forward the contents of oldMeta? If so what?
deleteDir( oldMeta );
}

File pack =
new File( dir.getParent( ), context.get( APPNAMETOKEN ) + ".war" );

JarOutputStream jout =
new JarOutputStream( new FileOutputStream( pack ) );

Stack toWrite = new Stack( );

toWrite.push( dir );

while ( !toWrite.empty( ) )
{
File current = (File) toWrite.pop( );

jout.putNextEntry( new JarEntry( getRelativePath( current, dir ) ) );

if ( current.isDirectory( ) )
{
/** push all my files and subdirs onto the stack */
String[] children = current.list( );

for ( int child = 0; child < children.length; child++ )
{
toWrite.push( new File( current, children[child] ) );
}
}
else
{
/** write contents of file to stream */
InputStream in =
new BufferedInputStream( new FileInputStream( current ) );

for ( int i = in.read( ); i > -1; i = in.read( ) )
jout.write( i );
}
}

jout.flush( );
jout.close( );

/* compute the URL of the newly created delivery package and cache
* it in the context. There must be a better way of doing this. */
HttpServletRequest req =
(HttpServletRequest) context.get( REQUESTMAGICTOKEN );
ServletContext sc = getServletContext( );

URL requrl = new URL( req.getRequestURL( ).toString( ) );
File root = new File( sc.getRealPath( "" ) );

context.put( DELIVERYPACKAGEURLTOKEN,
new URL( requrl.getProtocol( ), requrl.getHost( ),
requrl.getPort( ),
sc.getServletContextName( ) + File.separator +
getRelativePath( pack, root ) ) );
}

/**
* get the relative path of file, presumed to be contained within dir or
* some subdirectory of dir, from dir
*
* @param file a file
* @param dir a directory which encloses it
*
* @return the relative path, or (for the time being) the canonical path
* if file is not within dir.
*/
private String getRelativePath( File file, File dir )
throws IOException
{
String result = file.getCanonicalPath( );

/*
* OK, we need the relative pathname of current with respect to dir
* this is a hack. TODO: find a guaranteed safe way to do this
*/
String dirCanonical = dir.getCanonicalPath( );
String currentCanonical = file.getCanonicalPath( );

if ( currentCanonical.indexOf( dirCanonical ) == 0 )
{
/* i.e. if the canonical name of the current file starts with
* the canonical name of the directory we're packing - which
* it should */
result = currentCanonical.substring( dirCanonical.length( ) );
}

if ( result.startsWith( File.separator ) )
{
result = result.substring( 1 );
}

return result;
}
 
A

Andrew Thompson

On Fri, 14 May 2004 14:35:01 GMT, Simon Brooke wrote:

(problems with .WAR)
* When unpacked with ark (the KDE archiver) v2.1.9 all the contents can
be read and the archive appears perfect.
*

* When unpacked with jar as distributed in Sun j2sdk1.4.0, nothing is
unpacked and I get

java.util.zip.ZipException: missing entry name
at java.util.zip.ZipInputStream.readLOC(ZipInputStream.java:223) ....
* When unpacked with Info-ZIP's zip v2.3, it seems to unpack alright but
things which should be directories come out as empty files.

Those last two result make it sound like the Jar's
TOC is corrupt. For further info. on Zip
ToC's, see in this recent thread..
<http://google.com/[email protected]&rnum=25>
(Check especially Chris Uppal's detailed explanation).

* From that it seems KDE Archiver is utterly
ignoring the ToC and reading each file out
in order. I would say use it to extract the
bad file.

Just a thought.
 
C

Chris Uppal

Simon said:
if you've
exerience of repacking jar files you can probably help.

I'm not sure that will is address the problem you are seeing. But I've seen no
other replies, so...

It might be that you are generating a malformed "Zip" file by attempting to
create directory entries.

The most common convention (there doesn't seem to be a standard) is not to
include entries for any directories at all. So it's up to the decoding
application to infer their existence from the names of any files that are "in"
them.

A second convention is to create entries with a trailing '/' in their names,
and no contents. This is more likely to work (not confuse other zip
applications) if you also set the so-called "internal attributes" to the MSDOS
directory marker, however the Java API's provide no access to that. Note that
writing 0 bytes to the contents is NOT the same thing as having no contents if
the entry is compressed, since compressing a 0-length string produces a couple
of bytes.

So I suggest you don't generate directory entries at all, or -- if that doesn't
work for some reason -- then you fall back to creating them *uncompressed* and
with a trailing '/' to the name.

-- chris
 
R

Roedy Green

It might be that you are generating a malformed "Zip" file by attempting to
create directory entries.

The decoder thinks they are files and creates them. That prevents it
from creating directories later of that same name.

Check the zip spec to see if there is a directory attribute bit.

Anyway,just leave out the directories. Every decoder automatically
creates them as needed.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,055
Latest member
SlimSparkKetoACVReview

Latest Threads

Top