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;
}
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;
}