1

I'm trying to implement an xsd validator for schemas, that using imports and includes. I took this answer for example.

Here is my validating method:

public void validate(String filePath, String schemaName) throws Exception
{
    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    factory.setResourceResolver(new ResourceResolver());
    Source schemaFile = new StreamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
    Schema schema = factory.newSchema(schemaFile);
    Validator validator = schema.newValidator();
    validator.setErrorHandler(new MySAXParseErrorHandler());
    validator.validate(new StreamSource(filePath));
}

LSInput (entirely the same):

public class LSInputImpl implements LSInput
{

private String publicId;

private String systemId;

public String getPublicId()
{
    return publicId;
}

public void setPublicId(String publicId)
{
    this.publicId = publicId;
}

public String getBaseURI()
{
    return null;
}

public InputStream getByteStream()
{
    return null;
}

public boolean getCertifiedText()
{
    return false;
}

public Reader getCharacterStream()
{
    return null;
}

public String getEncoding()
{
    return null;
}

public String getStringData()
{
    synchronized (inputStream) {
        try {
            byte[] input = new byte[inputStream.available()];
            inputStream.read(input);
            String contents = new String(input);
            return contents;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Exception " + e);
        }
        return null;
    }
}

public void setBaseURI(String baseURI)
{
}

public void setByteStream(InputStream byteStream)
{
}

public void setCertifiedText(boolean certifiedText)
{
}

public void setCharacterStream(Reader characterStream)
{
}

public void setEncoding(String encoding)
{
}

public void setStringData(String stringData)
{
}

public String getSystemId()
{
    return systemId;
}

public void setSystemId(String systemId)
{
    this.systemId = systemId;
}

public BufferedInputStream getInputStream()
{
    return inputStream;
}

public void setInputStream(BufferedInputStream inputStream)
{
    this.inputStream = inputStream;
}

private BufferedInputStream inputStream;

public LSInputImpl(String publicId, String sysId, InputStream input)
{
    this.publicId = publicId;
    this.systemId = sysId;
    this.inputStream = new BufferedInputStream(input);
}

}

ResourceResolver:

public class ResourceResolver  implements LSResourceResolver
{

   public LSInput resolveResource(String type, String namespaceURI,
                           String publicId, String systemId, String baseURI)       {


    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(systemId);       
    return new LSInputImpl(publicId, systemId, resourceAsStream);
}

}

When I'm trying to validate xml with xsd that has only imports, it works fine. But when I split one schema with two by using include, process fails with IOException:

java.io.IOException: Stream closed
at java.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:159)
at java.io.BufferedInputStream.available(BufferedInputStream.java:410)
at LSInputImpl.getStringData(LSInputImpl.java:57)
at com.sun.org.apache.xerces.internal.util.DOMEntityResolverWrapper.resolveEntity(DOMEntityResolverWrapper.java:130)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.resolveEntity(XMLEntityManager.java:1073)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.resolveDocument(XMLSchemaLoader.java:659)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.resolveSchemaSource(XSDHandler.java:2105)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1088)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1120)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:620)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

org.xml.sax.SAXParseException; systemId: file:///C:/Users/adobryn/Java/sub/schema.xsd; lineNumber: 456; columnNumber: 73; src-resolve: Cannot resolve the name 'st_Term' to a(n) 'type definition' component.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4162)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4145)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1741)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseNamedElement(XSDElementTraverser.java:405)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseLocal(XSDElementTraverser.java:194)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseLocalElements(XSDHandler.java:3618)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:633)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Schema with include:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/schema" targetNamespace="http://www.w3.org/schema" elementFormDefault="qualified">    
    <xsd:include schemaLocation="st_Term.xsd"/>
    <xsd:element name="Term" type="st_Term" minOccurs="0" maxOccurs="1"/>
</xsd:schema>

I think, there is something wrong with getStringData() method. How can I modify it? Thanks for your help!

Community
  • 1
  • 1
a_dobryn
  • 79
  • 1
  • 8
  • Your use of `InputStream.available()` is specifically warned against in the Javadoc, and you're ignoring the result of `InputStream.read()`, which could be as little as 1, or indeed -1. – user207421 Apr 29 '16 at 00:38

2 Answers2

1

The problem was, that included xsd also has an include, and ResourceResolver looked in resources folder for it, not resources/sub, where that subincluded xsd was. So, I took another example, that has path tracking, and it works now :) Please note, that schemaBasePath should start with "/"

Community
  • 1
  • 1
a_dobryn
  • 79
  • 1
  • 8
-1

I had the same problem with includes in .xsd several years ago.

As I can remember my problem was that .xsd resource and its includes were located in some package (directory) within resources.

I've solved it with the following ResourceResolver where the package is passed to constructor (resourceRoot argument):

class ResourceResolver implements LSResourceResolver {

    private String resourceRoot;

    public ResourceResolver(String resourceRoot) {
        this.resourceRoot = resourceRoot;
    }

    public LSInput resolveResource(String type, String namespaceURI,String publicId, String systemId, String baseURI) {
        InputStream resourceAsStream = this.getClass().getResourceAsStream(resourceRoot + "/" + systemId);
        return new Input(publicId, systemId, resourceAsStream);
    }

}

and Input:

static class Input implements LSInput {

    private String publicId;

    private String systemId;

    public String getPublicId() {
        return publicId;
    }

    public void setPublicId(String publicId) {
        this.publicId = publicId;
    }

    public String getBaseURI() {
        return null;
    }

    public InputStream getByteStream() {
        return null;
    }

    public boolean getCertifiedText() {
        return false;
    }

    public Reader getCharacterStream() {
        return null;
    }

    public String getEncoding() {
        return null;
    }

    public String getStringData() {
        synchronized (inputStream) {
            try {
                byte[] input = new byte[inputStream.available()];
                inputStream.read(input);
                String contents = new String(input, "UTF-8");
                return contents;
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("Exception " + e);
                return null;
            }
        }
    }

    public void setBaseURI(String baseURI) {
    }

    public void setByteStream(InputStream byteStream) {
    }

    public void setCertifiedText(boolean certifiedText) {
    }

    public void setCharacterStream(Reader characterStream) {
    }

    public void setEncoding(String encoding) {
    }

    public void setStringData(String stringData) {
    }

    public String getSystemId() {
        return systemId;
    }

    public void setSystemId(String systemId) {
        this.systemId = systemId;
    }

    private final BufferedInputStream inputStream;

    public Input(String publicId, String sysId, InputStream input) {
        this.publicId = publicId;
        this.systemId = sysId;
        this.inputStream = new BufferedInputStream(input);
    }
}
Argb32
  • 1,365
  • 8
  • 10
  • Your use of `InputStream.available()` is specifically warned against in the Javadoc, and you're ignoring the result of `InputStream.read()`, which could be as little as 1, or indeed -1. – user207421 Apr 29 '16 at 00:38