1/*
2 * Copyright (c) 2013, 2014, 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 8009411
27 * @summary Test that a static method on an interface doesn't hide a default
28 *          method with the same name and signature in a separate compilation
29 *          scenario.
30 * @library /lib/testlibrary
31 * @build jdk.testlibrary.IOUtils
32 * @run main StaticInterfaceMethodInWayOfDefault
33 */
34
35import java.io.IOException;
36import java.io.InputStream;
37import java.lang.reflect.InvocationTargetException;
38import java.lang.reflect.Method;
39import java.util.concurrent.Callable;
40
41import jdk.testlibrary.IOUtils;
42
43public class StaticInterfaceMethodInWayOfDefault {
44    public interface A_v1 {
45    }
46
47    public interface A_v2 {
48        default void m() {
49            System.err.println("A.m() called");
50        }
51    }
52
53    public interface B  extends A_v1 {
54        static void m() {
55            System.err.println("B.m() called");
56        }
57    }
58
59    public interface C_v1 extends B {
60        default void m() {
61            System.err.println("C.m() called");
62        }
63    }
64
65    public interface C_v2 extends B {
66    }
67
68    public static class TestTask implements Callable<String> {
69        @Override
70        public String call() {
71            try {
72                Method m = C_v1.class.getMethod("m", (Class<?>[])null);
73                return  m.getDeclaringClass().getSimpleName();
74            } catch (NoSuchMethodException e) {
75                System.err.println("Couldn't find method");
76                return "ERROR";
77            }
78        }
79    }
80
81    public static void main(String[] args) throws Exception {
82        int errors = 0;
83        Callable<String> v1Task = new TestTask();
84
85        ClassLoader v2Loader = new V2ClassLoader(
86            StaticInterfaceMethodInWayOfDefault.class.getClassLoader());
87        Callable<String> v2Task = (Callable<String>) Class.forName(
88            TestTask.class.getName(),
89            true,
90            v2Loader).newInstance();
91
92        System.err.println("Running using _v1 classes:");
93        String res = v1Task.call();
94        if(!res.equals("C_v1")) {
95            System.err.println("Got wrong method, expecting C_v1, got: " + res);
96            errors++;
97        }
98
99        System.err.println("Running using _v2 classes:");
100        res = v2Task.call();
101        if(!res.equals("A_v1")) {
102            System.err.println("Got wrong method, expecting A_v1, got: " + res);
103            errors++;
104        }
105
106        if (errors != 0)
107            throw new RuntimeException("Errors found, check log for details");
108    }
109
110    /**
111     * A ClassLoader implementation that loads alternative implementations of
112     * classes. If class name ends with "_v1" it locates instead a class with
113     * name ending with "_v2" and loads that class instead.
114     */
115    static class V2ClassLoader extends ClassLoader {
116        V2ClassLoader(ClassLoader parent) {
117            super(parent);
118        }
119
120        @Override
121        protected Class<?> loadClass(String name, boolean resolve)
122            throws ClassNotFoundException {
123            if (name.indexOf('.') < 0) { // root package is our class
124                synchronized (getClassLoadingLock(name)) {
125                    // First, check if the class has already been loaded
126                    Class<?> c = findLoadedClass(name);
127                    if (c == null) {
128                        c = findClass(name);
129                    }
130                    if (resolve) {
131                        resolveClass(c);
132                    }
133                    return c;
134                }
135            }
136            else { // not our class
137                return super.loadClass(name, resolve);
138            }
139        }
140
141        @Override
142        protected Class<?> findClass(String name)
143            throws ClassNotFoundException {
144            // special class name -> replace it with alternative name
145            if (name.endsWith("_v1")) {
146                String altName = name.substring(0, name.length() - 3) + "_v2";
147                String altPath = altName.replace('.', '/').concat(".class");
148                try (InputStream is = getResourceAsStream(altPath)) {
149                    if (is != null) {
150                        byte[] bytes = IOUtils.readFully(is);
151                        // patch class bytes to contain original name
152                        for (int i = 0; i < bytes.length - 2; i++) {
153                            if (bytes[i] == '_' &&
154                                bytes[i + 1] == 'v' &&
155                                bytes[i + 2] == '2') {
156                                bytes[i + 2] = '1';
157                            }
158                        }
159                        return defineClass(name, bytes, 0, bytes.length);
160                    }
161                    else {
162                        throw new ClassNotFoundException(name);
163                    }
164                }
165                catch (IOException e) {
166                    throw new ClassNotFoundException(name, e);
167                }
168            }
169            else { // not special class name -> just load the class
170                String path = name.replace('.', '/').concat(".class");
171                try (InputStream is = getResourceAsStream(path)) {
172                    if (is != null) {
173                        byte[] bytes = IOUtils.readFully(is);
174                        return defineClass(name, bytes, 0, bytes.length);
175                    }
176                    else {
177                        throw new ClassNotFoundException(name);
178                    }
179                }
180                catch (IOException e) {
181                    throw new ClassNotFoundException(name, e);
182                }
183            }
184        }
185    }
186}
187