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?)
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
Updated spring-beans.xml