/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.cxf.common.xmlschema;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamWriter;
import javax.xml.transform.dom.DOMSource;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMError;
import org.w3c.dom.DOMErrorHandler;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.LSInput;
import org.w3c.dom.ls.LSResourceResolver;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.staxutils.PrettyPrintXMLStreamWriter;
import org.apache.cxf.staxutils.StaxUtils;
import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaComplexContent;
import org.apache.ws.commons.schema.XmlSchemaComplexContentExtension;
import org.apache.ws.commons.schema.XmlSchemaComplexContentRestriction;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSerializer;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.utils.NamespaceMap;
import org.junit.Assert;
import org.junit.Test;
/**
*
*/
public class ImportRepairTest extends Assert {
static boolean dumpSchemas;
private static final Logger LOG = LogUtils.getL7dLogger(ImportRepairTest.class);
private static final String IMPORTING_SCHEMA = "urn:importing";
private static final String BASE_TYPE_SCHEMA1 = "urn:baseType1";
private static final String BASE_TYPE_SCHEMA2 = "urn:baseType2";
private static final String ELEMENT_TYPE_SCHEMA = "urn:elementType";
private static final String ELEMENT_SCHEMA = "urn:element";
private static final String ATTRIBUTE_SCHEMA = "urn:attribute";
private static final String ATTRIBUTE_TYPE_SCHEMA = "urn:attributeType";
private SchemaCollection collection;
@Test
public void testImportRepairs() throws Exception {
if (System.getProperty("java.vendor").contains("IBM")) {
//the version of xerces built into IBM jdk won't work
//and we cannot get a good version unless we endorse it
return;
}
collection = new SchemaCollection();
XmlSchema importingSchema = newSchema(IMPORTING_SCHEMA);
XmlSchema baseTypeSchema1 = newSchema(BASE_TYPE_SCHEMA1);
XmlSchema baseTypeSchema2 = newSchema(BASE_TYPE_SCHEMA2);
XmlSchema elementTypeSchema = newSchema(ELEMENT_TYPE_SCHEMA);
XmlSchema elementSchema = newSchema(ELEMENT_SCHEMA);
XmlSchema attributeSchema = newSchema(ATTRIBUTE_SCHEMA);
XmlSchema attributeTypeSchema = newSchema(ATTRIBUTE_TYPE_SCHEMA);
createBaseType1(baseTypeSchema1);
createBaseType2(baseTypeSchema2);
XmlSchemaComplexContentExtension derivedType1Extension = createDerivedType1(importingSchema);
createDerivedType2(importingSchema);
createImportedElement(elementSchema);
createTypeImportingElement(importingSchema);
createTypeImportedByElement(elementTypeSchema);
createElementWithImportedType(importingSchema);
createImportedAttribute(attributeSchema);
XmlSchemaAttribute importingAttribute = new XmlSchemaAttribute(importingSchema, false);
importingAttribute.getRef().setTargetQName(new QName(ATTRIBUTE_SCHEMA, "imported"));
// borrow derivedType1 to make the reference.
derivedType1Extension.getAttributes().add(importingAttribute);
createImportedAttributeType(attributeTypeSchema);
createAttributeImportingType(importingSchema);
/*
* Notice that no imports have been added. In an ideal world, XmlSchema would do this for us.
*/
try {
tryToParseSchemas();
fail("Expected an exception");
} catch (DOMErrorException e) {
//ignore, expected
}
LOG.info("adding imports");
collection.addCrossImports();
tryToParseSchemas();
}
Method findMethod(Object o, String name) {
for (Method m: o.getClass().getMethods()) {
if (m.getName() == name) {
return m;
}
}
return null;
}
private void tryToParseSchemas() throws Exception {
// Get DOM Implementation using DOM Registry
final List<DOMLSInput> inputs = new ArrayList<>();
final Map<String, LSInput> resolverMap = new HashMap<>();
for (XmlSchema schema : collection.getXmlSchemas()) {
if (XMLConstants.W3C_XML_SCHEMA_NS_URI.equals(schema.getTargetNamespace())) {
continue;
}
Document document = new XmlSchemaSerializer().serializeSchema(schema, false)[0];
DOMLSInput input = new DOMLSInput(document, schema.getTargetNamespace());
dumpSchema(document);
resolverMap.put(schema.getTargetNamespace(), input);
inputs.add(input);
}
DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();
DOMImplementation impl = registry.getDOMImplementation("XS-Loader");
try {
Object schemaLoader = findMethod(impl, "createXSLoader").invoke(impl, new Object[1]);
DOMConfiguration config = (DOMConfiguration)findMethod(schemaLoader, "getConfig").invoke(schemaLoader);
config.setParameter("validate", Boolean.TRUE);
try {
//bug in the JDK doesn't set this, but accesses it
config.setParameter("http://www.oracle.com/xml/jaxp/properties/xmlSecurityPropertyManager",
Class.forName("com.sun.org.apache.xerces.internal.utils.XMLSecurityPropertyManager")
.newInstance());
config.setParameter("http://apache.org/xml/properties/security-manager",
Class.forName("com.sun.org.apache.xerces.internal.utils.XMLSecurityManager")
.newInstance());
} catch (Throwable t) {
//ignore
}
config.setParameter("error-handler", new DOMErrorHandler() {
public boolean handleError(DOMError error) {
LOG.info("Schema parsing error: " + error.getMessage()
+ " " + error.getType()
+ " " + error.getLocation().getUri()
+ " " + error.getLocation().getLineNumber()
+ ":" + error.getLocation().getColumnNumber());
throw new DOMErrorException(error);
}
});
config.setParameter("resource-resolver", new LSResourceResolver() {
public LSInput resolveResource(String type, String namespaceURI, String publicId,
String systemId, String baseURI) {
return resolverMap.get(namespaceURI);
}
});
Method m = findMethod(schemaLoader, "loadInputList");
String name = m.getParameterTypes()[0].getName() + "Impl";
name = name.replace("xs.LS", "impl.xs.util.LS");
Class<?> c = Class.forName(name);
Object inputList = c.getConstructor(LSInput[].class, Integer.TYPE)
.newInstance(inputs.toArray(new LSInput[inputs.size()]), inputs.size());
findMethod(schemaLoader, "loadInputList").invoke(schemaLoader, inputList);
} catch (InvocationTargetException ite) {
throw (Exception)ite.getTargetException();
}
}
private void dumpSchema(Document document) throws Exception {
if (!dumpSchemas) {
return;
}
XMLStreamWriter xwriter = StaxUtils.createXMLStreamWriter(System.err);
xwriter = new PrettyPrintXMLStreamWriter(xwriter, 2);
StaxUtils.copy(new DOMSource(document), xwriter);
xwriter.close();
}
private void createTypeImportedByElement(XmlSchema elementTypeSchema) {
XmlSchemaComplexType elementImportedType = new XmlSchemaComplexType(elementTypeSchema, true);
elementImportedType.setName("importedElementType");
elementImportedType.setParticle(new XmlSchemaSequence());
}
private XmlSchema newSchema(String uri) {
XmlSchema schema = collection.newXmlSchemaInCollection(uri);
NamespaceMap map = new NamespaceMap();
map.add("", XMLConstants.W3C_XML_SCHEMA_NS_URI);
schema.setNamespaceContext(map);
return schema;
}
private void createAttributeImportingType(XmlSchema importingSchema) {
XmlSchemaAttribute attributeImportingType = new XmlSchemaAttribute(importingSchema, true);
attributeImportingType.setName("importingType");
attributeImportingType.setSchemaTypeName(new QName(ATTRIBUTE_TYPE_SCHEMA, "importedAttributeType"));
}
private void createImportedAttributeType(XmlSchema attributeTypeSchema) {
XmlSchemaSimpleType attributeImportedType = new XmlSchemaSimpleType(attributeTypeSchema, true);
attributeImportedType.setName("importedAttributeType");
XmlSchemaSimpleTypeRestriction simpleContent = new XmlSchemaSimpleTypeRestriction();
attributeImportedType.setContent(simpleContent);
simpleContent.setBaseTypeName(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"));
}
private void createImportedAttribute(XmlSchema attributeSchema) {
XmlSchemaAttribute importedAttribute = new XmlSchemaAttribute(attributeSchema, true);
importedAttribute.setName("imported");
importedAttribute.setSchemaTypeName(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"));
}
private void createElementWithImportedType(XmlSchema importingSchema) {
XmlSchemaElement elementWithImportedType = new XmlSchemaElement(importingSchema, true);
elementWithImportedType.setName("elementWithImportedType");
elementWithImportedType.setSchemaTypeName(new QName(ELEMENT_TYPE_SCHEMA, "importedElementType"));
}
private void createTypeImportingElement(XmlSchema importingSchema) {
XmlSchemaComplexType typeWithElementRef = new XmlSchemaComplexType(importingSchema, true);
typeWithElementRef.setName("typeWithRef");
XmlSchemaSequence sequence = new XmlSchemaSequence();
typeWithElementRef.setParticle(sequence);
XmlSchemaElement refElement = new XmlSchemaElement(importingSchema, false);
refElement.getRef().setTargetQName(new QName(ELEMENT_SCHEMA, "importedElement"));
}
private void createImportedElement(XmlSchema elementSchema) {
XmlSchemaElement importedElement = new XmlSchemaElement(elementSchema, true);
importedElement.setName("importedElement");
importedElement.setSchemaTypeName(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"));
}
private void createDerivedType2(XmlSchema importingSchema) {
XmlSchemaComplexContent complexContent;
XmlSchemaComplexType derivedType2 = new XmlSchemaComplexType(importingSchema, true);
derivedType2.setName("derivedRestriction");
XmlSchemaComplexContentRestriction restriction = new XmlSchemaComplexContentRestriction();
restriction.setBaseTypeName(new QName(BASE_TYPE_SCHEMA2, "baseType2"));
complexContent = new XmlSchemaComplexContent();
complexContent.setContent(restriction);
derivedType2.setContentModel(complexContent);
}
private XmlSchemaComplexContentExtension createDerivedType1(XmlSchema importingSchema) {
XmlSchemaComplexType derivedType1 = new XmlSchemaComplexType(importingSchema, true);
derivedType1.setName("derivedExtension");
XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension();
extension.setBaseTypeName(new QName(BASE_TYPE_SCHEMA1, "baseType1"));
XmlSchemaComplexContent complexContent = new XmlSchemaComplexContent();
complexContent.setContent(extension);
derivedType1.setContentModel(complexContent);
return extension;
}
private XmlSchemaComplexType createBaseType2(XmlSchema baseTypeSchema2) {
XmlSchemaComplexType baseType2 = new XmlSchemaComplexType(baseTypeSchema2, true);
baseType2.setName("baseType2");
baseType2.setParticle(new XmlSchemaSequence());
return baseType2;
}
private void createBaseType1(XmlSchema baseTypeSchema1) {
XmlSchemaComplexType baseType1 = new XmlSchemaComplexType(baseTypeSchema1, true);
baseType1.setName("baseType1");
baseType1.setParticle(new XmlSchemaSequence());
}
}