From: <svn...@os...> - 2011-07-29 09:02:30
|
Author: aaime Date: 2011-07-29 02:02:24 -0700 (Fri, 29 Jul 2011) New Revision: 37746 Added: branches/2.7.x/modules/extension/xsd/xsd-gml3/src/test/java/org/geotools/gml3/SchemasTest.java Modified: branches/2.7.x/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/Schemas.java Log: [GEOT-3762] Concurrent calls to Schemas.parse/Schemas.dispose easily result in concurrency exceptions Modified: branches/2.7.x/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/Schemas.java =================================================================== --- branches/2.7.x/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/Schemas.java 2011-07-29 08:59:39 UTC (rev 37745) +++ branches/2.7.x/modules/extension/xsd/xsd-core/src/main/java/org/geotools/xml/Schemas.java 2011-07-29 09:02:24 UTC (rev 37746) @@ -20,7 +20,6 @@ import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; -import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -61,8 +60,10 @@ import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDSchemaContent; +import org.eclipse.xsd.XSDSchemaDirective; import org.eclipse.xsd.XSDSimpleTypeDefinition; import org.eclipse.xsd.XSDTypeDefinition; +import org.eclipse.xsd.XSDWildcard; import org.eclipse.xsd.util.XSDConstants; import org.eclipse.xsd.util.XSDResourceFactoryImpl; import org.eclipse.xsd.util.XSDResourceImpl; @@ -81,7 +82,6 @@ import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.helpers.DefaultHandler; -import org.eclipse.xsd.XSDWildcard; /** * Utility class for performing various operations. @@ -254,9 +254,13 @@ XSDResourceImpl xsdMainResource = (XSDResourceImpl) resourceSet.createResource(URI.createURI( ".xsd")); xsdMainResource.setURI(uri); - xsdMainResource.load(resourceSet.getLoadOptions()); - - return xsdMainResource.getSchema(); + + // schema building has effects on referenced schemas, it will alter them -> we need + // to synchronize this call so that only one of these operations is active at any time + synchronized(Schemas.class) { + xsdMainResource.load(resourceSet.getLoadOptions()); + return xsdMainResource.getSchema(); + } } /** @@ -297,6 +301,41 @@ return imprt; } + /** + * Remove all references to a schema + * It is important to call this method for every dynamic schema created that is not needed + * anymore, because references in the static schema's will otherwise keep it alive forever + * + * @param schema to be flushed + * @since 2.7.3 + */ + public static final void dispose(XSDSchema schema) { + for (XSDSchemaContent content : schema.getContents()) { + if (content instanceof XSDSchemaDirective) { + XSDSchemaDirective directive = (XSDSchemaDirective) content; + XSDSchema resolvedSchema = directive.getResolvedSchema(); + + if (resolvedSchema != null) { + synchronized (Schemas.class) { + resolvedSchema.getReferencingDirectives().remove(directive); + for (XSDElementDeclaration dec : resolvedSchema.getElementDeclarations()) { + if(dec == null) { + continue; + } + List<XSDElementDeclaration> toRemove = new ArrayList<XSDElementDeclaration>(); + for (XSDElementDeclaration subs : dec.getSubstitutionGroup()) { + if (subs != null && subs.getContainer() != null && subs.getContainer().equals(schema)) { + toRemove.add(subs); + } + } + dec.getSubstitutionGroup().removeAll(toRemove); + } + } + } + } + } + } + public static final List validateImportsIncludes(String location) throws IOException { return validateImportsIncludes(location,Collections.EMPTY_LIST,Collections.EMPTY_LIST); } Added: branches/2.7.x/modules/extension/xsd/xsd-gml3/src/test/java/org/geotools/gml3/SchemasTest.java =================================================================== --- branches/2.7.x/modules/extension/xsd/xsd-gml3/src/test/java/org/geotools/gml3/SchemasTest.java (rev 0) +++ branches/2.7.x/modules/extension/xsd/xsd-gml3/src/test/java/org/geotools/gml3/SchemasTest.java 2011-07-29 09:02:24 UTC (rev 37746) @@ -0,0 +1,45 @@ +package org.geotools.gml3; + +import java.io.File; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.eclipse.xsd.XSDSchema; +import org.geotools.xml.Schemas; +import org.junit.Test; + +public class SchemasTest { + + @Test + public void testConcurrentParse() throws Exception { + URL location = SchemasTest.class.getResource("states.xsd"); + final File schemaFile = new File(location.toURI()); + final List locators = Arrays.asList( GML.getInstance().createSchemaLocator() ); + + ExecutorService es = Executors.newFixedThreadPool(32); + List<Future<Void>> results = new ArrayList<Future<Void>>(); + for(int i = 0; i < 128; i++) { + Future<Void> future = es.submit(new Callable<Void>() { + + public Void call() throws Exception { + XSDSchema schema = Schemas.parse( schemaFile.getAbsolutePath(), locators, null ); + Schemas.dispose(schema); + return null; + } + + }); + results.add(future); + } + + // make sure none threw an exception + for (Future<Void> future : results) { + future.get(); + } + } +} |