1 /* 2 * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 package jdk.test.lib.util; 25 26 import java.io.FileInputStream; 27 import java.io.FileNotFoundException; 28 import java.io.FileOutputStream; 29 import java.io.IOException; 30 import java.nio.file.Files; 31 import java.nio.file.InvalidPathException; 32 import java.nio.file.Path; 33 import java.nio.file.Paths; 34 import java.util.Enumeration; 35 import java.util.HashMap; 36 import java.util.Map; 37 import java.util.jar.JarEntry; 38 import java.util.jar.JarFile; 39 import java.util.jar.JarOutputStream; 40 import java.util.jar.Manifest; 41 42 /** 43 * Common library for various test jar file utility functions. 44 */ 45 public final class JarUtils { 46 47 /** 48 * Create jar file with specified files. If a specified file does not exist, 49 * a new jar entry will be created with the file name itself as the content. 50 */ 51 public static void createJar(String dest, String... files) 52 throws IOException { 53 try (JarOutputStream jos = new JarOutputStream( 54 new FileOutputStream(dest), new Manifest())) { 55 for (String file : files) { 56 System.out.println(String.format("Adding %s to %s", 57 file, dest)); 58 59 // add an archive entry, and write a file 60 jos.putNextEntry(new JarEntry(file)); 61 try (FileInputStream fis = new FileInputStream(file)) { 62 fis.transferTo(jos); 63 } catch (FileNotFoundException e) { 64 jos.write(file.getBytes()); 65 } 66 } 67 } 68 System.out.println(); 69 } 70 71 /** 72 * Add or remove specified files to existing jar file. If a specified file 73 * to be updated or added does not exist, the jar entry will be created 74 * with the file name itself as the content. 75 * 76 * @param src the original jar file name 77 * @param dest the new jar file name 78 * @param files the files to update. The list is broken into 2 groups 79 * by a "-" string. The files before in the 1st group will 80 * be either updated or added. The files in the 2nd group 81 * will be removed. If no "-" exists, all files belong to 82 * the 1st group. 83 */ 84 public static void updateJar(String src, String dest, String... files) 85 throws IOException { 86 Map<String,Object> changes = new HashMap<>(); 87 boolean update = true; 88 for (String file : files) { 89 if (file.equals("-")) { 90 update = false; 91 } else if (update) { 92 try { 93 Path p = Paths.get(file); 94 if (Files.exists(p)) { 95 changes.put(file, p); 96 } else { 97 changes.put(file, file); 98 } 99 } catch (InvalidPathException e) { 100 // Fallback if file not a valid Path. 101 changes.put(file, file); 102 } 103 } else { 104 changes.put(file, Boolean.FALSE); 105 } 106 } 107 updateJar(src, dest, changes); 108 } 109 110 /** 111 * Update content of a jar file. 112 * 113 * @param src the original jar file name 114 * @param dest the new jar file name 115 * @param changes a map of changes, key is jar entry name, value is content. 116 * Value can be Path, byte[] or String. If key exists in 117 * src but value is Boolean FALSE. The entry is removed. 118 * Existing entries in src not a key is unmodified. 119 * @throws IOException 120 */ 121 public static void updateJar(String src, String dest, 122 Map<String,Object> changes) 123 throws IOException { 124 125 // What if input changes is immutable? 126 changes = new HashMap<>(changes); 127 128 System.out.printf("Creating %s from %s...\n", dest, src); 129 try (JarOutputStream jos = new JarOutputStream( 130 new FileOutputStream(dest))) { 131 132 try (JarFile srcJarFile = new JarFile(src)) { 133 Enumeration<JarEntry> entries = srcJarFile.entries(); 134 while (entries.hasMoreElements()) { 135 JarEntry entry = entries.nextElement(); 136 String name = entry.getName(); 137 if (changes.containsKey(name)) { 138 System.out.println(String.format("- Update %s", name)); 139 updateEntry(jos, name, changes.get(name)); 140 changes.remove(name); 141 } else { 142 System.out.println(String.format("- Copy %s", name)); 143 jos.putNextEntry(entry); 144 srcJarFile.getInputStream(entry).transferTo(jos); 145 } 146 } 147 } 148 for (Map.Entry<String, Object> e : changes.entrySet()) { 149 System.out.println(String.format("- Add %s", e.getKey())); 150 updateEntry(jos, e.getKey(), e.getValue()); 151 } 152 } 153 System.out.println(); 154 } 155 156 private static void updateEntry(JarOutputStream jos, String name, Object content) 157 throws IOException { 158 if (content instanceof Boolean) { 159 if (((Boolean) content).booleanValue()) { 160 throw new RuntimeException("Boolean value must be FALSE"); 161 } 162 } else { 163 jos.putNextEntry(new JarEntry(name)); 164 if (content instanceof Path) { 165 Files.newInputStream((Path) content).transferTo(jos); 166 } else if (content instanceof byte[]) { 167 jos.write((byte[]) content); 168 } else if (content instanceof String) { 169 jos.write(((String) content).getBytes()); 170 } else { 171 throw new RuntimeException("Unknown type " + content.getClass()); 172 } 173 } 174 } 175 }