1 /* 2 * Copyright (c) 2016, 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 /* 25 * @test 26 * @library /test/lib 27 * @modules java.base/jdk.internal.misc 28 * @build jdk.test.lib.JDKToolFinder jdk.test.lib.Platform 29 * @run testng Basic 30 */ 31 32 import static org.testng.Assert.*; 33 34 import org.testng.annotations.*; 35 36 import java.io.*; 37 import java.nio.file.*; 38 import java.nio.file.attribute.*; 39 import java.util.*; 40 import java.util.function.Consumer; 41 import java.util.jar.*; 42 import java.util.stream.Stream; 43 import java.util.zip.*; 44 45 import jdk.test.lib.JDKToolFinder; 46 47 import static java.lang.String.format; 48 import static java.lang.System.out; 49 50 public class Basic { 51 private final String src = System.getProperty("test.src", "."); 52 private final String usr = System.getProperty("user.dir", "."); 53 54 @Test 55 // create a regular, non-multi-release jar 56 public void test00() throws IOException { 57 String jarfile = "test.jar"; 58 59 compile("test01"); //use same data as test01 60 61 Path classes = Paths.get("classes"); 62 jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".") 63 .assertSuccess(); 64 65 checkMultiRelease(jarfile, false); 66 67 Map<String,String[]> names = Map.of( 68 "version/Main.class", 69 new String[] {"base", "version", "Main.class"}, 70 71 "version/Version.class", 72 new String[] {"base", "version", "Version.class"} 73 ); 74 75 compare(jarfile, names); 76 77 delete(jarfile); 78 deleteDir(Paths.get(usr, "classes")); 79 } 80 81 @Test 82 // create a multi-release jar 83 public void test01() throws IOException { 84 String jarfile = "test.jar"; 85 86 compile("test01"); 87 88 Path classes = Paths.get("classes"); 89 jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", 90 "--release", "9", "-C", classes.resolve("v9").toString(), ".", 91 "--release", "10", "-C", classes.resolve("v10").toString(), ".") 92 .assertSuccess(); 93 94 checkMultiRelease(jarfile, true); 95 96 Map<String,String[]> names = Map.of( 97 "version/Main.class", 98 new String[] {"base", "version", "Main.class"}, 99 100 "version/Version.class", 101 new String[] {"base", "version", "Version.class"}, 102 103 "META-INF/versions/9/version/Version.class", 104 new String[] {"v9", "version", "Version.class"}, 105 106 "META-INF/versions/10/version/Version.class", 107 new String[] {"v10", "version", "Version.class"} 108 ); 109 110 compare(jarfile, names); 111 112 delete(jarfile); 113 deleteDir(Paths.get(usr, "classes")); 114 } 115 116 @Test 117 // update a regular jar to a multi-release jar 118 public void test02() throws IOException { 119 String jarfile = "test.jar"; 120 121 compile("test01"); //use same data as test01 122 123 Path classes = Paths.get("classes"); 124 jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".") 125 .assertSuccess(); 126 127 checkMultiRelease(jarfile, false); 128 129 jar("uf", jarfile, "--release", "9", "-C", classes.resolve("v9").toString(), ".") 130 .assertSuccess(); 131 132 checkMultiRelease(jarfile, true); 133 134 Map<String,String[]> names = Map.of( 135 "version/Main.class", 136 new String[] {"base", "version", "Main.class"}, 137 138 "version/Version.class", 139 new String[] {"base", "version", "Version.class"}, 140 141 "META-INF/versions/9/version/Version.class", 142 new String[] {"v9", "version", "Version.class"} 143 ); 144 145 compare(jarfile, names); 146 147 delete(jarfile); 148 deleteDir(Paths.get(usr, "classes")); 149 } 150 151 @Test 152 // replace a base entry and a versioned entry 153 public void test03() throws IOException { 154 String jarfile = "test.jar"; 155 156 compile("test01"); //use same data as test01 157 158 Path classes = Paths.get("classes"); 159 jar("cf", jarfile, "-C", classes.resolve("base").toString(), ".", 160 "--release", "9", "-C", classes.resolve("v9").toString(), ".") 161 .assertSuccess(); 162 163 checkMultiRelease(jarfile, true); 164 165 Map<String,String[]> names = Map.of( 166 "version/Main.class", 167 new String[] {"base", "version", "Main.class"}, 168 169 "version/Version.class", 170 new String[] {"base", "version", "Version.class"}, 171 172 "META-INF/versions/9/version/Version.class", 173 new String[] {"v9", "version", "Version.class"} 174 ); 175 176 compare(jarfile, names); 177 178 // write the v9 version/Version.class entry in base and the v10 179 // version/Version.class entry in versions/9 section 180 jar("uf", jarfile, "-C", classes.resolve("v9").toString(), "version", 181 "--release", "9", "-C", classes.resolve("v10").toString(), ".") 182 .assertSuccess(); 183 184 checkMultiRelease(jarfile, true); 185 186 names = Map.of( 187 "version/Main.class", 188 new String[] {"base", "version", "Main.class"}, 189 190 "version/Version.class", 191 new String[] {"v9", "version", "Version.class"}, 192 193 "META-INF/versions/9/version/Version.class", 194 new String[] {"v10", "version", "Version.class"} 195 ); 196 197 delete(jarfile); 198 deleteDir(Paths.get(usr, "classes")); 199 } 200 201 /* 202 * Test Infrastructure 203 */ 204 private void compile(String test) throws IOException { 205 Path classes = Paths.get(usr, "classes", "base"); 206 Files.createDirectories(classes); 207 Path source = Paths.get(src, "data", test, "base", "version"); 208 javac(classes, source.resolve("Main.java"), source.resolve("Version.java")); 209 210 classes = Paths.get(usr, "classes", "v9"); 211 Files.createDirectories(classes); 212 source = Paths.get(src, "data", test, "v9", "version"); 213 javac(classes, source.resolve("Version.java")); 214 215 classes = Paths.get(usr, "classes", "v10"); 216 Files.createDirectories(classes); 217 source = Paths.get(src, "data", test, "v10", "version"); 218 javac(classes, source.resolve("Version.java")); 219 } 220 221 private void checkMultiRelease(String jarFile, boolean expected) throws IOException { 222 try (JarFile jf = new JarFile(new File(jarFile), true, ZipFile.OPEN_READ, 223 JarFile.runtimeVersion())) { 224 assertEquals(jf.isMultiRelease(), expected); 225 } 226 } 227 228 // compares the bytes found in the jar entries with the bytes found in the 229 // corresponding data files used to create the entries 230 private void compare(String jarfile, Map<String,String[]> names) throws IOException { 231 try (JarFile jf = new JarFile(jarfile)) { 232 for (String name : names.keySet()) { 233 Path path = Paths.get("classes", names.get(name)); 234 byte[] b1 = Files.readAllBytes(path); 235 byte[] b2; 236 JarEntry je = jf.getJarEntry(name); 237 try (InputStream is = jf.getInputStream(je)) { 238 b2 = is.readAllBytes(); 239 } 240 assertEquals(b1,b2); 241 } 242 } 243 } 244 245 private void delete(String name) throws IOException { 246 Files.delete(Paths.get(usr, name)); 247 } 248 249 private void deleteDir(Path dir) throws IOException { 250 Files.walkFileTree(dir, new SimpleFileVisitor<Path>() { 251 @Override 252 public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { 253 Files.delete(file); 254 return FileVisitResult.CONTINUE; 255 } 256 257 @Override 258 public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { 259 Files.delete(dir); 260 return FileVisitResult.CONTINUE; 261 } 262 }); 263 } 264 265 /* 266 * The following methods were taken from modular jar and other jar tests 267 */ 268 269 void javac(Path dest, Path... sourceFiles) throws IOException { 270 String javac = JDKToolFinder.getJDKTool("javac"); 271 272 List<String> commands = new ArrayList<>(); 273 commands.add(javac); 274 commands.add("-d"); 275 commands.add(dest.toString()); 276 Stream.of(sourceFiles).map(Object::toString).forEach(x -> commands.add(x)); 277 278 quickFail(run(new ProcessBuilder(commands))); 279 } 280 281 Result jarWithStdin(File stdinSource, String... args) { 282 String jar = JDKToolFinder.getJDKTool("jar"); 283 List<String> commands = new ArrayList<>(); 284 commands.add(jar); 285 Stream.of(args).forEach(x -> commands.add(x)); 286 ProcessBuilder p = new ProcessBuilder(commands); 287 if (stdinSource != null) 288 p.redirectInput(stdinSource); 289 return run(p); 290 } 291 292 Result jar(String... args) { 293 return jarWithStdin(null, args); 294 } 295 296 void quickFail(Result r) { 297 if (r.ec != 0) 298 throw new RuntimeException(r.output); 299 } 300 301 Result run(ProcessBuilder pb) { 302 Process p; 303 out.printf("Running: %s%n", pb.command()); 304 try { 305 p = pb.start(); 306 } catch (IOException e) { 307 throw new RuntimeException( 308 format("Couldn't start process '%s'", pb.command()), e); 309 } 310 311 String output; 312 try { 313 output = toString(p.getInputStream(), p.getErrorStream()); 314 } catch (IOException e) { 315 throw new RuntimeException( 316 format("Couldn't read process output '%s'", pb.command()), e); 317 } 318 319 try { 320 p.waitFor(); 321 } catch (InterruptedException e) { 322 throw new RuntimeException( 323 format("Process hasn't finished '%s'", pb.command()), e); 324 } 325 return new Result(p.exitValue(), output); 326 } 327 328 String toString(InputStream in1, InputStream in2) throws IOException { 329 try (ByteArrayOutputStream dst = new ByteArrayOutputStream(); 330 InputStream concatenated = new SequenceInputStream(in1, in2)) { 331 concatenated.transferTo(dst); 332 return new String(dst.toByteArray(), "UTF-8"); 333 } 334 } 335 336 static class Result { 337 final int ec; 338 final String output; 339 340 private Result(int ec, String output) { 341 this.ec = ec; 342 this.output = output; 343 } 344 Result assertSuccess() { 345 assertTrue(ec == 0, format("ec: %d, output: %s", ec, output)); 346 return this; 347 } 348 Result assertFailure() { 349 assertTrue(ec != 0, format("ec: %d, output: %s", ec, output)); 350 return this; 351 } 352 Result resultChecker(Consumer<Result> r) { r.accept(this); return this; } 353 } 354 }