Basic.java revision 12973:2e63fa2efdb1
1/*
2 * Copyright (c) 2011, 2015, 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 6887710
27 * @summary Verify the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader
28 * @modules jdk.jartool/sun.tools.jar
29 *          jdk.httpserver
30 *          jdk.compiler
31 * @run main/othervm Basic
32 */
33
34import java.io.IOException;
35import java.io.BufferedReader;
36import java.io.File;
37import java.io.FileInputStream;
38import java.io.InputStream;
39import java.io.InputStreamReader;
40import java.io.OutputStream;
41import java.net.InetSocketAddress;
42import java.net.URI;
43import java.net.URL;
44import java.net.URLClassLoader;
45import java.util.Arrays;
46import java.util.Iterator;
47import java.util.ServiceLoader;
48import com.sun.net.httpserver.Headers;
49import com.sun.net.httpserver.HttpExchange;
50import com.sun.net.httpserver.HttpHandler;
51import com.sun.net.httpserver.HttpServer;
52
53/**
54 * Verifies the impact of sun.misc.JarIndex.metaInfFilenames on ServiceLoader
55 * and on finding resources via Class.getResource.
56 *
57 * 1) Compile the test sources:
58 *   jarA:
59 *     META-INF/services/my.happy.land
60 *     com/message/spi/MessageService.java
61 *     a/A.java
62 *   jarB:
63 *     META-INF/JAVA2.DS
64 *     META-INF/services/no.name.service
65 *     b/B.java
66 *   jarC:
67 *     META-INF/fonts.mf
68 *     META-INF/fonts/Company-corporate.ttf
69 *     META-INF/fonts/kidpr.ttf
70 *     META-INF/services/com.message.spi.MessageService
71 *     my/impl/StandardMessageService.java
72 *
73 * 2) Build three jar files a.jar, b.jar, c.jar
74 *
75 * 3) Create an index in a.jar (jar -i a.jar b.jar c.jar)
76 *      with sun.misc.JarIndex.metaInfFilenames=true
77 *
78 * 4) Start a HTTP server serving out the three jars.
79 *
80 * The test then tries to locate services/resources within the jars using
81 * URLClassLoader. Each request to the HTTP server is recorded to ensure
82 * only the correct amount of requests are being made.
83 *
84 */
85
86public class Basic {
87    static final String slash = File.separator;
88    static final String[] testSources =  {
89         "jarA" + slash + "a" + slash + "A.java",
90         "jarA" + slash + "com" + slash + "message" + slash + "spi" + slash + "MessageService.java",
91         "jarB" + slash + "b" + slash + "B.java",
92         "jarC" + slash + "my" + slash + "impl" + slash + "StandardMessageService.java"};
93
94    static final String testSrc = System.getProperty("test.src");
95    static final String testSrcDir = testSrc != null ? testSrc : ".";
96    static final String testClasses = System.getProperty("test.classes");
97    static final String testClassesDir = testClasses != null ? testClasses : ".";
98
99    static JarHttpServer httpServer;
100
101    public static void main(String[] args) throws Exception {
102
103        // Set global url cache to false so that we can track every jar request.
104        (new URL("http://localhost/")).openConnection().setDefaultUseCaches(false);
105
106        buildTest();
107
108        try {
109            httpServer = new JarHttpServer(testClassesDir);
110            httpServer.start();
111
112            doTest(httpServer.getAddress());
113
114        } catch (IOException ioe) {
115            ioe.printStackTrace();
116        } finally {
117            if (httpServer != null) { httpServer.stop(2); }
118        }
119    }
120
121    static void buildTest() {
122        /* compile the source that will be used to generate the jars */
123        for (int i=0; i<testSources.length; i++)
124            testSources[i] = testSrcDir + slash + testSources[i];
125
126        compile("-d" , testClassesDir,
127                "-sourcepath", testSrcDir,
128                testSources[0], testSources[1], testSources[2], testSources[3]);
129
130        /* build the 3 jar files */
131        jar("-cf", testClassesDir + slash + "a.jar",
132            "-C", testClassesDir, "a",
133            "-C", testClassesDir, "com",
134            "-C", testSrcDir + slash + "jarA", "META-INF");
135        jar("-cf", testClassesDir + slash + "b.jar",
136            "-C", testClassesDir, "b",
137            "-C", testSrcDir + slash + "jarB", "META-INF");
138        jar("-cf", testClassesDir + slash + "c.jar",
139            "-C", testClassesDir, "my",
140            "-C", testSrcDir + slash + "jarC", "META-INF");
141
142        /* Create an index in a.jar for b.jar and c.jar */
143        createIndex(testClassesDir);
144    }
145
146    /* run jar <args> */
147    static void jar(String... args) {
148        debug("Running: jar " + Arrays.toString(args));
149        sun.tools.jar.Main jar = new sun.tools.jar.Main(System.out, System.err, "jar");
150        if (!jar.run(args)) {
151            throw new RuntimeException("jar failed: args=" + Arrays.toString(args));
152        }
153    }
154
155    /* run javac <args> */
156    static void compile(String... args) {
157        debug("Running: javac " + Arrays.toString(args));
158        if (com.sun.tools.javac.Main.compile(args) != 0) {
159             throw new RuntimeException("javac failed: args=" + Arrays.toString(args));
160        }
161    }
162
163    static String jar;
164    static {
165        jar = System.getProperty("java.home") + slash+  "bin" + slash + "jar";
166    }
167
168    /* create the index */
169    static void createIndex(String workingDir) {
170        // ProcessBuilder is used so that the current directory can be set
171        // to the directory that directly contains the jars.
172        debug("Running jar to create the index");
173        ProcessBuilder pb = new ProcessBuilder(
174           jar, "-J-Dsun.misc.JarIndex.metaInfFilenames=true", "-i", "a.jar", "b.jar", "c.jar");
175        pb.directory(new File(workingDir));
176        //pd.inheritIO();
177        try {
178            Process p = pb.start();
179            if(p.waitFor() != 0)
180                throw new RuntimeException("jar indexing failed");
181
182            if(debug && p != null) {
183                String line = null;
184                BufferedReader reader =
185                         new BufferedReader(new InputStreamReader(p.getInputStream()));
186                while((line = reader.readLine()) != null)
187                    debug(line);
188                reader = new BufferedReader(new InputStreamReader(p.getErrorStream()));
189                while((line = reader.readLine()) != null)
190                    debug(line);
191            }
192        } catch(InterruptedException ie) { throw new RuntimeException(ie);
193        } catch(IOException e) { throw new RuntimeException(e); }
194    }
195
196    static final boolean debug = true;
197
198    static void debug(Object message) { if (debug) System.out.println(message); }
199
200    /* service define in c.jar */
201    static final String messageService = "com.message.spi.MessageService";
202
203    /* a service that is not defined in any of the jars */
204    static final String unknownService = "java.lang.Object";
205
206    static void doTest(InetSocketAddress serverAddress) throws IOException {
207        URL baseURL = new URL("http://localhost:" + serverAddress.getPort() + "/");
208
209        int failed = 0;
210
211        // Tests using java.util.SerivceLoader
212        if (!javaUtilServiceLoaderTest(baseURL, messageService, true, false, true)) {
213            System.out.println("Test: ServiceLoader looking for " + messageService + ", failed");
214            failed++;
215        }
216        if (!javaUtilServiceLoaderTest(baseURL, unknownService, false, false, false)) {
217            System.out.println("Test: ServiceLoader looking for " + unknownService + " failed");
218            failed++;
219        }
220
221        // Tests using java.lang.Class (similar to the FontManager in javafx)
222        if (!klassLoader(baseURL, "/META-INF/fonts.mf", true, false, true)) {
223            System.out.println("Test: klassLoader looking for /META-INF/fonts.mf failed");
224            failed++;
225        }
226        if (!klassLoader(baseURL, "/META-INF/unknown.mf", false, false, false)) {
227            System.out.println("Test: klassLoader looking for /META-INF/unknown.mf failed");
228            failed++;
229        }
230
231        if (failed > 0)
232            throw new RuntimeException("Failed: " + failed + " tests");
233    }
234
235    static boolean javaUtilServiceLoaderTest(URL baseURL,
236                                             String serviceClass,
237                                             boolean expectToFind,
238                                             boolean expectbDotJar,
239                                             boolean expectcDotJar) throws IOException {
240        debug("----------------------------------");
241        debug("Running test with java.util.ServiceLoader looking for " + serviceClass);
242        URLClassLoader loader = getLoader(baseURL);
243        httpServer.reset();
244
245        Class<?> messageServiceClass = null;
246        try {
247            messageServiceClass = loader.loadClass(serviceClass);
248        } catch (ClassNotFoundException cnfe) {
249            System.err.println(cnfe);
250            throw new RuntimeException("Error in test: " + cnfe);
251        }
252
253        Iterator<?> iterator = (ServiceLoader.load(messageServiceClass, loader)).iterator();
254        if (expectToFind && !iterator.hasNext()) {
255            debug(messageServiceClass + " NOT found.");
256            return false;
257        }
258
259        while (iterator.hasNext()) {
260            debug("found " + iterator.next() + " " + messageService);
261        }
262
263        debug("HttpServer: " + httpServer);
264
265        if (!expectbDotJar && httpServer.bDotJar > 0) {
266            debug("Unexpeced request sent to the httpserver for b.jar");
267            return false;
268        }
269        if (!expectcDotJar && httpServer.cDotJar > 0) {
270            debug("Unexpeced request sent to the httpserver for c.jar");
271            return false;
272        }
273
274        return true;
275    }
276
277    /* Tries to find a resource in a similar way to the font manager in javafx
278     * com.sun.javafx.scene.text.FontManager */
279    static boolean klassLoader(URL baseURL,
280                               String resource,
281                               boolean expectToFind,
282                               boolean expectbDotJar,
283                               boolean expectcDotJar) throws IOException {
284        debug("----------------------------------");
285        debug("Running test looking for " + resource);
286        URLClassLoader loader = getLoader(baseURL);
287        httpServer.reset();
288
289        Class<?> ADotAKlass = null;
290        try {
291            ADotAKlass = loader.loadClass("a.A");
292        } catch (ClassNotFoundException cnfe) {
293            System.err.println(cnfe);
294            throw new RuntimeException("Error in test: " + cnfe);
295        }
296
297        URL u = ADotAKlass.getResource(resource);
298        if (expectToFind && u == null) {
299            System.out.println("Expected to find " + resource + " but didn't");
300            return false;
301        }
302
303        debug("HttpServer: " + httpServer);
304
305        if (!expectbDotJar && httpServer.bDotJar > 0) {
306            debug("Unexpeced request sent to the httpserver for b.jar");
307            return false;
308        }
309        if (!expectcDotJar && httpServer.cDotJar > 0) {
310            debug("Unexpeced request sent to the httpserver for c.jar");
311            return false;
312        }
313
314        return true;
315    }
316
317    static URLClassLoader getLoader(URL baseURL) throws IOException {
318        ClassLoader loader = Basic.class.getClassLoader();
319
320        while (loader.getParent() != null)
321            loader = loader.getParent();
322
323        return new URLClassLoader( new URL[]{
324            new URL(baseURL, "a.jar"),
325            new URL(baseURL, "b.jar"),
326            new URL(baseURL, "c.jar")}, loader );
327    }
328
329    /**
330     * HTTP Server to server the jar files.
331     */
332    static class JarHttpServer implements HttpHandler {
333        final String docsDir;
334        final HttpServer httpServer;
335        int aDotJar, bDotJar, cDotJar;
336
337        JarHttpServer(String docsDir) throws IOException {
338            this.docsDir = docsDir;
339
340            httpServer = HttpServer.create(new InetSocketAddress(0), 0);
341            httpServer.createContext("/", this);
342        }
343
344        void start() throws IOException {
345            httpServer.start();
346        }
347
348        void stop(int delay) {
349            httpServer.stop(delay);
350        }
351
352        InetSocketAddress getAddress() {
353            return httpServer.getAddress();
354        }
355
356        void reset() {
357            aDotJar = bDotJar = cDotJar = 0;
358        }
359
360        @Override
361        public String toString() {
362            return "aDotJar=" + aDotJar + ", bDotJar=" + bDotJar + ", cDotJar=" + cDotJar;
363        }
364
365        public void handle(HttpExchange t) throws IOException {
366            InputStream is = t.getRequestBody();
367            Headers map = t.getRequestHeaders();
368            Headers rmap = t.getResponseHeaders();
369            URI uri = t.getRequestURI();
370
371            debug("Server: received request for " + uri);
372            String path = uri.getPath();
373            if (path.endsWith("a.jar"))
374                aDotJar++;
375            else if (path.endsWith("b.jar"))
376                bDotJar++;
377            else if (path.endsWith("c.jar"))
378                cDotJar++;
379            else
380                System.out.println("Unexpected resource request" + path);
381
382            while (is.read() != -1);
383            is.close();
384
385            File file = new File(docsDir, path);
386            if (!file.exists())
387                throw new RuntimeException("Error: request for " + file);
388            long clen = file.length();
389            t.sendResponseHeaders (200, clen);
390            OutputStream os = t.getResponseBody();
391            FileInputStream fis = new FileInputStream(file);
392            try {
393                byte[] buf = new byte [16 * 1024];
394                int len;
395                while ((len=fis.read(buf)) != -1) {
396                    os.write (buf, 0, len);
397                }
398            } catch (IOException e) {
399                e.printStackTrace();
400            }
401            fis.close();
402            os.close();
403        }
404    }
405}
406