3

There are many postings with the same title as this. I have looked at many of them, but their situations seem to be different from mine.

I have a Java application that is configured at run-time by an XML file. There is a corresponding XML schema that defines the structure of the XML. The schema imports other schema to use type definitions they define. The application reads in the schema and configuration file and loads the XML as Java objects.

The application is built using Apache Maven and the maven-jaxb2-plugin is used to translate the schema definitions into Java classes so the application can ingest the XML configuration information. Everything builds successfully and from what I can tell, the XJC generated classes are correct and in the correct locations.

I'm running into a problem when I attempt execute the application, which first reads in the XML and load it as Java objects. An example exception follows.

[2018-08-09 10:31:05.056] ERROR: common.ConfigLoader:121 - Exception: 
org.xml.sax.SAXParseException; lineNumber: 291; columnNumber: 80; src-resolve: Cannot resolve the name 'sc:ExpectedDataFormatEnum' to a(n) 'type definition' component.
    at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:198)
    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:1678)
    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:617)
    at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:575)
    at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:541)
    at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:252)
    at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:627)
    at {removed}.ConfigLoader.load(ConfigLoader.java:91)
    at {removed}.Launcher.main(Launcher.java:121)
[2018-08-09 10:31:05.058] ERROR: wf.Launcher:122 - Unable to load configuration file

I suspect the problem is related to how the imported schemae are being referenced/resolved at run-time. I use an XML catalog (a new concept for me) to resolve schema locations during the build. Do I need something similar to resolve the location at run time?

For context, here are snippets of relevant files.

Schema that is imported by the application schema (security-common.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="security_common"
  xmlns:sc="security_common" elementFormDefault="qualified" attributeFormDefault="unqualified">

  <xs:simpleType name="TypeList">
    <xs:list itemType="xs:string" />
  </xs:simpleType>

  <xs:simpleType name="ExpectedDataFormatEnum">
    <xs:restriction base="xs:string">
      <xs:enumeration value="STRING" />
      <xs:enumeration value="BYTE_ARRAY" />
    </xs:restriction>
  </xs:simpleType>

</xs:schema>

Application schema that uses the schema above (config-schema.xsd):

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" targetNamespace="injector"
  xmlns:inj="injector" xmlns:sc="security_common" xmlns:camel="camel_config"
  elementFormDefault="qualified" attributeFormDefault="unqualified">

  <xs:import namespace="security_common" />
  <xs:import namespace="camel_config" />

  ...
  <xs:complexType name="WireCaptureConfig">
    <xs:sequence>
      <xs:element name="expected-data-format" type="sc:ExpectedDataFormatEnum" />
      ...
    </xs:sequence>
  </xs:complexType>
  ...
</xs:schema>

The XML catalog file:

PUBLIC "security_common" "maven:{removed}:security-common:jar::!/config/security-common.xsd"
PUBLIC "camel_config" "maven:{removed}:security-common:jar::!/config/camel-config.xsd"

My understanding is that the catalog file maps the schema namespaces to maven artifacts, which represent the schemae to be imported/referenced. It seems like a little black magic, but it does work.

The exception thrown would seem to indicate that the application cannot "see" the imported schema definitions. Based upon what has been described, is there a solution to this problem? If other information needs to be provided, please let me know and I'll see what I can do.

Joseph Gagnon
  • 1,731
  • 3
  • 30
  • 63

1 Answers1

2

You're right to suspect that you have to make accomodations for your XML catalog in your runtime Java code too.

See Using an XML Catalog with a Java library that uses JAXP internally for details on how to use a CatalogResolver (or org.apache.xml.resolver.tools.CatalogResolver).

Note also that it can be useful to establish that all else is in order with your XSDs by placing them in URI-accessible locations1 and adding @schemaLocation attributes to your xs:imports. Many XML Catalog implementation's diagnostic messages are less than ideal for tracking down problems.

1See How to reference a local XML Schema file correctly?

kjhughes
  • 106,133
  • 27
  • 181
  • 240
  • Unfortunately, I'm required to use Java 7, which takes CatalogResolver out of the equation. – Joseph Gagnon Aug 09 '18 at 17:13
  • Please provide examples of what you mean by URI-accessible locations. I'm always seeing http used, and I has assumed that meant the schema was somehow "served" over HTTP. That is not an option for me. Are there other ways to do it? – Joseph Gagnon Aug 09 '18 at 17:18
  • There are CatalogResolvers available for Java 7, and you can use your local file system for the imported XSDs as a test ahead of implementing XML catalogs. See updates to answer to address your follow-up questions. – kjhughes Aug 09 '18 at 17:31
  • OK, I'm so confused and overwhelmed. I've checked out the links you provided and some that those provided, plus doing some google searches of my own. There seem to be a number (Apache seems to have more than one) of APIs for this - I just don't know which to use or how to use it. Many of the articles and examples I've found seem to assume a certain level of knowledge WRT XML schema and URI resolution, but I'm apparently not at that level. Is there some sort of a primer for this concept? – Joseph Gagnon Aug 09 '18 at 18:11
  • I have selected Apache Commons Configuration as the tool I will attempt to learn and use to help me resolve XML schema at run-time. An initial impression I'm getting from what I've read so far makes me think that I need to use the commons configuration API more or less as a whole. The examples I've seen indicate creating a catalog resolver and passing it to a builder or configuration object. Do I need to do this, or can the catalog resolver be used independently? – Joseph Gagnon Aug 13 '18 at 12:22
  • You can use it independently. I've updated the link in the answer to point to the [Apache Commons resolver API page](https://xerces.apache.org/xml-commons/components/apidocs/resolver/org/apache/xml/resolver/tools/CatalogResolver.html). Also, see their documentation at [Apache Commons Resolver](https://xerces.apache.org/xml-commons/components/resolver/index.html). – kjhughes Aug 13 '18 at 12:32
  • I have seen and read through this documentation, but am still not sure how to use it, especially where it involves pointing to a local resource. Can I use a relative path in the catalog? I assume that the imported schema file must be accessible in the filesystem and that its path (either absolute or relative) is what needs to be specified in the catalog. I would assume that the schema can't be accessed from within the jar file it's packaged up in, correct? – Joseph Gagnon Aug 13 '18 at 12:42
  • I believe we've reached the point here where you should [**accept**](http://meta.stackoverflow.com/q/5234/234215) this answer if it's helped and ask new questions as needed for your further follow-ups. Thank you. – kjhughes Aug 13 '18 at 13:13