blob: 018fb17c14d21c42eb3188fcdc5d7b188de20744 [file] [log] [blame]
/*
* Copyright 2005 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.jdo.impl.enhancer;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.zip.CRC32;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
/**
* This is a helper-class to perform some useful operations outside a
* byte code enhancer and delegate the real work to the enhancer.
*/
public class ClassFileEnhancerHelper
{
/**
* Enhances a classfile.
*
* @param enhancer The enhancer to delegate the work to.
* @param in The input stream with the Java class.
* @param out The output stream to write the enhanced class to.
*
* @return Has the input stream been enhanced?
*
* @exception EnhancerUserException If something went wrong.
* @exception EnhancerFatalError If something went wrong.
*
* @see ClassFileEnhancer#enhanceClassFile
*/
static public boolean enhanceClassFile(ClassFileEnhancer enhancer,
InputStream in,
OutputStream out)
throws EnhancerUserException,
EnhancerFatalError
{
return enhancer.enhanceClassFile(in, new OutputStreamWrapper(out));
}
/**
* Enhances a zip file. The zip file is given as a uip input stream.
* It's entries are read and - if necessary - individually enhanced.
* The output stream has the same compressíon (if any) as the input
* stream.
*
* @param enhancer The enhancer.
* @param zip_in The zip input stream.
* @param zip_out The zip output stream.
*
* @return <code>true</code> if at least one entry of the zip file has
* been enhanced, <code>false</code> otherwise.
*
* @exception EnhancerUserException If something went wrong.
* @exception EnhancerFatalError If something went wrong.
*
* @see ClassFileEnhancer#enhanceClassFile
*/
static public boolean enhanceZipFile(ClassFileEnhancer enhancer,
ZipInputStream zip_in,
ZipOutputStream zip_out)
throws EnhancerUserException,
EnhancerFatalError
{
boolean enhanced = false;
try {
CRC32 crc32 = new CRC32();
ZipEntry entry;
while ((entry = zip_in.getNextEntry()) != null) {
InputStream in = zip_in;
final ZipEntry out_entry = new ZipEntry(entry);
// try to enhance
if (isClassFileEntry(entry)) {
// enhance the classfile
// we have to copy the classfile, because if it won't be
// enhanced, the OutputStream is empty and we have to
// re-read the InputStream, which is impossible with a
// ZipInputStream (no mark/reset)
in = openZipEntry(zip_in);
in.mark(Integer.MAX_VALUE);
final ByteArrayOutputStream tmp
= new ByteArrayOutputStream();
if (enhancer.enhanceClassFile(in, tmp)) {
enhanced = true;
final byte[] bytes = tmp.toByteArray();
tmp.close();
in.close();
modifyZipEntry(out_entry, bytes, crc32);
in = new ByteArrayInputStream(bytes);
} else {
// the classfile has not been enhanced
in.reset();
}
}
// copy the entry
zip_out.putNextEntry(out_entry);
copyZipEntry(in, zip_out);
zip_out.closeEntry();
if (in != zip_in) {
in.close();
}
}
} catch (IOException ex) {
throw new EnhancerFatalError(ex);
}
return enhanced;
}
/**
* Copies a zip entry from one stream to another.
*
* @param in The inout stream.
* @param out The output stream.
*
* @exception IOException If the stream access failed.
*/
static private void copyZipEntry(InputStream in,
OutputStream out)
throws IOException
{
int b;
while ((in.available() > 0) && (b = in.read()) > -1) {
out.write(b);
}
}
/**
* Opens the next zip entry of a zip input stream and copies it to
* a <code>java.io.ByteArrayOutputStream</code>. It's byte array is made
* available via an <code>java.io.ByteArrayInputStream</code> which is
* returned.
*
* @param in The zip input stream.
*
* @return The newly created input stream with the next zip entry.
*
* @exception IOException If an I/O operation failed.
*/
static private InputStream openZipEntry(ZipInputStream in)
throws IOException
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
copyZipEntry(in, out);
return new ByteArrayInputStream(out.toByteArray());
}
/**
* Modifies the given zip entry so that it can be added to zip file.
* The given zip entry represents an enhanced class, so the zip entry
* has to get the correct size and checksum (but only if the entry won't
* be compressed).
*
* @param entry The zip entry to modify.
* @param bytes The uncompressed byte representation of the classfile.
* @param crc32 The checksum evaluator.
*/
private static void modifyZipEntry(ZipEntry entry,
byte [] bytes,
CRC32 crc32)
{
entry.setSize(bytes.length);
if (entry.getMethod() == 0) {
//no compression (ZipInputStream.STORED - not accessible)
crc32.reset();
crc32.update(bytes);
entry.setCrc(crc32.getValue());
entry.setCompressedSize(bytes.length);
}
}
/**
* Determines if a given entry represents a classfile.
*
* @return Does the given entry represent a classfile?
*/
private static boolean isClassFileEntry(ZipEntry entry)
{
return entry.getName().endsWith(".class");
}
}