/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-2015, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.xml; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.util.Collections; import java.util.List; import java.util.Map; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.ecore.resource.impl.ExtensibleURIConverterImpl; import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.util.XSDSchemaLocationResolver; import org.geotools.xml.impl.HTTPURIHandler; import org.geotools.xs.XS; import junit.framework.TestCase; /** * Tests for {@link Schemas}. * * @source $URL$ */ public class SchemasTest extends TestCase { File tmp,sub; private ExecutorService executorService = Executors.newSingleThreadExecutor(); protected void setUp() throws Exception { super.setUp(); tmp = File.createTempFile("schemas", "xsd"); tmp.delete(); tmp.mkdir(); tmp.deleteOnExit(); sub = new File( tmp, "sub" ); sub.mkdir(); sub.deleteOnExit(); File f = new File( tmp, "root.xsd" ); String xsd = "<xsd:schema xmlns='http://geotools.org/test' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "targetNamespace='http://geotools.org/test'> " + "<xsd:import namespace='http://geotools/org/import1' " + "schemaLocation='import1.xsd'/>" + "<xsd:import namespace='http://geotools/org/import2' " + "schemaLocation='import2.xsd'/>" + "<xsd:include location='include1.xsd'/>" + "<xsd:include location='include2.xsd'/>" + "</xsd:schema>"; write( f, xsd ); f = new File( tmp, "import1.xsd" ); xsd = "<xsd:schema xmlns='http://geotools.org/import1' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "targetNamespace='http://geotools.org/import1'> " + "</xsd:schema>"; write( f , xsd ); f = new File( sub, "import2.xsd" ); xsd = "<xsd:schema xmlns='http://geotools.org/import2' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "targetNamespace='http://geotools.org/import2'> " + "</xsd:schema>"; write( f , xsd ); f = new File( tmp, "include1.xsd" ); xsd = "<xsd:schema xmlns='http://geotools.org/test' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "targetNamespace='http://geotools.org/test'> " + "</xsd:schema>"; write( f, xsd ); f = new File( sub, "include2.xsd" ); xsd = "<xsd:schema xmlns='http://geotools.org/test' " + "xmlns:xsd='http://www.w3.org/2001/XMLSchema' " + "targetNamespace='http://geotools.org/test'> " + "</xsd:schema>"; write( f, xsd ); System.setProperty(Schemas.FORCE_SCHEMA_IMPORT, "false"); } void write( File f, String xsd ) throws IOException { f.deleteOnExit(); f.createNewFile(); FileWriter w = new FileWriter( f ); w.write( xsd ); w.flush(); w.close(); } protected void tearDown() throws Exception { super.tearDown(); new File( tmp, "root.xsd" ).delete(); new File( tmp, "import1.xsd" ).delete(); new File( sub, "import2.xsd" ).delete(); new File( tmp, "include1.xsd" ).delete(); new File( sub, "include2.xsd" ).delete(); sub.delete(); tmp.delete(); System.setProperty(Schemas.FORCE_SCHEMA_IMPORT, "false"); executorService.shutdown(); } public void testValidateImportsIncludes() throws Exception { String location = new File( tmp, "root.xsd").getAbsolutePath(); List errors = Schemas.validateImportsIncludes( location ); assertEquals( 2, errors.size() ); SchemaLocationResolver resolver1 = new SchemaLocationResolver(XS.getInstance()) { public boolean canHandle(XSDSchema schema, String uri, String location) { if ( location.endsWith("import2.xsd") ) { return true; } return false; } public String resolveSchemaLocation(XSDSchema schema, String uri, String location) { return new File( sub, "import2.xsd" ).getAbsolutePath(); } }; SchemaLocationResolver resolver2 = new SchemaLocationResolver(XS.getInstance()) { public boolean canHandle(XSDSchema schema, String uri, String location) { if ( location.endsWith("include2.xsd") ) { return true; } return false; } public String resolveSchemaLocation(XSDSchema schema, String uri, String location) { return new File( sub, "include2.xsd" ).getAbsolutePath(); } }; errors = Schemas.validateImportsIncludes( location, null, new XSDSchemaLocationResolver[]{resolver1,resolver2} ); assertEquals( 0, errors.size() ); } /** * Tests that element declarations and type definitions from imported schemas are parsed, * even if the importing schema itself contains no element nor type. * * @throws IOException */ public void testImportsOnly() throws IOException { XSDSchema schema = Schemas.parse(Schemas.class.getResource("importFacetsEmpty.xsd").toString()); assertNotNull(schema); boolean elFound = hasElement(schema, "collapsedString"); assertTrue(elFound); } /** * Tests that system property "org.geotools.xml.forceSchemaImport" is properly taken into account. * @throws IOException */ public void testForcedSchemaImport() throws IOException { XSDSchema schema = Schemas.parse(Schemas.class.getResource("importFacetsNotEmpty.xsd").toString()); assertNotNull(schema); // importing schema is not empty and system property "org.geotools.xml.forceSchemaImport" is false: // elements defined in imported schema should not be found boolean elFound = hasElement(schema, "collapsedString"); assertFalse(elFound); // force import of external schemas in any case System.setProperty(Schemas.FORCE_SCHEMA_IMPORT, "true"); schema = Schemas.parse(Schemas.class.getResource("importFacetsNotEmpty.xsd").toString()); assertNotNull(schema); elFound = hasElement(schema, "collapsedString"); assertTrue(elFound); } private boolean hasElement(XSDSchema schema, String elQName) { boolean elFound = false; EList<XSDElementDeclaration> elDeclList = schema.getElementDeclarations(); for (XSDElementDeclaration elDecl: elDeclList) { if (elQName.equals(elDecl.getQName())) { elFound = true; } } return elFound; } /** * {@link HTTPURIHandler} implementation mocks a remote GeoServer which synchronizes on {@link Schemas} * class, as GeoServer does. The mock uses a timeout, to avoid the test to hang for ever in case * of future implementation changes. */ private final class MockServerBehaviour extends HTTPURIHandler { @Override public InputStream createInputStream(URI uri, Map<?, ?> options) throws IOException { try { return executorService.invokeAny(Collections.singletonList(new Callable<InputStream>() { @Override public InputStream call() throws Exception { synchronized (Schemas.class) { return Schemas.class.getResourceAsStream("remoteSchemaLocation.xsd"); } } }),3, TimeUnit.SECONDS); } catch (InterruptedException e) { return null; } catch (ExecutionException e) { return null; } catch (TimeoutException e) { throw new RuntimeException("Timed out.", e); } } } /** * Test ensures no deadlock occurs when a remote schema is loaded, which resolves to the same JVM. * Deadlock used to occur in GeoServer, when schema was loaded from same GeoServer instance, because * schema consumer and schema server both synchronized on same {@link Schemas} class instance. * * @throws IOException */ public void testParseRemoteDoesNotBlock() throws IOException { URIConverter converter = new ExtensibleURIConverterImpl( Collections.singletonList(new MockServerBehaviour()), Collections.emptyList()); ResourceSet resourceSet = new ResourceSetImpl(); resourceSet.setURIConverter(converter); XSDSchema schema = Schemas.parse("http://www.foo.bar/remoteSchemaLocation.xsd", resourceSet); assertNotNull(schema); } }