1/*
2 * Copyright (c) 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 * @summary Test Package object is local to each ClassLoader.
27 *          There can be one Package object of "foo" name defined by
28 *          different class loader.
29 * @compile Foo.java
30 * @run testng GetPackages
31 */
32
33import java.io.IOException;
34import java.io.UncheckedIOException;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.util.Arrays;
39import java.lang.reflect.*;
40
41import org.testng.annotations.DataProvider;
42import org.testng.annotations.Test;
43import static org.testng.Assert.*;
44
45public class GetPackages {
46    final TestClassLoader loader;
47    final Class<?> fooClass;
48    /*
49     * Each TestClassLoader defines a "foo.Foo" class which has a unique
50     * Package object of "foo" regardless whether its ancestor class loader
51     * defines a package "foo" or not.
52     */
53    GetPackages(TestClassLoader loader) throws ClassNotFoundException {
54        this.loader = loader;
55        this.fooClass = loader.loadClass("foo.Foo");
56    }
57
58    /*
59     * Check package "foo" defined locally in the TestClassLoader
60     * as well as its ancestors.
61     */
62    void checkPackage() throws ClassNotFoundException {
63        // Name of an unnamed package is empty string
64        assertEquals(this.getClass().getPackage().getName(), "");
65
66        assertEquals(fooClass.getClassLoader(), loader);
67
68        Package p = loader.getDefinedPackage("foo");
69        assertEquals(p.getName(), "foo");
70        assertEquals(p, loader.getPackage("foo"));
71
72        if (loader.getParent() != null) {
73            Package p2 = ((TestClassLoader)loader.getParent()).getDefinedPackage("foo");
74            assertTrue(p != p2);
75        }
76
77        long count = Arrays.stream(loader.getDefinedPackages())
78                            .map(Package::getName)
79                            .filter(pn -> pn.equals("foo"))
80                            .count();
81        assertEquals(count, 1);
82    }
83
84    /*
85     * Check the number of package "foo" defined to this class loader and
86     * its ancestors
87     */
88    Package[] getPackagesFromLoader() {
89        return loader.packagesInClassLoaderChain();
90    }
91
92    /*
93     * Package.getPackages is caller-sensitve.  Call through Foo class
94     * to find all Packages visible to this TestClassLoader and its ancestors
95     */
96    Package[] getPackagesFromFoo() throws Exception {
97        Method m = fooClass.getMethod("getPackages");
98        return (Package[])m.invoke(null);
99    }
100
101    private static long numFooPackages(Package[] pkgs) throws Exception {
102        return Arrays.stream(pkgs)
103                     .filter(p -> p.getName().equals("foo"))
104                     .count();
105    }
106
107    @DataProvider(name = "loaders")
108    public static Object[][] testLoaders() {
109        TestClassLoader loader1 = new TestClassLoader(null);
110        TestClassLoader loader2 = new TestClassLoader(loader1);
111        TestClassLoader loader3 = new TestClassLoader(loader2);
112
113        // Verify the number of expected Package object of "foo" visible
114        // to the class loader
115        return new Object[][] {
116                { loader1, 1 },
117                { loader2, 2 },
118                { loader3, 3 }
119        };
120    }
121
122    @Test(dataProvider = "loaders")
123    public static void test(TestClassLoader loader, long expected) throws Exception {
124        GetPackages test = new GetPackages(loader);
125        // check package "foo" existence
126        test.checkPackage();
127
128        assertEquals(numFooPackages(test.getPackagesFromLoader()), expected);
129        assertEquals(numFooPackages(test.getPackagesFromFoo()), expected);
130    }
131}
132
133class TestClassLoader extends ClassLoader {
134    public TestClassLoader() {
135        super();
136    }
137
138    public TestClassLoader(ClassLoader parent) {
139        super(parent);
140    }
141
142    public Package getPackage(String pn) {
143        return super.getPackage(pn);
144    }
145
146    public Package[] packagesInClassLoaderChain() {
147        return super.getPackages();
148    }
149
150    @Override
151    protected Class<?> findClass(String name) throws ClassNotFoundException {
152        Path p = Paths.get(System.getProperty("test.classes", "."));
153
154        try {
155            byte[] bb = Files.readAllBytes(p.resolve("foo/Foo.class"));
156            return defineClass(name, bb, 0, bb.length);
157        } catch (IOException e) {
158            throw new UncheckedIOException(e);
159        }
160    }
161    @Override
162    protected Class<?> loadClass(String cn, boolean resolve) throws ClassNotFoundException {
163        if (!cn.equals("foo.Foo"))
164            return super.loadClass(cn, resolve);
165        return findClass(cn);
166    }
167}
168