1/*
2 * Copyright (c) 2016, 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
24import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;
25
26import java.lang.module.ModuleDescriptor.Provides;
27import java.util.Arrays;
28import java.util.HashSet;
29import java.util.Set;
30import java.util.stream.Stream;
31
32import org.xml.sax.helpers.XMLReaderFactory;
33
34public class Main {
35    /*
36     * @param args, the names of provider modules, which have been loaded
37     */
38    public static void main(String[] args) throws Exception {
39        Module xml = ModuleLayer.boot().findModule("java.xml").get();
40
41        Set<String> allServices = new HashSet<>(Arrays.asList(expectedAllServices));
42        if (!allServices.equals(xml.getDescriptor().uses()))
43            throw new AssertionError("Expect xml module uses: " + allServices + " But actually uses: "
44                    + xml.getDescriptor().uses());
45
46        long violationCount = Stream.of(args)
47                .map(xmlProviderName -> ModuleLayer.boot().findModule(xmlProviderName).get())
48                .mapToLong(
49                        // services provided by the implementation in provider module
50                        provider -> provider.getDescriptor().provides().stream()
51                                .map(Provides::service)
52                                .filter(serviceName -> {
53                                    allServices.remove(serviceName); // remove service provided by
54                                                                     // customized module from allServices
55                                    return !belongToModule(serviceName, instantiateXMLService(serviceName), provider);
56                                }).count())
57                .sum();
58
59        // the remaining services should be provided by the default implementation
60        violationCount += allServices.stream()
61                .filter(serviceName -> !belongToModule(serviceName, instantiateXMLService(serviceName), xml))
62                .count();
63
64        if (violationCount > 0)
65            throw new AssertionError(violationCount + " services are not provided by expected module");
66    }
67
68    /*
69     * instantiate a xml factory by reflection e.g.
70     * DocumentBuilderFactory.newInstance()
71     */
72    private static Object instantiateXMLService(String serviceName) {
73        try {
74            if (serviceName.equals("org.xml.sax.XMLReader"))
75                return XMLReaderFactory.createXMLReader();
76            else if (serviceName.equals("javax.xml.validation.SchemaFactory"))
77                return Class.forName(serviceName).getMethod("newInstance", String.class)
78                        .invoke(null, W3C_XML_SCHEMA_NS_URI);
79            else
80                return Class.forName(serviceName).getMethod("newInstance").invoke(null);
81        } catch (Exception e) {
82            e.printStackTrace(System.err);
83            throw new RuntimeException(e);
84        }
85    }
86
87    /*
88     * verify which module provides the xml factory
89     */
90    private static boolean belongToModule(String factoryName, Object factory, Module expected) {
91        Module actual = factory.getClass().getModule();
92        if (!actual.equals(expected)) {
93            System.err.println("Expect " + factoryName + " is provided by " + expected
94                    + ", but actual implementation " + factory.getClass() + " is provided by " + actual);
95            return false;
96        } else {
97            System.out.println(factory.getClass() + " is provided by " + expected);
98            return true;
99        }
100    }
101
102    /*
103     * This list equals the declarations in java.xml module-info.java
104     */
105    private static final String[] expectedAllServices = { "javax.xml.datatype.DatatypeFactory",
106            "javax.xml.parsers.DocumentBuilderFactory", "javax.xml.parsers.SAXParserFactory",
107            "javax.xml.stream.XMLEventFactory", "javax.xml.stream.XMLInputFactory",
108            "javax.xml.stream.XMLOutputFactory", "javax.xml.transform.TransformerFactory",
109            "javax.xml.validation.SchemaFactory", "javax.xml.xpath.XPathFactory",
110            "org.xml.sax.XMLReader"};
111
112}
113