jnlp.sample.jardiff.JarDiffPatcher Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of webstart-jnlp-servlet Show documentation
Show all versions of webstart-jnlp-servlet Show documentation
JNLP Sample servlet that supports pack200 protocol. Taken from Sun's JDK sample/jnlp directory thanks to
its permissive license.
The newest version!
/*
* @(#)JarDiffPatcher.java 1.7 05/11/17
*
* Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* -Redistribution of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* -Redistribution 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.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MIDROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed, licensed or intended
* for use in the design, construction, operation or maintenance of any
* nuclear facility.
*/
package jnlp.sample.jardiff;
import java.io.*;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipEntry;
/**
* JarDiff is able to create a jar file containing the delta between two
* jar files (old and new). The delta jar file can then be applied to the
* old jar file to reconstruct the new jar file.
*
* Refer to the JNLP spec for details on how this is done.
*
* @version 1.11, 06/26/03
*/
public class JarDiffPatcher
implements JarDiffConstants, Patcher
{
private static final int DEFAULT_READ_SIZE = 2048;
private static byte[] newBytes = new byte[DEFAULT_READ_SIZE];
private static byte[] oldBytes = new byte[DEFAULT_READ_SIZE];
private static ResourceBundle _resources = JarDiff.getResources();
public static ResourceBundle getResources()
{
return JarDiff.getResources();
}
public void applyPatch( Patcher.PatchDelegate delegate, String oldJarPath, String jarDiffPath, OutputStream result )
throws IOException
{
File oldFile = new File( oldJarPath );
File diffFile = new File( jarDiffPath );
JarOutputStream jos = new JarOutputStream( result );
JarFile oldJar = new JarFile( oldFile );
JarFile jarDiff = new JarFile( diffFile );
Set ignoreSet = new HashSet();
Map renameMap = new HashMap();
determineNameMapping( jarDiff, ignoreSet, renameMap );
// get all keys in renameMap
Object[] keys = renameMap.keySet().toArray();
// Files to implicit move
Set oldjarNames = new HashSet();
Enumeration oldEntries = oldJar.entries();
if ( oldEntries != null )
{
while ( oldEntries.hasMoreElements() )
{
oldjarNames.add( ( (JarEntry) oldEntries.nextElement() ).getName() );
}
}
// size depends on the three parameters below, which is
// basically the counter for each loop that do the actual
// writes to the output file
// since oldjarNames.size() changes in the first two loop
// below, we need to adjust the size accordingly also when
// oldjarNames.size() changes
double size = oldjarNames.size() + keys.length + jarDiff.size();
double currentEntry = 0;
// Handle all remove commands
oldjarNames.removeAll( ignoreSet );
size -= ignoreSet.size();
// Add content from JARDiff
Enumeration entries = jarDiff.entries();
if ( entries != null )
{
while ( entries.hasMoreElements() )
{
JarEntry entry = (JarEntry) entries.nextElement();
if ( !INDEX_NAME.equals( entry.getName() ) )
{
updateDelegate( delegate, currentEntry, size );
currentEntry++;
writeEntry( jos, entry, jarDiff );
// Remove entry from oldjarNames since no implicit
//move is needed
boolean wasInOld = oldjarNames.remove( entry.getName() );
// Update progress counters. If it was in old, we do
// not need an implicit move, so adjust total size.
if ( wasInOld )
{
size--;
}
}
else
{
// no write is done, decrement size
size--;
}
}
}
// go through the renameMap and apply move for each entry
for ( int j = 0; j < keys.length; j++ )
{
// Apply move command
String newName = (String) keys[j];
String oldName = (String) renameMap.get( newName );
// Get source JarEntry
JarEntry oldEntry = oldJar.getJarEntry( oldName );
if ( oldEntry == null )
{
String moveCmd = MOVE_COMMAND + oldName + " " + newName;
handleException( "jardiff.error.badmove", moveCmd );
}
// Create dest JarEntry
JarEntry newEntry = new JarEntry( newName );
newEntry.setTime( oldEntry.getTime() );
newEntry.setSize( oldEntry.getSize() );
newEntry.setCompressedSize( oldEntry.getCompressedSize() );
newEntry.setCrc( oldEntry.getCrc() );
newEntry.setMethod( oldEntry.getMethod() );
newEntry.setExtra( oldEntry.getExtra() );
newEntry.setComment( oldEntry.getComment() );
updateDelegate( delegate, currentEntry, size );
currentEntry++;
writeEntry( jos, newEntry, oldJar.getInputStream( oldEntry ) );
// Remove entry from oldjarNames since no implicit
//move is needed
boolean wasInOld = oldjarNames.remove( oldName );
// Update progress counters. If it was in old, we do
// not need an implicit move, so adjust total size.
if ( wasInOld )
{
size--;
}
}
// implicit move
Iterator iEntries = oldjarNames.iterator();
if ( iEntries != null )
{
while ( iEntries.hasNext() )
{
String name = (String) iEntries.next();
JarEntry entry = oldJar.getJarEntry( name );
updateDelegate( delegate, currentEntry, size );
currentEntry++;
writeEntry( jos, entry, oldJar );
}
}
updateDelegate( delegate, currentEntry, size );
jos.finish();
}
private void updateDelegate( Patcher.PatchDelegate delegate, double currentSize, double size )
{
if ( delegate != null )
{
delegate.patching( (int) ( currentSize / size ) );
}
}
private void determineNameMapping( JarFile jarDiff, Set ignoreSet, Map renameMap )
throws IOException
{
InputStream is = jarDiff.getInputStream( jarDiff.getEntry( INDEX_NAME ) );
if ( is == null )
{
handleException( "jardiff.error.noindex", null );
}
LineNumberReader indexReader = new LineNumberReader( new InputStreamReader( is, "UTF-8" ) );
String line = indexReader.readLine();
if ( line == null || !line.equals( VERSION_HEADER ) )
{
handleException( "jardiff.error.badheader", line );
}
while ( ( line = indexReader.readLine() ) != null )
{
if ( line.startsWith( REMOVE_COMMAND ) )
{
List sub = getSubpaths( line.substring( REMOVE_COMMAND.length() ) );
if ( sub.size() != 1 )
{
handleException( "jardiff.error.badremove", line );
}
ignoreSet.add( sub.get( 0 ) );
}
else if ( line.startsWith( MOVE_COMMAND ) )
{
List sub = getSubpaths( line.substring( MOVE_COMMAND.length() ) );
if ( sub.size() != 2 )
{
handleException( "jardiff.error.badmove", line );
}
// target of move should be the key
if ( renameMap.put( sub.get( 1 ), sub.get( 0 ) ) != null )
{
// invalid move - should not move to same target twice
handleException( "jardiff.error.badmove", line );
}
}
else if ( line.length() > 0 )
{
handleException( "jardiff.error.badcommand", line );
}
}
}
private void handleException( String errorMsg, String line )
throws IOException
{
try
{
throw new IOException( getResources().getString( errorMsg ) + " " + line );
}
catch ( MissingResourceException mre )
{
System.err.println( "Fatal error: " + errorMsg );
new Throwable().printStackTrace( System.err );
System.exit( -1 );
}
}
private List getSubpaths( String path )
{
int index = 0;
int length = path.length();
ArrayList sub = new ArrayList();
while ( index < length )
{
while ( index < length && Character.isWhitespace( path.charAt( index ) ) )
{
index++;
}
if ( index < length )
{
int start = index;
int last = start;
String subString = null;
while ( index < length )
{
char aChar = path.charAt( index );
if ( aChar == '\\' && ( index + 1 ) < length && path.charAt( index + 1 ) == ' ' )
{
if ( subString == null )
{
subString = path.substring( last, index );
}
else
{
subString += path.substring( last, index );
}
last = ++index;
}
else if ( Character.isWhitespace( aChar ) )
{
break;
}
index++;
}
if ( last != index )
{
if ( subString == null )
{
subString = path.substring( last, index );
}
else
{
subString += path.substring( last, index );
}
}
sub.add( subString );
}
}
return sub;
}
private void writeEntry( JarOutputStream jos, JarEntry entry, JarFile file )
throws IOException
{
writeEntry( jos, entry, file.getInputStream( entry ) );
}
private void writeEntry( JarOutputStream jos, JarEntry entry, InputStream data )
throws IOException
{
//Create a new ZipEntry to clear the compressed size. 5079423
jos.putNextEntry( new ZipEntry( entry.getName() ) );
// Read the entry
int size = data.read( newBytes );
while ( size != -1 )
{
jos.write( newBytes, 0, size );
size = data.read( newBytes );
}
data.close();
}
}