1 /*
  2  * Copyright (c) 2014, 2023, 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 8060130
 27  * @library /test/lib
 28  * @build package2.Class2 GetSystemPackage
 29  * @summary Test if getSystemPackage() return consistent values for cases
 30  *          where a manifest is provided or not and ensure only jars on
 31  *          bootclasspath gets resolved via Package.getSystemPackage
 32  * @run main GetSystemPackage
 33  */
 34 
 35 import java.io.File;
 36 import java.io.FileInputStream;
 37 import java.io.FileNotFoundException;
 38 import java.io.FileOutputStream;
 39 import java.io.IOException;
 40 import java.util.jar.Attributes;
 41 import java.util.jar.JarEntry;
 42 import java.util.jar.JarOutputStream;
 43 import java.util.jar.Manifest;
 44 import jdk.test.lib.process.ProcessTools;
 45 
 46 public class GetSystemPackage {
 47 
 48     static final String testClassesDir = System.getProperty("test.classes", ".");
 49     static final File tmpFolder = new File(testClassesDir);
 50     static final String manifestTitle = "Special JAR";
 51 
 52     public static void main(String ... args) throws Exception {
 53         if (args.length == 0) {
 54             buildJarsAndInitiateSystemPackageTest();
 55             return;
 56         }
 57         switch (args[0]) {
 58             case "system-manifest":
 59                 verifyPackage(true, true);
 60                 break;
 61             case "system-no-manifest":
 62                 verifyPackage(false, true);
 63                 break;
 64             case "non-system-manifest":
 65                 verifyPackage(true, false);
 66                 break;
 67             case "non-system-no-manifest":
 68             default:
 69                 verifyPackage(false, false);
 70                 break;
 71         }
 72     }
 73 
 74     private static void buildJarsAndInitiateSystemPackageTest()
 75             throws Exception
 76     {
 77         Manifest m = new Manifest();
 78         // not setting MANIFEST_VERSION prevents META-INF/MANIFEST.MF from
 79         // getting written
 80         m.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
 81         m.getMainAttributes().put(Attributes.Name.SPECIFICATION_TITLE,
 82                                   manifestTitle);
 83 
 84         buildJar("manifest.jar", m);
 85         buildJar("no-manifest.jar", null);
 86 
 87         runSubProcess("System package with manifest improperly resolved.",
 88                 "-Xbootclasspath/a:" + testClassesDir + "/manifest.jar",
 89                 "GetSystemPackage", "system-manifest");
 90 
 91         runSubProcess("System package from directory improperly resolved.",
 92                 "-Xbootclasspath/a:" + testClassesDir, "GetSystemPackage",
 93                 "system-no-manifest");
 94 
 95         runSubProcess("System package with no manifest improperly resolved",
 96                 "-Xbootclasspath/a:" + testClassesDir + "/no-manifest.jar",
 97                 "GetSystemPackage", "system-no-manifest");
 98 
 99         runSubProcess("Classpath package with manifest improperly resolved",
100                 "-cp", testClassesDir + "/manifest.jar", "GetSystemPackage",
101                 "non-system-manifest");
102 
103         runSubProcess("Classpath package with no manifest improperly resolved",
104                 "-cp", testClassesDir + "/no-manifest.jar", "GetSystemPackage",
105                 "non-system-no-manifest");
106 
107     }
108 
109     private static void buildJar(String name, Manifest man) throws Exception {
110         JarBuilder jar = new JarBuilder(tmpFolder, name, man);
111         jar.addClassFile("package2/Class2.class",
112                 testClassesDir + "/package2/Class2.class");
113         jar.addClassFile("GetSystemPackage.class",
114                 testClassesDir + "/GetSystemPackage.class");
115         jar.build();
116     }
117 
118     private static void runSubProcess(String messageOnError, String ... args)
119             throws Exception
120     {
121         ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder(args);
122         int res = pb.directory(tmpFolder).inheritIO().start().waitFor();
123         if (res != 0) {
124             throw new RuntimeException(messageOnError);
125         }
126     }
127 
128     private static void verifyPackage(boolean hasManifest,
129                                       boolean isSystemPackage)
130             throws Exception
131     {
132         Class<?> c = Class.forName("package2.Class2");
133         Package pkg = c.getPackage();
134         if (pkg == null || pkg != Package.getPackage("package2") ||
135                 !"package2".equals(pkg.getName())) {
136             fail("package2 not found via Package.getPackage()");
137         }
138 
139         String specificationTitle = pkg.getSpecificationTitle();
140         if (!"package2".equals(pkg.getName())) {
141             fail("Invalid package for Class2");
142         }
143 
144         if (hasManifest && (specificationTitle == null
145                 || !manifestTitle.equals(specificationTitle))) {
146             fail("Invalid manifest for package " + pkg.getName());
147         }
148         if (!hasManifest && specificationTitle != null) {
149             fail("Invalid manifest for package " + pkg.getName() + ": was " +
150                     specificationTitle + " expected: null");
151         }
152 
153         ClassLoader ld = c.getClassLoader();
154         Package systemPkg = ld != null ? null : Package.getPackage("package2");
155 
156         if (findPackage("java.lang") == null) {
157             fail("java.lang not found via Package.getPackages()");
158         }
159         Package foundPackage = findPackage("package2");
160         if (isSystemPackage) {
161             if (systemPkg == null) {
162                 fail("System package could not be found via getSystemPackage");
163             }
164             if (foundPackage != systemPkg || systemPkg != pkg) {
165                 fail("Inconsistent package found via Package.getPackages()");
166             }
167         } else {
168             if (systemPkg != null) {
169                 fail("Non-system package could be found via getSystemPackage");
170             }
171             if (foundPackage == null) {
172                 fail("Non-system package not found via Package.getPackages()");
173             }
174         }
175     }
176 
177     private static Package findPackage(String name) {
178         Package[] packages = Package.getPackages();
179         for (Package p : packages) {
180             if (p.getName().equals(name)) {
181                 return p;
182             }
183         }
184         return null;
185     }
186 
187     private static void fail(String message) {
188         throw new RuntimeException(message);
189     }
190 }
191 
192 /*
193  * Helper class for building jar files
194  */
195 class JarBuilder {
196 
197     private JarOutputStream os;
198 
199     public JarBuilder(File tmpFolder, String jarName, Manifest manifest)
200             throws FileNotFoundException, IOException
201     {
202         File jarFile = new File(tmpFolder, jarName);
203         if (manifest != null) {
204             this.os = new JarOutputStream(new FileOutputStream(jarFile),
205                                           manifest);
206         } else {
207             this.os = new JarOutputStream(new FileOutputStream(jarFile));
208         }
209     }
210 
211     public void addClassFile(String pathFromRoot, String file)
212             throws IOException
213     {
214         byte[] buf = new byte[1024];
215         try (FileInputStream in = new FileInputStream(file)) {
216             JarEntry entry = new JarEntry(pathFromRoot);
217             os.putNextEntry(entry);
218             int len;
219             while ((len = in.read(buf)) > 0) {
220                 os.write(buf, 0, len);
221             }
222             os.closeEntry();
223         }
224     }
225 
226     public void build() throws IOException {
227         os.close();
228     }
229 }