Recently, I tried to help a teammate design a WSDL file. I gently drove him toward separating the interface itself in the WSDL file and domain objects in a XML Schema file. One thing leading to another, I also made him split this XSD into two separate files, one including another for design purposes. Alas, tests were already present, and they failed miserably after my refactoring, complaining about a type in the included file not being found. The situation was extremely unpleasant, not only because I looked a little foolish in front of one of my co-worker, but also because despite my best efforts, I couldn’t achieve validation.
I finally found the solution, and I hope to spread it as much as I can in order for other developers to stop loosing time regarding this issue. The root of the problem is that the Java XML validation API cannot resolved included XML schemas (and imported ones as well), period. However, it allows for registering a (crude) resolver that can provide the content of the included/imported XSD. So, the solution is to implement your own resolver and your own content holder (there’s none provided in the JDK 6).
- Create a "input" implementation.
This class is responsible for holding the content of the resolved schema.
public class LSInputImpl implements LSInput { private Reader characterStream; private InputStream byteStream; private String stringData; private String systemId; private String publicId; private String baseURI; private String encoding; private boolean certifiedText; // Getters and setters here }
- Create a resolver implementation.
This one is based on the premise that the included/imported schemas lies at the root of the classpath, and is relatively simple.
More complex implementations can provide for a variety of locations (filesystem, internet, etc.).
public class ClasspathResourceResolver implements LSResourceResolver { @Override public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) { LSInputImpl input = new LSInputImpl(); InputStream stream = getClass().getClassLoader().getResourceAsStream(systemId); input.setPublicId(publicId); input.setSystemId(systemId); input.setBaseURI(baseURI); input.setCharacterStream(new InputStreamReader(stream)); return input; } }
- Finally, just set the resolver on the schema factory:
SchemaFactory schemaFactory = SchemaFactory.newInstance(W3C_XML_SCHEMA_NS_URI); schemaFactory.setResourceResolver(new ClasspathResourceResolver());
These 3 steps will go a long way toward cleanly splitting your XML schemas.