< prev index next >

test/lib/jdk/test/lib/util/JarUtils.java

Print this page
rev 51882 : [mq]: 8211171-1


   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);


 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 }


   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.File;
  27 import java.io.FileInputStream;
  28 import java.io.FileNotFoundException;
  29 import java.io.FileOutputStream;
  30 import java.io.IOException;
  31 import java.io.OutputStream;
  32 import java.nio.file.Files;
  33 import java.nio.file.InvalidPathException;
  34 import java.nio.file.Path;
  35 import java.nio.file.Paths;
  36 import java.nio.file.StandardCopyOption;
  37 import java.util.ArrayList;
  38 import java.util.Enumeration;
  39 import java.util.HashMap;
  40 import java.util.List;
  41 import java.util.Map;
  42 import java.util.Set;
  43 import java.util.jar.JarEntry;
  44 import java.util.jar.JarFile;
  45 import java.util.jar.JarOutputStream;
  46 import java.util.jar.Manifest;
  47 import java.util.stream.Collectors;
  48 import java.util.stream.Stream;
  49 
  50 /**
  51  * This class consists exclusively of static utility methods that are useful
  52  * for creating and manipulating JAR files.
  53  */
  54 public final class JarUtils {
  55     private JarUtils() { }
  56 
  57     /**
  58      * Creates a JAR file.
  59      *
  60      * Equivalent to {@code jar cfm <jarfile> <manifest> -C <dir> file...}
  61      *
  62      * The input files are resolved against the given directory. Any input
  63      * files that are directories are processed recursively.
  64      */
  65     public static void createJarFile(Path jarfile, Manifest man, Path dir, Path... files)
  66             throws IOException
  67     {
  68         // create the target directory
  69         Path parent = jarfile.getParent();
  70         if (parent != null) {
  71             Files.createDirectories(parent);
  72         }
  73 
  74         List<Path> entries = findAllRegularFiles(dir, files);
  75 
  76         try (OutputStream out = Files.newOutputStream(jarfile);
  77              JarOutputStream jos = new JarOutputStream(out)) {
  78             if (man != null) {
  79                 JarEntry je = new JarEntry(JarFile.MANIFEST_NAME);
  80                 jos.putNextEntry(je);
  81                 man.write(jos);
  82                 jos.closeEntry();
  83             }
  84 
  85             for (Path entry : entries) {
  86                 String name = toJarEntryName(entry);
  87                 jos.putNextEntry(new JarEntry(name));
  88                 Files.copy(dir.resolve(entry), jos);
  89                 jos.closeEntry();
  90             }
  91         }
  92     }
  93 
  94     /**
  95      * Creates a JAR file.
  96      *
  97      * Equivalent to {@code jar cf <jarfile>  -C <dir> file...}
  98      *
  99      * The input files are resolved against the given directory. Any input
 100      * files that are directories are processed recursively.
 101      */
 102     public static void createJarFile(Path jarfile, Path dir, Path... files)
 103             throws IOException
 104     {
 105         createJarFile(jarfile, null, dir, files);
 106     }
 107 
 108     /**
 109      * Creates a JAR file from the contents of a directory.
 110      *
 111      * Equivalent to {@code jar cf <jarfile> -C <dir> .}
 112      */
 113     public static void createJarFile(Path jarfile, Path dir) throws IOException {
 114         createJarFile(jarfile, dir, Paths.get("."));
 115     }
 116 
 117 
 118     /**
 119      * Creates a JAR file.
 120      *
 121      * Equivalent to {@code jar cf <jarfile> -C <dir> file...}
 122      *
 123      * The input files are resolved against the given directory. Any input
 124      * files that are directories are processed recursively.
 125      */
 126     public static void createJarFile(Path jarfile, Path dir, String... input)
 127             throws IOException
 128     {
 129         Path[] paths = Stream.of(input).map(Paths::get).toArray(Path[]::new);
 130         createJarFile(jarfile, dir, paths);
 131     }
 132 
 133     /**
 134      * Updates a JAR file.
 135      *
 136      * Equivalent to {@code jar uf <jarfile> -C <dir> file...}
 137      *
 138      * The input files are resolved against the given directory. Any input
 139      * files that are directories are processed recursively.
 140      */
 141     public static void updateJarFile(Path jarfile, Path dir, Path... files)
 142             throws IOException
 143     {
 144         List<Path> entries = findAllRegularFiles(dir, files);
 145 
 146         Set<String> names = entries.stream()
 147                                    .map(JarUtils::toJarEntryName)
 148                                    .collect(Collectors.toSet());
 149 
 150         Path tmpfile = Files.createTempFile("jar", "jar");
 151 
 152         try (OutputStream out = Files.newOutputStream(tmpfile);
 153              JarOutputStream jos = new JarOutputStream(out)) {
 154             // copy existing entries from the original JAR file
 155             try (JarFile jf = new JarFile(jarfile.toString())) {
 156                 Enumeration<JarEntry> jentries = jf.entries();
 157                 while (jentries.hasMoreElements()) {
 158                     JarEntry jentry = jentries.nextElement();
 159                     if (!names.contains(jentry.getName())) {
 160                         jos.putNextEntry(jentry);
 161                         jf.getInputStream(jentry).transferTo(jos);
 162                     }
 163                 }
 164             }
 165 
 166             // add the new entries
 167             for (Path entry : entries) {
 168                 String name = toJarEntryName(entry);
 169                 jos.putNextEntry(new JarEntry(name));
 170                 Files.copy(dir.resolve(entry), jos);
 171             }
 172         }
 173 
 174         // replace the original JAR file
 175         Files.move(tmpfile, jarfile, StandardCopyOption.REPLACE_EXISTING);
 176     }
 177 
 178     /**
 179      * Updates a JAR file.
 180      *
 181      * Equivalent to {@code jar uf <jarfile> -C <dir> .}
 182      */
 183     public static void updateJarFile(Path jarfile, Path dir) throws IOException {
 184         updateJarFile(jarfile, dir, Paths.get("."));
 185     }
 186 
 187 
 188     /**
 189      * Create jar file with specified files. If a specified file does not exist,
 190      * a new jar entry will be created with the file name itself as the content.
 191      */
 192     @Deprecated
 193     public static void createJar(String dest, String... files)
 194             throws IOException {
 195         try (JarOutputStream jos = new JarOutputStream(
 196                 new FileOutputStream(dest), new Manifest())) {
 197             for (String file : files) {
 198                 System.out.println(String.format("Adding %s to %s",
 199                         file, dest));
 200 
 201                 // add an archive entry, and write a file
 202                 jos.putNextEntry(new JarEntry(file));
 203                 try (FileInputStream fis = new FileInputStream(file)) {
 204                     fis.transferTo(jos);
 205                 } catch (FileNotFoundException e) {
 206                     jos.write(file.getBytes());
 207                 }
 208             }
 209         }
 210         System.out.println();
 211     }
 212 
 213     /**
 214      * Add or remove specified files to existing jar file. If a specified file
 215      * to be updated or added does not exist, the jar entry will be created
 216      * with the file name itself as the content.
 217      *
 218      * @param src the original jar file name
 219      * @param dest the new jar file name
 220      * @param files the files to update. The list is broken into 2 groups
 221      *              by a "-" string. The files before in the 1st group will
 222      *              be either updated or added. The files in the 2nd group
 223      *              will be removed. If no "-" exists, all files belong to
 224      *              the 1st group.
 225      */
 226     @Deprecated
 227     public static void updateJar(String src, String dest, String... files)
 228             throws IOException {
 229         Map<String,Object> changes = new HashMap<>();
 230         boolean update = true;
 231         for (String file : files) {
 232             if (file.equals("-")) {
 233                 update = false;
 234             } else if (update) {
 235                 try {
 236                     Path p = Paths.get(file);
 237                     if (Files.exists(p)) {
 238                         changes.put(file, p);
 239                     } else {
 240                         changes.put(file, file);
 241                     }
 242                 } catch (InvalidPathException e) {
 243                     // Fallback if file not a valid Path.
 244                     changes.put(file, file);
 245                 }
 246             } else {
 247                 changes.put(file, Boolean.FALSE);
 248             }
 249         }
 250         updateJar(src, dest, changes);
 251     }
 252 
 253     /**
 254      * Update content of a jar file.
 255      *
 256      * @param src the original jar file name
 257      * @param dest the new jar file name
 258      * @param changes a map of changes, key is jar entry name, value is content.
 259      *                Value can be Path, byte[] or String. If key exists in
 260      *                src but value is Boolean FALSE. The entry is removed.
 261      *                Existing entries in src not a key is unmodified.
 262      * @throws IOException
 263      */
 264     @Deprecated
 265     public static void updateJar(String src, String dest,
 266                                  Map<String,Object> changes)
 267             throws IOException {
 268 
 269         // What if input changes is immutable?
 270         changes = new HashMap<>(changes);
 271 
 272         System.out.printf("Creating %s from %s...\n", dest, src);
 273         try (JarOutputStream jos = new JarOutputStream(
 274                 new FileOutputStream(dest))) {
 275 
 276             try (JarFile srcJarFile = new JarFile(src)) {
 277                 Enumeration<JarEntry> entries = srcJarFile.entries();
 278                 while (entries.hasMoreElements()) {
 279                     JarEntry entry = entries.nextElement();
 280                     String name = entry.getName();
 281                     if (changes.containsKey(name)) {
 282                         System.out.println(String.format("- Update %s", name));
 283                         updateEntry(jos, name, changes.get(name));
 284                         changes.remove(name);


 299 
 300     private static void updateEntry(JarOutputStream jos, String name, Object content)
 301            throws IOException {
 302         if (content instanceof Boolean) {
 303             if (((Boolean) content).booleanValue()) {
 304                 throw new RuntimeException("Boolean value must be FALSE");
 305             }
 306         } else {
 307             jos.putNextEntry(new JarEntry(name));
 308             if (content instanceof Path) {
 309                 Files.newInputStream((Path) content).transferTo(jos);
 310             } else if (content instanceof byte[]) {
 311                 jos.write((byte[]) content);
 312             } else if (content instanceof String) {
 313                 jos.write(((String) content).getBytes());
 314             } else {
 315                 throw new RuntimeException("Unknown type " + content.getClass());
 316             }
 317         }
 318     }
 319 
 320     /**
 321      * Maps a file path to the equivalent name in a JAR file
 322      */
 323     private static String toJarEntryName(Path file) {
 324         Path normalized = file.normalize();
 325         return normalized.subpath(0, normalized.getNameCount())  // drop root
 326                          .toString()
 327                          .replace(File.separatorChar, '/');
 328     }
 329 
 330     private static List<Path> findAllRegularFiles(Path dir, Path[] files) throws IOException {
 331         List<Path> entries = new ArrayList<>();
 332         for (Path file : files) {
 333             try (Stream<Path> stream = Files.find(dir.resolve(file), Integer.MAX_VALUE,
 334                     (p, attrs) -> attrs.isRegularFile())) {
 335                 stream.map(dir::relativize)
 336                       .forEach(entries::add);
 337             }
 338         }
 339         return entries;
 340     }
 341 }
< prev index next >