1 /*
2  * Copyright (c) 2012, 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  * @bug     6901992
27  * @summary InvalidJarIndexException due to bug in sun.misc.JarIndex.merge()
28  *          Test URLClassLoader usage of the merge method when using indexes
29  * @author  Diego Belfer
30  */
31 import java.io.BufferedReader;
32 import java.io.File;
33 import java.io.FileNotFoundException;
34 import java.io.FileOutputStream;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.io.InputStreamReader;
38 import java.net.URL;
39 import java.net.URLClassLoader;
40 import java.util.jar.JarEntry;
41 import java.util.jar.JarOutputStream;
42 
43 public class JarIndexMergeForClassLoaderTest {
44     static final String slash = File.separator;
45     static final String testClassesDir = System.getProperty("test.classes", ".");
46     static final String jar;
47     static final boolean debug = true;
48     static final File tmpFolder = new File(testClassesDir);
49 
50     static {
51         jar = System.getProperty("java.home") + slash + "bin" + slash + "jar";
52     }
53 
main(String[] args)54     public static void main(String[] args) throws Exception {
55         // Create the jars file
56         File jar1 = buildJar1();
57         File jar2 = buildJar2();
58         File jar3 = buildJar3();
59 
60         // Index jar files in two levels: jar1 -> jar2 -> jar3
61         createIndex(jar2.getName(), jar3.getName());
62         createIndex(jar1.getName(), jar2.getName());
63 
64         // Get root jar of the URLClassLoader
65         URL url = jar1.toURI().toURL();
66 
67         URLClassLoader classLoader = new URLClassLoader(new URL[] { url });
68 
69         assertResource(classLoader, "com/jar1/resource.file", "jar1");
70         assertResource(classLoader, "com/test/resource1.file", "resource1");
71         assertResource(classLoader, "com/jar2/resource.file", "jar2");
72         assertResource(classLoader, "com/test/resource2.file", "resource2");
73         assertResource(classLoader, "com/test/resource3.file", "resource3");
74 
75         /*
76          * The following two asserts failed before the fix of the bug 6901992
77          */
78         // Check that an existing file is found using the merged index
79         assertResource(classLoader, "com/missing/jar3/resource.file", "jar3");
80         // Check that a non existent file in directory which does not contain
81         // any file is not found and it does not throw InvalidJarIndexException
82         assertResource(classLoader, "com/missing/nofile", null);
83     }
84 
buildJar3()85     private static File buildJar3() throws FileNotFoundException, IOException {
86         JarBuilder jar3Builder = new JarBuilder(tmpFolder, "jar3.jar");
87         jar3Builder.addResourceFile("com/test/resource3.file", "resource3");
88         jar3Builder.addResourceFile("com/missing/jar3/resource.file", "jar3");
89         return jar3Builder.build();
90     }
91 
buildJar2()92     private static File buildJar2() throws FileNotFoundException, IOException {
93         JarBuilder jar2Builder = new JarBuilder(tmpFolder, "jar2.jar");
94         jar2Builder.addResourceFile("com/jar2/resource.file", "jar2");
95         jar2Builder.addResourceFile("com/test/resource2.file", "resource2");
96         return jar2Builder.build();
97     }
98 
buildJar1()99     private static File buildJar1() throws FileNotFoundException, IOException {
100         JarBuilder jar1Builder = new JarBuilder(tmpFolder, "jar1.jar");
101         jar1Builder.addResourceFile("com/jar1/resource.file", "jar1");
102         jar1Builder.addResourceFile("com/test/resource1.file", "resource1");
103         return jar1Builder.build();
104     }
105 
106     /* create the index */
createIndex(String parentJar, String childJar)107     static void createIndex(String parentJar, String childJar) {
108         // ProcessBuilder is used so that the current directory can be set
109         // to the directory that directly contains the jars.
110         debug("Running jar to create the index for: " + parentJar + " and "
111                 + childJar);
112         ProcessBuilder pb = new ProcessBuilder(jar, "-i", parentJar, childJar);
113 
114         pb.directory(tmpFolder);
115         // pd.inheritIO();
116         try {
117             Process p = pb.start();
118             if (p.waitFor() != 0)
119                 throw new RuntimeException("jar indexing failed");
120 
121             if (debug && p != null) {
122                 debugStream(p.getInputStream());
123                 debugStream(p.getErrorStream());
124             }
125         } catch (InterruptedException | IOException x) {
126             throw new RuntimeException(x);
127         }
128     }
129 
debugStream(InputStream is)130     private static void debugStream(InputStream is) throws IOException {
131         try (BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
132             String line;
133             while ((line = reader.readLine()) != null) {
134                 debug(line);
135             }
136         }
137     }
138 
assertResource(URLClassLoader classLoader, String file, String expectedContent)139     private static void assertResource(URLClassLoader classLoader, String file,
140             String expectedContent) throws IOException {
141         InputStream fileStream = classLoader.getResourceAsStream(file);
142 
143         if (fileStream == null && expectedContent == null) {
144             return;
145         }
146         if (fileStream == null && expectedContent != null) {
147             throw new RuntimeException(
148                     buildMessage(file, expectedContent, null));
149         }
150         try {
151             String actualContent = readAsString(fileStream);
152 
153             if (fileStream != null && expectedContent == null) {
154                 throw new RuntimeException(buildMessage(file, null,
155                         actualContent));
156             }
157             if (!expectedContent.equals(actualContent)) {
158                 throw new RuntimeException(buildMessage(file, expectedContent,
159                         actualContent));
160             }
161         } finally {
162             fileStream.close();
163         }
164     }
165 
buildMessage(String file, String expectedContent, String actualContent)166     private static String buildMessage(String file, String expectedContent,
167             String actualContent) {
168         return "Expected: " + expectedContent + " for: " + file + " was: "
169                 + actualContent;
170     }
171 
readAsString(InputStream fileStream)172     private static String readAsString(InputStream fileStream)
173             throws IOException {
174         byte[] buffer = new byte[1024];
175         int count, len = 0;
176         while ((count = fileStream.read(buffer, len, buffer.length-len)) != -1)
177                 len += count;
178         return new String(buffer, 0, len, "ASCII");
179     }
180 
debug(Object message)181     static void debug(Object message) {
182         if (debug)
183             System.out.println(message);
184     }
185 
186     /*
187      * Helper class for building jar files
188      */
189     public static class JarBuilder {
190         private JarOutputStream os;
191         private File jarFile;
192 
JarBuilder(File tmpFolder, String jarName)193         public JarBuilder(File tmpFolder, String jarName)
194             throws FileNotFoundException, IOException
195         {
196             this.jarFile = new File(tmpFolder, jarName);
197             this.os = new JarOutputStream(new FileOutputStream(jarFile));
198         }
199 
addResourceFile(String pathFromRoot, String content)200         public void addResourceFile(String pathFromRoot, String content)
201             throws IOException
202         {
203             JarEntry entry = new JarEntry(pathFromRoot);
204             os.putNextEntry(entry);
205             os.write(content.getBytes("ASCII"));
206             os.closeEntry();
207         }
208 
build()209         public File build() throws IOException {
210             os.close();
211             return jarFile;
212         }
213     }
214 }
215 
216