0

I have a complicated set of XSDs, so the end XSD has many <xsd:import> entries, so it requires a resource resolver to locate the referenced XSDs. The resource resolver then needs to be injected into the SchemaFactory. Simplified example:

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">

  <modelVersion>4.0.0</modelVersion>
  <groupId>com.ndg</groupId>
  <artifactId>dummy</artifactId>
  <name>NDG test project</name>
  <description>NDG test project</description>
  <version>0.0.1-SNAPSHOT</version>

    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.10.1</version>
                    <configuration>
                        <release>11</release>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>

  
  <dependencies>
        <dependency>
            <groupId>jakarta.xml.bind</groupId>
                <artifactId>jakarta.xml.bind-api</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
                <artifactId>jaxb-impl</artifactId>
            <version>4.0.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.22</version>
        </dependency>
  </dependencies>

</project>

Resolver.java

import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;

public class Resolver implements LSResourceResolver
{
    @Override
    public LSInput resolveResource (String type, String namespaceURI, String publicId, String systemId, String baseURI)
    {
        return null;
    }
}

Main.java

import javax.xml.validation.SchemaFactory;

public class Main
{
    public static final void main (final String [] args)
    {
        Resolver resolver = new Resolver ();
        
        SchemaFactory schemaFactory = SchemaFactory.newInstance ("http://www.w3.org/2001/XMLSchema");
        schemaFactory.setResourceResolver (resolver);
        
        System.out.println ("All ok");
    }
}

This runs fine, both under JDK 11 and JDK 17. But if I try to wire the application with Spring, like so:

spring-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >

    <bean id="resolver" class="Resolver" />
    
    <bean id="schemaFactory" class="javax.xml.validation.SchemaFactory" factory-method="newInstance">
        <constructor-arg value="http://www.w3.org/2001/XMLSchema" />
        <property name="resourceResolver" ref="resolver" />
    </bean>
    
</beans>

Spring.java

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Spring
{
    public static final void main (final String [] args)
    {
        new ClassPathXmlApplicationContext ("/spring-beans.xml");
        System.out.println ("All ok");
    }
}

Then on JDK 11 it outputs a warning:

WARNING: An illegal reflective access operation has occurred

WARNING: Illegal reflective access by org.springframework.beans.BeanWrapperImpl$BeanPropertyHandler (file:/W:/maven/repository/org/springframework/spring-beans/5.3.22/spring-beans-5.3.22.jar) to method com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.setResourceResolver(org.w3c.dom.ls.LSResourceResolver)

and on JDK 17 such usage of internal types is now illegal and so it fails completely. Note SchemaFactory is an abstract class - the concrete class at runtime is com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory as per the message, so the warning/error is technically correct in that the code is indeed trying to call setResourceResolver on an internal com.sun class.

What I don't understand is:

  • Why does it work directly in my Main class but not from Spring? Surely they're both just calling the same setResourceResolver method.
  • If it is a problem with Spring, then do they not have JDK 17 support yet? I read Spring 5.3+ should be fine with JDK 17.
  • Is there any solution other than waiting for Spring 6? (will that even solve it?)
nigelg
  • 151
  • 3
  • 12

1 Answers1

0

Given that the problem seems to be Spring invoking methods on public interfaces or abstract classes which have com.sun internal implementations, I came up with this workaround. But don't really regard this as an acceptable solution, it shouldn't be necessary to have to create a "setter class" like this, I would much rather see a way to have Spring handle this natively.

SchemaFactoryFactory.java

import javax.xml.validation.SchemaFactory;

import org.w3c.dom.ls.LSResourceResolver;

public class SchemaFactoryFactory
{
    public SchemaFactory createSchemaFactory (final LSResourceResolver resourceResolver)
    {
        final SchemaFactory factory = SchemaFactory.newInstance ("http://www.w3.org/2001/XMLSchema");
        factory.setResourceResolver (resourceResolver);
        
        return factory;
    }
}

Updated spring-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" >

    <bean id="resolver" class="Resolver" />
    
    <bean id="schemaFactoryFactory" class="SchemaFactoryFactory" />
    
    <bean id="schemaFactory" class="javax.xml.validation.SchemaFactory" factory-bean="schemaFactoryFactory" factory-method="createSchemaFactory">
        <constructor-arg ref="resolver" />
    </bean>
    
</beans>
nigelg
  • 151
  • 3
  • 12