package org.eclipse.buckminster.jarprocessor; import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; import java.util.zip.ZipOutputStream; import org.eclipse.buckminster.core.helpers.FileUtils; import org.eclipse.buckminster.runtime.Buckminster; import org.eclipse.buckminster.runtime.BuckminsterException; import org.eclipse.buckminster.runtime.IOUtils; import org.eclipse.core.runtime.CoreException; public class RecursiveUnpacker extends RecursivePack200 { static boolean isJarMagic(byte[] magic) { return (magic[0] == (byte) 'P' && magic[1] == (byte) 'K' && magic[2] >= 1 && magic[2] < 8 && magic[3] == magic[2] + 1); } public RecursiveUnpacker(File tempDir, List<String> defaultArgs) { super(tempDir, defaultArgs); } private void nestedUnpack(InputStream input, OutputStream unpacked) throws IOException { // The Unpack200 will corrupt the manifest for packed files if -E0 is // used. This is because it copies the jar entries one by one and // rewrites the manifest. // // Here we check if the packed file starts with a jar magic. If it does, // then this is a plain copy and we treat is as such. // final BufferedInputStream bufferedInput = new BufferedInputStream(input); bufferedInput.mark(4); byte[] magic = new byte[4]; if (bufferedInput.read(magic, 0, 4) != 4) throw new IOException("Unable to read packed file magic"); //$NON-NLS-1$ bufferedInput.reset(); ZipInputStream jarIn; if (isJarMagic(magic)) jarIn = new ZipInputStream(bufferedInput); else { final ProducerThread jarPumper = new ProducerThread("Unpack200 jarPumper") //$NON-NLS-1$ { @Override protected void internalRun(OutputStream writer) throws Exception { unpack(new NonClosingInputStream(bufferedInput), writer); } }; jarPumper.start(); jarIn = new ZipInputStream(jarPumper.getReaderStream()); } ZipOutputStream jarOut = new ZipOutputStream(unpacked); ZipEntry entry; while ((entry = jarIn.getNextEntry()) != null) { String name = entry.getName(); if (entry.isDirectory()) { jarOut.putNextEntry(createEntry(entry)); continue; } String jarName = null; InputStream packedInput = null; if (name.endsWith(PACK_GZ_SUFFIX)) { jarName = name.substring(0, name.length() - PACK_GZ_SUFFIX.length()); packedInput = new GZIPInputStream(new NonClosingInputStream(jarIn)); } else if (name.endsWith(PACK_SUFFIX)) { jarName = name.substring(0, name.length() - PACK_SUFFIX.length()); packedInput = jarIn; } if (packedInput != null) { Buckminster.getLogger().debug("Unpacker: Recursive unpack of %s", name); //$NON-NLS-1$ jarOut.putNextEntry(createEntry(entry, jarName)); nestedUnpack(packedInput, jarOut); continue; } jarOut.putNextEntry(createEntry(entry)); IOUtils.copy(jarIn, jarOut, null); } jarOut.finish(); } public void unpack(File packedFile, File destFolder, boolean retainPacked) throws CoreException { boolean sharedFolder; if (destFolder == null) { sharedFolder = true; destFolder = packedFile.getParentFile(); } else sharedFolder = destFolder.equals(packedFile.getParentFile()); String fileName = packedFile.getAbsolutePath(); String name = packedFile.getName(); try (InputStream input = new GZIPInputStream(new FileInputStream(packedFile)); OutputStream output = new FileOutputStream(new File(destFolder, name.substring(0, name.length() - PACK_GZ_SUFFIX.length())))) { nestedUnpack(input, output); if (sharedFolder) { if (!retainPacked) packedFile.delete(); } else { if (retainPacked) FileUtils.copyFile(packedFile, destFolder, name, null); } } catch (IOException e) { throw BuckminsterException.fromMessage(e, "Unable to condition %s", fileName); //$NON-NLS-1$ } } }