/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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.openejb.maven.plugin.customizer.monkey.jar; import org.apache.commons.compress.archivers.jar.JarArchiveEntry; import org.apache.commons.compress.archivers.jar.JarArchiveInputStream; import org.apache.commons.compress.archivers.jar.JarArchiveOutputStream; import org.apache.commons.compress.archivers.zip.ZipArchiveEntry; import org.apache.commons.compress.utils.IOUtils; import org.apache.openejb.loader.IO; import org.apache.openejb.maven.plugin.customizer.monkey.index.Item; import org.codehaus.plexus.util.FileUtils; 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.io.PrintStream; import java.text.SimpleDateFormat; import java.util.Date; import java.util.List; public class JarPatcher { public void patch(final PrintStream stream, final File tmp, final File key, final List<Item> value) { stream.println(" " + key); if (key.isDirectory()) { throw new IllegalArgumentException("Directory not yet supported - shouldn't occur"); } if (!key.getName().endsWith(".jar")) { throw new IllegalArgumentException(key + " not a jar - shouldn't occur"); } doJarPatch(stream, tmp, key, value); } private void doJarPatch(final PrintStream stream, final File tmp, final File jarFile, final List<Item> items) { final File exploded = new File(tmp, jarFile.getName() + ".exploded"); try { // explode the jar final int method = unjar(exploded, jarFile); // patch files for (final Item item : items) { switch (item.getAction()) { case ADD_OR_UPDATE: replace(stream, jarFile.getName(), exploded, item.getPath(), item.getFile()); break; case REMOVE: try { FileUtils.forceDelete(new File(exploded, item.getPath())); } catch (final IOException e) { throw new IllegalStateException(e); } break; default: } } // recreate the jar try { FileUtils.forceDelete(jarFile); } catch (final IOException e) { if (!jarFile.renameTo(new File(jarFile.getParentFile(), jarFile.getName() + "_patched"))) { throw new IllegalStateException(e); } } final String name = jarFile.getName(); jar(method, exploded, new File(jarFile.getParentFile(), jarName(name))); } finally { try { FileUtils.deleteDirectory(exploded); } catch (IOException e) { // no-op } } } private String jarName(final String name) { return name.substring(0, name.length() - ".jar".length()) + "tomee-monkey-" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + ".jar"; } private void replace(final PrintStream stream, final String name, final File exploded, final String path, final File file) { final File existing = new File(exploded, path); if (!existing.isFile()) { stream.println(" - No " + path + " in " + name + ", creating it."); existing.getParentFile().mkdirs(); } else { stream.println(" - Replacing " + path + " in " + name + " with " + file); // try to delete the file first otherwise try to overwrite it directly existing.delete(); } OutputStream os = null; InputStream is = null; try { os = new FileOutputStream(existing); is = new FileInputStream(file); IO.copy(is, os); } catch (final IOException e) { throw new IllegalStateException(e); } finally { IO.close(os); IO.close(is); } } private int unjar(final File exploded, final File from) { int method = -1; JarArchiveInputStream stream = null; try { stream = new JarArchiveInputStream(new FileInputStream(from)); JarArchiveEntry entry; while ((entry = stream.getNextJarEntry()) != null) { final File archiveEntry = new File(exploded, entry.getName()); archiveEntry.getParentFile().mkdirs(); if (entry.isDirectory()) { archiveEntry.mkdir(); continue; } final OutputStream out = new FileOutputStream(archiveEntry); IOUtils.copy(stream, out); out.close(); if (method < 0) { method = entry.getMethod(); } } } catch (final IOException e) { throw new IllegalArgumentException(e); } finally { IO.close(stream); } return method; } private void jar(final int method, final File exploded, final File target) { JarArchiveOutputStream stream = null; try { stream = new JarArchiveOutputStream(new FileOutputStream(target)); final String prefix = exploded.getCanonicalFile().getAbsolutePath() + File.separator; for (final String f : exploded.list()) { jar(method, stream, new File(exploded, f).getCanonicalFile(), prefix); } } catch (final IOException e) { throw new IllegalArgumentException(e); } finally { IO.close(stream); } } private void jar(final int method, final JarArchiveOutputStream jar, final File f, final String prefix) throws IOException { final String path = f.getPath().replace(prefix, "").replace(File.separator, "/"); final ZipArchiveEntry zip = new ZipArchiveEntry(f, path); zip.setMethod(method); final JarArchiveEntry archiveEntry = new JarArchiveEntry(zip); jar.putArchiveEntry(archiveEntry); if (f.isDirectory()) { jar.closeArchiveEntry(); final File[] files = f.listFiles(); if (files != null) { for (final File child : files) { jar(method, jar, child.getCanonicalFile(), prefix); } } } else { final InputStream is = new FileInputStream(f); IOUtils.copy(is, jar); is.close(); jar.closeArchiveEntry(); } } }