View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *   http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.bcel.classfile;
18  
19  import static org.junit.jupiter.api.Assertions.assertEquals;
20  import static org.junit.jupiter.api.Assertions.assertTrue;
21  
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.net.URL;
25  import java.util.ArrayList;
26  import java.util.Arrays;
27  import java.util.HashMap;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.stream.Stream;
31  
32  import org.apache.bcel.Const;
33  import org.apache.commons.lang3.stream.Streams;
34  import org.junit.jupiter.params.ParameterizedTest;
35  import org.junit.jupiter.params.provider.MethodSource;
36  
37  /**
38   * Tests {@code module-info.class} files.
39   */
40  public final class ConstantPoolModuleAccessTestCase {
41  
42      static Stream<URL> testJREModules() throws IOException {
43          return Streams.of(ConstantPoolModuleAccessTestCase.class.getClassLoader().getResources("module-info.class"));
44      }
45  
46      @ParameterizedTest
47      @MethodSource
48      public void testJREModules(final URL url) throws Exception {
49          try (InputStream inputStream = url.openStream()) {
50              final ClassParser classParser = new ClassParser(inputStream, "module-info.class");
51              final JavaClass javaClass = classParser.parse();
52              final ConstantPool constantPool = javaClass.getConstantPool();
53              final EmptyVisitor visitor = new EmptyVisitor() {
54                  @Override
55                  public void visitModule(final Module obj) {
56                      final String urlPath = url.getPath();
57                      if (urlPath.contains("/commons-")) {
58                          assertEquals(4096, obj.getModuleFlags(), url.toString());
59                      } else {
60                          assertEquals(0, obj.getModuleFlags(), url.toString());
61                      }
62                      final String[] usedClassNames = obj.getUsedClassNames(constantPool, true);
63                      if (urlPath.contains("junit-jupiter-engine")) {
64                          assertEquals(1, usedClassNames.length);
65                          assertEquals("org.junit.jupiter.api.extension.Extension", usedClassNames[0]);
66                      } else if (urlPath.contains("junit-platform-launcher")) {
67                          final List<String> expected = new ArrayList<>();
68                          expected.add("org.junit.platform.engine.TestEngine");
69                          expected.add("org.junit.platform.launcher.LauncherDiscoveryListener");
70                          expected.add("org.junit.platform.launcher.LauncherInterceptor");
71                          expected.add("org.junit.platform.launcher.LauncherSessionListener");
72                          expected.add("org.junit.platform.launcher.PostDiscoveryFilter");
73                          expected.add("org.junit.platform.launcher.TestExecutionListener");
74                          assertEquals(expected, Arrays.asList(usedClassNames));
75                      } else if (urlPath.contains("junit-platform-engine")) {
76                          final List<String> expected = new ArrayList<>();
77                          expected.add("org.junit.platform.engine.discovery.DiscoverySelectorIdentifierParser");
78                          assertEquals(expected, Arrays.asList(usedClassNames));
79                      } else if (urlPath.contains("/java.rmi/module-info.class")) {
80                          final List<String> expected = new ArrayList<>();
81                          expected.add("java.rmi.server.RMIClassLoaderSpi");
82                          assertEquals(expected, Arrays.asList(usedClassNames));
83                      } else if (urlPath.contains("/java.xml/module-info.class")) {
84                          final List<String> expected = new ArrayList<>();
85                          expected.add("javax.xml.datatype.DatatypeFactory");
86                          expected.add("javax.xml.parsers.DocumentBuilderFactory");
87                          expected.add("javax.xml.parsers.SAXParserFactory");
88                          expected.add("javax.xml.stream.XMLEventFactory");
89                          expected.add("javax.xml.stream.XMLInputFactory");
90                          expected.add("javax.xml.stream.XMLOutputFactory");
91                          expected.add("javax.xml.transform.TransformerFactory");
92                          expected.add("javax.xml.validation.SchemaFactory");
93                          expected.add("javax.xml.xpath.XPathFactory");
94                          expected.add("org.xml.sax.XMLReader");
95                          assertEquals(expected, Arrays.asList(usedClassNames));
96                      } else if (urlPath.contains("/java.datatransfer/module-info.class")) {
97                          final List<String> expected = new ArrayList<>();
98                          expected.add("sun.datatransfer.DesktopDatatransferService");
99                          assertEquals(expected, Arrays.asList(usedClassNames));
100                     } else if (urlPath.contains("/java.desktop/module-info.class")) {
101                         final List<String> expected = new ArrayList<>();
102                         expected.add("java.awt.im.spi.InputMethodDescriptor");
103                         expected.add("javax.accessibility.AccessibilityProvider");
104                         expected.add("javax.imageio.spi.ImageInputStreamSpi");
105                         expected.add("javax.imageio.spi.ImageOutputStreamSpi");
106                         expected.add("javax.imageio.spi.ImageReaderSpi");
107                         expected.add("javax.imageio.spi.ImageTranscoderSpi");
108                         expected.add("javax.imageio.spi.ImageWriterSpi");
109                         expected.add("javax.print.PrintServiceLookup");
110                         expected.add("javax.print.StreamPrintServiceFactory");
111                         expected.add("javax.sound.midi.spi.MidiDeviceProvider");
112                         expected.add("javax.sound.midi.spi.MidiFileReader");
113                         expected.add("javax.sound.midi.spi.MidiFileWriter");
114                         expected.add("javax.sound.midi.spi.SoundbankReader");
115                         expected.add("javax.sound.sampled.spi.AudioFileReader");
116                         expected.add("javax.sound.sampled.spi.AudioFileWriter");
117                         expected.add("javax.sound.sampled.spi.FormatConversionProvider");
118                         expected.add("javax.sound.sampled.spi.MixerProvider");
119                         expected.add("sun.swing.InteropProvider");
120                         assertEquals(expected, Arrays.asList(usedClassNames));
121                     } else if (urlPath.contains("/java.naming/module-info.class")) {
122                         final List<String> expected = new ArrayList<>();
123                         expected.add("javax.naming.ldap.StartTlsResponse");
124                         expected.add("javax.naming.spi.InitialContextFactory");
125                         if (javaClass.getMajor() > Const.MAJOR_11) {
126                             expected.add("javax.naming.ldap.spi.LdapDnsProvider");
127                         }
128                         assertEquals(expected, Arrays.asList(usedClassNames));
129                     } else if (urlPath.contains("/java.prefs/module-info.class")) {
130                         final List<String> expected = new ArrayList<>();
131                         expected.add("java.util.prefs.PreferencesFactory");
132                         assertEquals(expected, Arrays.asList(usedClassNames));
133                     } else if (urlPath.contains("/java.base/module-info.class")) {
134                         final List<String> expected = new ArrayList<>();
135                         expected.add("java.lang.System$LoggerFinder");
136                         expected.add("java.net.ContentHandlerFactory");
137                         if (javaClass.getMajor() > Const.MAJOR_17) {
138                             expected.add("java.net.spi.InetAddressResolverProvider");
139                         }
140                         expected.add("java.net.spi.URLStreamHandlerProvider");
141                         expected.add("java.nio.channels.spi.AsynchronousChannelProvider");
142                         expected.add("java.nio.channels.spi.SelectorProvider");
143                         expected.add("java.nio.charset.spi.CharsetProvider");
144                         expected.add("java.nio.file.spi.FileSystemProvider");
145                         expected.add("java.nio.file.spi.FileTypeDetector");
146                         expected.add("java.security.Provider");
147                         expected.add("java.text.spi.BreakIteratorProvider");
148                         expected.add("java.text.spi.CollatorProvider");
149                         expected.add("java.text.spi.DateFormatProvider");
150                         expected.add("java.text.spi.DateFormatSymbolsProvider");
151                         expected.add("java.text.spi.DecimalFormatSymbolsProvider");
152                         expected.add("java.text.spi.NumberFormatProvider");
153                         expected.add("java.time.chrono.AbstractChronology");
154                         expected.add("java.time.chrono.Chronology");
155                         expected.add("java.time.zone.ZoneRulesProvider");
156                         if (javaClass.getMajor() > Const.MAJOR_11) {
157                             expected.add("java.util.random.RandomGenerator");
158                         }
159                         expected.add("java.util.spi.CalendarDataProvider");
160                         expected.add("java.util.spi.CalendarNameProvider");
161                         expected.add("java.util.spi.CurrencyNameProvider");
162                         expected.add("java.util.spi.LocaleNameProvider");
163                         expected.add("java.util.spi.ResourceBundleControlProvider");
164                         expected.add("java.util.spi.ResourceBundleProvider");
165                         expected.add("java.util.spi.TimeZoneNameProvider");
166                         expected.add("java.util.spi.ToolProvider");
167                         expected.add("javax.security.auth.spi.LoginModule");
168                         if (javaClass.getMajor() > Const.MAJOR_17) {
169                             expected.add("jdk.internal.io.JdkConsoleProvider");
170                         }
171                         expected.add("jdk.internal.logger.DefaultLoggerFinder");
172                         expected.add("sun.text.spi.JavaTimeDateTimePatternProvider");
173                         expected.add("sun.util.locale.provider.LocaleDataMetaInfo");
174                         expected.add("sun.util.resources.LocaleData$CommonResourceBundleProvider");
175                         expected.add("sun.util.resources.LocaleData$SupplementaryResourceBundleProvider");
176                         expected.add("sun.util.spi.CalendarProvider");
177                         assertEquals(expected, Arrays.asList(usedClassNames));
178                     } else if (urlPath.contains("/jdk.management.agent/module-info.class") && javaClass.getMajor() < Const.MAJOR_21) {
179                         final List<String> expected = new ArrayList<>();
180                         expected.add("jdk.internal.agent.spi.AgentProvider");
181                         assertEquals(expected, Arrays.asList(usedClassNames));
182                     } else if (urlPath.contains("/java.management/module-info.class")) {
183                         final List<String> expected = new ArrayList<>();
184                         expected.add("javax.management.remote.JMXConnectorProvider");
185                         expected.add("javax.management.remote.JMXConnectorServerProvider");
186                         expected.add("sun.management.spi.PlatformMBeanProvider");
187                         assertEquals(expected, Arrays.asList(usedClassNames));
188                     } else if (urlPath.contains("/java.sql/module-info.class")) {
189                         final List<String> expected = new ArrayList<>();
190                         expected.add("java.sql.Driver");
191                         assertEquals(expected, Arrays.asList(usedClassNames));
192                     } else if (urlPath.contains("/jdk.httpserver/module-info.class")) {
193                         final List<String> expected = new ArrayList<>();
194                         expected.add("com.sun.net.httpserver.spi.HttpServerProvider");
195                         assertEquals(expected, Arrays.asList(usedClassNames));
196                     } else if (urlPath.contains("/java.sql.rowset/module-info.class")) {
197                         final List<String> expected = new ArrayList<>();
198                         expected.add("javax.sql.rowset.RowSetFactory");
199                         assertEquals(expected, Arrays.asList(usedClassNames));
200                     } else if (urlPath.contains("/java.compiler/module-info.class")) {
201                         final List<String> expected = new ArrayList<>();
202                         expected.add("javax.tools.DocumentationTool");
203                         expected.add("javax.tools.JavaCompiler");
204                         assertEquals(expected, Arrays.asList(usedClassNames));
205                     } else if (urlPath.contains("/java.scripting/module-info.class")) {
206                         final List<String> expected = new ArrayList<>();
207                         expected.add("javax.script.ScriptEngineFactory");
208                         assertEquals(expected, Arrays.asList(usedClassNames));
209                     } else if (urlPath.contains("/jdk.dynalink/module-info.class")) {
210                         final List<String> expected = new ArrayList<>();
211                         expected.add("jdk.dynalink.linker.GuardingDynamicLinkerExporter");
212                         assertEquals(expected, Arrays.asList(usedClassNames));
213                     } else if (urlPath.contains("/jdk.jdi/module-info.class")) {
214                         final List<String> expected = new ArrayList<>();
215                         expected.add("com.sun.jdi.connect.Connector");
216                         expected.add("com.sun.jdi.connect.spi.TransportService");
217                         assertEquals(expected, Arrays.asList(usedClassNames));
218                     } else if (urlPath.contains("/jdk.compiler/module-info.class")) {
219                         final List<String> expected = new ArrayList<>();
220                         expected.add("javax.annotation.processing.Processor");
221                         expected.add("com.sun.source.util.Plugin");
222                         if (javaClass.getMajor() > Const.MAJOR_11) {
223                             expected.add("com.sun.tools.doclint.DocLint");
224                         }
225                         expected.add("com.sun.tools.javac.platform.PlatformProvider");
226                         assertEquals(expected, Arrays.asList(usedClassNames));
227                     } else if (urlPath.contains("/jdk.jconsole/module-info.class")) {
228                         final List<String> expected = new ArrayList<>();
229                         expected.add("com.sun.tools.jconsole.JConsolePlugin");
230                         assertEquals(expected, Arrays.asList(usedClassNames));
231                     } else if (urlPath.contains("/jdk.attach/module-info.class")) {
232                         final List<String> expected = new ArrayList<>();
233                         expected.add("com.sun.tools.attach.spi.AttachProvider");
234                         assertEquals(expected, Arrays.asList(usedClassNames));
235                     } else if (urlPath.contains("/jdk.jshell/module-info.class")) {
236                         final List<String> expected = new ArrayList<>();
237                         expected.add("jdk.jshell.spi.ExecutionControlProvider");
238                         expected.add("jdk.internal.editor.spi.BuildInEditorProvider");
239                         assertEquals(expected, Arrays.asList(usedClassNames));
240                     } else if (urlPath.contains("/jdk.internal.le/module-info.class")) {
241                         final List<String> expected = new ArrayList<>();
242                         assertEquals(expected, Arrays.asList(usedClassNames));
243                     } else if (urlPath.contains("/jdk.jlink/module-info.class")) {
244                         final List<String> expected = new ArrayList<>();
245                         expected.add("jdk.tools.jlink.plugin.Plugin");
246                         assertEquals(expected, Arrays.asList(usedClassNames));
247                     } else if (urlPath.contains("/jdk.internal.jvmstat/module-info.class")) {
248                         final List<String> expected = new ArrayList<>();
249                         expected.add("sun.jvmstat.monitor.MonitoredHostService");
250                         assertEquals(expected, Arrays.asList(usedClassNames));
251                     } else if (urlPath.contains("/jdk.jpackage/module-info.class")) {
252                         final List<String> expected = new ArrayList<>();
253                         expected.add("jdk.jpackage.internal.Bundler");
254                         expected.add("jdk.jpackage.internal.Bundlers");
255                         assertEquals(expected, Arrays.asList(usedClassNames));
256                     } else if (urlPath.contains("/jdk.naming.ldap/module-info.class")) {
257                         final List<String> expected = new ArrayList<>();
258                         expected.add("com.sun.jndi.ldap.spi.LdapDnsProvider");
259                         assertEquals(expected, Arrays.asList(usedClassNames));
260                     } else if (urlPath.contains("/jdk.jsobject/module-info.class") && javaClass.getMajor() == Const.MAJOR_11) {
261                         final List<String> expected = new ArrayList<>();
262                         expected.add("jdk.internal.netscape.javascript.spi.JSObjectProvider");
263                         assertEquals(expected, Arrays.asList(usedClassNames));
264                     } else if (urlPath.contains("/org/assertj/assertj-core/")) {
265                         final List<String> expected = new ArrayList<>();
266                         expected.add("org.assertj.core.configuration.Configuration");
267                         expected.add("org.assertj.core.presentation.Representation");
268                         assertEquals(expected, Arrays.asList(usedClassNames));
269                     } else {
270                         assertEquals(0, usedClassNames.length, "Found " + Arrays.toString(usedClassNames) + " in " + urlPath);
271                     }
272                     super.visitModule(obj);
273                 }
274 
275                 @Override
276                 public void visitModuleExports(final ModuleExports obj) {
277                     assertEquals(0, obj.getExportsFlags(), url.toString());
278                     final String packageName = obj.getPackageName(constantPool);
279                     final String[] toModuleNames = obj.getToModuleNames(constantPool);
280                     if (url.getPath().contains("junit-platform-commons")) {
281                         final List<String> expected = new ArrayList<>();
282                         expected.add("org.junit.jupiter.api");
283                         expected.add("org.junit.jupiter.engine");
284                         expected.add("org.junit.jupiter.migrationsupport");
285                         expected.add("org.junit.jupiter.params");
286                         expected.add("org.junit.platform.console");
287                         expected.add("org.junit.platform.engine");
288                         expected.add("org.junit.platform.launcher");
289                         expected.add("org.junit.platform.reporting");
290                         expected.add("org.junit.platform.runner");
291                         expected.add("org.junit.platform.suite.api");
292                         switch (packageName) {
293                         case "org.junit.platform.commons.util":
294                             expected.add("org.junit.platform.suite.commons");
295                             expected.add("org.junit.platform.suite.engine");
296                             expected.add("org.junit.platform.testkit");
297                             expected.add("org.junit.vintage.engine");
298                             assertEquals(expected, Arrays.asList(toModuleNames));
299                             break;
300                         case "org.junit.platform.commons.logging":
301                             expected.add("org.junit.platform.suite.engine");
302                             expected.add("org.junit.platform.testkit");
303                             expected.add("org.junit.vintage.engine");
304                             assertEquals(expected, Arrays.asList(toModuleNames));
305                             break;
306                         default:
307                             assertEquals(0, toModuleNames.length);
308                             break;
309                         }
310                     }
311                     super.visitModuleExports(obj);
312                 }
313 
314                 @Override
315                 public void visitModuleOpens(final ModuleOpens obj) {
316                     assertEquals(0, obj.getOpensFlags(), url.toString());
317                     final String packageName = obj.getPackageName(constantPool);
318                     final String[] toModuleNames = obj.getToModuleNames(constantPool);
319                     final String urlPath = url.getPath();
320                     if (urlPath.contains("junit-jupiter-engine")) {
321                         assertEquals("org.junit.jupiter.engine.extension", packageName);
322                         assertEquals(1, toModuleNames.length);
323                         assertEquals("org.junit.platform.commons", toModuleNames[0]);
324                     } else if (urlPath.contains("junit-jupiter-api")) {
325                         assertEquals("org.junit.jupiter.api.condition", packageName);
326                         assertEquals(1, toModuleNames.length);
327                         assertEquals("org.junit.platform.commons", toModuleNames[0]);
328                     }
329                     super.visitModuleOpens(obj);
330                 }
331 
332                 @Override
333                 public void visitModuleProvides(final ModuleProvides obj) {
334                     final String interfaceName = obj.getInterfaceName(constantPool);
335                     final String[] implementationClassNames = obj.getImplementationClassNames(constantPool, true);
336                     final String urlPath = url.getPath();
337                     if (urlPath.contains("junit-jupiter-engine")) {
338                         assertEquals("org.junit.platform.engine.TestEngine", interfaceName);
339                         assertEquals(1, implementationClassNames.length);
340                         assertEquals("org.junit.jupiter.engine.JupiterTestEngine", implementationClassNames[0]);
341                     } else if (urlPath.contains("junit-platform-launcher")) {
342                         assertEquals("org.junit.platform.launcher.TestExecutionListener", interfaceName);
343                         assertEquals(1, implementationClassNames.length);
344                         assertEquals("org.junit.platform.launcher.listeners.UniqueIdTrackingListener", implementationClassNames[0]);
345                     }
346                     super.visitModuleProvides(obj);
347                 }
348 
349                 @Override
350                 public void visitModuleRequires(final ModuleRequires obj) {
351                     if (url.getPath().contains("junit-jupiter-engine")) {
352                         final String moduleName = obj.getModuleName(constantPool);
353                         final Map<String, Integer> expected = new HashMap<>();
354                         expected.put("java.base", 32768);
355                         expected.put("org.apiguardian.api", 64);
356                         expected.put("org.junit.jupiter.api", 0);
357                         expected.put("org.junit.platform.commons", 0);
358                         expected.put("org.junit.platform.engine", 0);
359                         expected.put("org.opentest4j", 0);
360                         assertTrue(expected.containsKey(moduleName));
361                         assertEquals(expected.get(moduleName), obj.getRequiresFlags(), moduleName);
362                     }
363                     super.visitModuleRequires(obj);
364                 }
365             };
366             javaClass.accept(new DescendingVisitor(javaClass, visitor));
367         }
368     }
369 }