You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by re...@apache.org on 2021/11/05 13:07:57 UTC
[uima-uimaj] branch
bugfix/UIMA-6393-Circular-imports-break-resource-manager-cache updated:
[UIMA-6393]: Circular imports break resource manager cache
This is an automated email from the ASF dual-hosted git repository.
rec pushed a commit to branch bugfix/UIMA-6393-Circular-imports-break-resource-manager-cache
in repository https://gitbox.apache.org/repos/asf/uima-uimaj.git
The following commit(s) were added to refs/heads/bugfix/UIMA-6393-Circular-imports-break-resource-manager-cache by this push:
new d9e7c20 [UIMA-6393]: Circular imports break resource manager cache
d9e7c20 is described below
commit d9e7c2081b7f7c3b74a7babde6553f139042ae20
Author: Richard Eckart de Castilho <re...@apache.org>
AuthorDate: Fri Nov 5 14:07:50 2021 +0100
[UIMA-6393]: Circular imports break resource manager cache
- Started refactoring the import resolving - WIP
- Added another test
- Renamed some test files
---
.../metadata/impl/TypeSystemDescription_impl.java | 290 ++++++++++++++++-----
.../impl/TypeSystemDescription_implTest.java | 45 +++-
.../{Circular1.xml => Loop-with-2-nodes-1.xml} | 2 +-
.../{Circular2.xml => Loop-with-2-nodes-2.xml} | 2 +-
.../{Circular1.xml => Loop-with-3-nodes-1.xml} | 4 +-
.../{Circular1.xml => Loop-with-3-nodes-2.xml} | 4 +-
.../{Circular1.xml => Loop-with-3-nodes-3.xml} | 4 +-
7 files changed, 264 insertions(+), 87 deletions(-)
diff --git a/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_impl.java b/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_impl.java
index 6e9f3f2..7e5f1cb 100644
--- a/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_impl.java
+++ b/uimaj-core/src/main/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_impl.java
@@ -19,10 +19,12 @@
package org.apache.uima.resource.metadata.impl;
+import static java.util.Arrays.asList;
+import static org.apache.uima.UIMAFramework.getXMLParser;
+
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -46,11 +48,11 @@ import org.apache.uima.util.XMLizable;
*
*
*/
-public class TypeSystemDescription_impl extends MetaDataObject_impl implements
- TypeSystemDescription {
+public class TypeSystemDescription_impl extends MetaDataObject_impl
+ implements TypeSystemDescription {
static final long serialVersionUID = -3372766232454730201L;
-
+
private String mName;
private String mVersion;
@@ -73,6 +75,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#getName()
*/
+ @Override
public String getName() {
return mName;
}
@@ -80,6 +83,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#setName(String)
*/
+ @Override
public void setName(String aName) {
mName = aName;
}
@@ -87,6 +91,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#getVersion()
*/
+ @Override
public String getVersion() {
return mVersion;
}
@@ -94,6 +99,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#setVersion(String)
*/
+ @Override
public void setVersion(String aVersion) {
mVersion = aVersion;
}
@@ -101,6 +107,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#getDescription()
*/
+ @Override
public String getDescription() {
return mDescription;
}
@@ -108,6 +115,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#setDescription(String)
*/
+ @Override
public void setDescription(String aDescription) {
mDescription = aDescription;
}
@@ -115,6 +123,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#getVendor()
*/
+ @Override
public String getVendor() {
return mVendor;
}
@@ -122,6 +131,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see ResourceMetaData#setVendor(String)
*/
+ @Override
public void setVendor(String aVendor) {
mVendor = aVendor;
}
@@ -129,6 +139,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#getImports()
*/
+ @Override
public Import[] getImports() {
return mImports;
}
@@ -136,6 +147,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#setImports(Import[])
*/
+ @Override
public void setImports(Import[] aImports) {
if (aImports == null) {
throw new UIMA_IllegalArgumentException(UIMA_IllegalArgumentException.ILLEGAL_ARGUMENT,
@@ -147,6 +159,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#getTypes()
*/
+ @Override
public TypeDescription[] getTypes() {
return mTypes;
}
@@ -154,6 +167,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#setTypes(TypeDescription[])
*/
+ @Override
public void setTypes(TypeDescription[] aTypes) {
if (aTypes == null) {
throw new UIMA_IllegalArgumentException(UIMA_IllegalArgumentException.ILLEGAL_ARGUMENT,
@@ -165,6 +179,7 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#addType(String, String, String)
*/
+ @Override
public TypeDescription addType(String aTypeName, String aDescription, String aSupertypeName) {
// create new type description
TypeDescription newType = new TypeDescription_impl(aTypeName, aDescription, aSupertypeName);
@@ -186,10 +201,12 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
/**
* @see TypeSystemDescription#getType(java.lang.String)
*/
+ @Override
public TypeDescription getType(String aTypeName) {
for (int i = 0; i < mTypes.length; i++) {
- if (aTypeName.equals(mTypes[i].getName()))
+ if (aTypeName.equals(mTypes[i].getName())) {
return mTypes[i];
+ }
}
return null;
}
@@ -198,95 +215,224 @@ public class TypeSystemDescription_impl extends MetaDataObject_impl implements
* @see TypeSystemDescription#resolveImports()
*/
// allow these calls to be done multiple times on this same object, in different threads
+ @Override
public synchronized void resolveImports() throws InvalidXMLException {
- if (getImports().length == 0) {
- resolveImports(null, null);
- } else {
- resolveImports(new TreeSet<>(), UIMAFramework.newDefaultResourceManager());
- }
+ resolveImports(null, UIMAFramework.newDefaultResourceManager());
}
- public synchronized void resolveImports(ResourceManager aResourceManager) throws InvalidXMLException {
- resolveImports((getImports().length == 0) ? null : new TreeSet<>(), aResourceManager);
+ @Override
+ public synchronized void resolveImports(ResourceManager aResourceManager)
+ throws InvalidXMLException {
+ resolveImports(null, aResourceManager);
}
+ @Override
public synchronized void resolveImports(Collection<String> aAlreadyImportedTypeSystemURLs,
+ ResourceManager aResMgr) throws InvalidXMLException {
+
+ if (getImports().length == 0) {
+ this.setImports(Import.EMPTY_IMPORTS);
+ }
+
+ List<String> stack = new ArrayList<>();
+ Set<String> seen = new TreeSet<>();
+ if (aAlreadyImportedTypeSystemURLs != null) {
+ seen.addAll(aAlreadyImportedTypeSystemURLs);
+ }
+
+ // add our own URL, if known, to the collection of already imported URLs
+ String url = getSourceUrl() != null ? getSourceUrl().toString() : "<TOP>";
+ seen.add(url);
+ stack.add(url);
+
+ Map<String, XMLizable> importCache = ((ResourceManager_impl) aResMgr).getImportCache();
+ synchronized (importCache) {
+ _resolveImports(url, this, stack, seen, aResMgr);
+ }
+ }
+
+ /**
+ * @param aUrl
+ * URL of the currently processed description
+ * @param aStack
+ * current traversal stack. This is used to discover <b>at which point</b> we entered
+ * into a loop.
+ * @param aSeen
+ * all URLs that were already processed. This is used to see <b>if</b> we enter into any
+ * kind of loop and to discover the transitive imports of a given import.
+ * @param aResourceManager
+ * the resource manager used to resolve imports.
+ * @return the stack depth of the item in the stack which triggered the current loop or
+ * {@code -1}.
+ * @throws InvalidXMLException
+ * if an import could not be resolved.
+ */
+ private static Loop _resolveImports(String aUrl, TypeSystemDescription_impl aDesc,
+ List<String> aStack, Collection<String> aSeen,
ResourceManager aResourceManager) throws InvalidXMLException {
- List<TypeDescription> importedTypes = null;
- if (getImports().length != 0) {
- // add our own URL, if known, to the collection of already imported URLs
- if (getSourceUrl() != null) {
- aAlreadyImportedTypeSystemURLs.add(getSourceUrl().toString());
+
+ Map<String, XMLizable> importCache = ((ResourceManager_impl) aResourceManager).getImportCache();
+ Map<String, Set<String>> transitiveImportCache = ((ResourceManager_impl) aResourceManager)
+ .getImportUrlsCache();
+
+ List<TypeDescription> importedTypes = new ArrayList<>();
+ Loop loop = null;
+ for (Import imp : aDesc.getImports()) {
+ // ensure Import's relative path base is set, to allow for users who create new import objects
+ if (imp instanceof Import_impl) {
+ ((Import_impl) imp).setSourceUrlIfNull(aDesc.getSourceUrl());
+ }
+
+ URL impUrl = imp.findAbsoluteUrl(aResourceManager);
+ String sImpUrl = impUrl.toString();
+ if (aStack.contains(sImpUrl)) {
+ loop = loop == null ? new Loop(sImpUrl, aStack) : loop.extend(sImpUrl, aStack, loop);
+ continue;
}
-
- importedTypes = new ArrayList<>();
- Import[] imports = getImports();
- for (int i = 0; i < imports.length; i++) {
- // make sure Import's relative path base is set, to allow for users who create
- // new import objects
- if (imports[i] instanceof Import_impl) {
- ((Import_impl) imports[i]).setSourceUrlIfNull(this.getSourceUrl());
+
+ aSeen.add(sImpUrl);
+ aStack.add(sImpUrl);
+
+ // check the import cache
+ String urlString = sImpUrl;
+ XMLizable cachedObject = importCache.get(urlString);
+ if (cachedObject instanceof TypeSystemDescription) {
+ TypeSystemDescription desc = (TypeSystemDescription) cachedObject;
+ // Add the URLs parsed for this cached object to the list already-parsed (UIMA-5058)
+ Set<String> transitiveImports = transitiveImportCache.get(urlString);
+ assert transitiveImports != null : "TSD found in cache but no cached transitive imports info";
+ importedTypes.addAll(asList(desc.getTypes()));
+
+ for (String tImp : transitiveImports) {
+ if (aSeen.contains(tImp)) {
+ loop = loop == null ? new Loop(tImp, aStack) : loop.extend(tImp, aStack, loop);
+ } else {
+ aSeen.add(tImp);
+ }
}
- URL url = imports[i].findAbsoluteUrl(aResourceManager);
- if (!aAlreadyImportedTypeSystemURLs.contains(url.toString())) {
- aAlreadyImportedTypeSystemURLs.add(url.toString());
- try {
- resolveImport(url, aAlreadyImportedTypeSystemURLs, importedTypes, aResourceManager);
- } catch (IOException e) {
- throw new InvalidXMLException(InvalidXMLException.IMPORT_FAILED_COULD_NOT_READ_FROM_URL,
- new Object[] { url, imports[i].getSourceUrlString() }, e);
+ } else {
+ // Make a snapshot of the URLs we have "seen" until now
+ TreeSet<String> seenBeforeResolvingImport = new TreeSet<>(aSeen);
+
+ // Resolve the current import - this adds new URLs to the "seen" list
+ TypeSystemDescription_impl desc = loadImportedTypeSystem(imp, impUrl);
+ Loop nestedLoop = _resolveImports(urlString, desc, aStack, aSeen, aResourceManager);
+ importCache.put(urlString, desc);
+
+ // Calculate which URLs were newly added by the above resolving and add them to the
+ // transitive import cache
+ TreeSet<String> transitiveImports = new TreeSet<>(aSeen);
+ transitiveImports.removeAll(seenBeforeResolvingImport);
+ transitiveImportCache.put(urlString, transitiveImports);
+ importedTypes.addAll(asList(desc.getTypes()));
+
+ if (nestedLoop != null) {
+ for (String tImp : nestedLoop.members) {
+ if (aStack.contains(tImp)) {
+ loop = loop == null ? new Loop(tImp, aStack) : loop.extend(tImp, aStack, loop);
+ } else {
+ aSeen.add(tImp);
+ }
}
}
}
+
+ aStack.remove(aStack.size() - 1);
}
- // maybe update this object
- TypeDescription[] existingTypes = this.getTypes();
- if (existingTypes == null) {
- this.setTypes(existingTypes = TypeDescription.EMPTY_TYPE_DESCRIPTIONS);
- }
- if (null != importedTypes) {
- TypeDescription[] newTypes = new TypeDescription[existingTypes.length + importedTypes.size()];
- System.arraycopy(existingTypes, 0, newTypes, 0, existingTypes.length);
- for (int i = 0; i < importedTypes.size(); i++) {
- newTypes[existingTypes.length + i] = importedTypes.get(i);
+
+ aDesc.addTypes(importedTypes);
+
+ // On the way back up here, we need to account for circular imports. So if we skipped
+ // resolving imports because the URL of the descriptor was in the "already imported" list,
+ // then we need to check if the descriptor imports any of the already imported descriptors
+ // and if so update its type list with the types from that descriptor...
+ // See: https://issues.apache.org/jira/browse/UIMA-6393
+ if (loop != null) {
+ int depth = aStack.indexOf(aUrl);
+ assert depth != -1 : "Current type system description not found on stack during import traversal";
+ if (depth == loop.entryPoint) {
+ importCache.put(aUrl, aDesc);
+ Set<String> transitiveImports = new TreeSet<>();
+ for (String member : loop.members) {
+ TypeSystemDescription tsd = (TypeSystemDescription) importCache.get(member);
+ tsd.setTypes(aDesc.getTypes());
+ Set<String> memberTransitiveImports = transitiveImportCache.get(member);
+ if (memberTransitiveImports != null) {
+ transitiveImports.addAll(memberTransitiveImports);
+ }
+ }
+ transitiveImportCache.put(aUrl, transitiveImports);
+ loop = null;
}
- this.setTypes(newTypes);
}
+
// clear imports
- this.setImports(Import.EMPTY_IMPORTS);
+ aDesc.setImports(Import.EMPTY_IMPORTS);
+
+ return loop;
}
- private void resolveImport(URL aURL, Collection<String> aAlreadyImportedTypeSystemURLs,
- Collection<TypeDescription> aResults, ResourceManager aResourceManager) throws InvalidXMLException,
- IOException {
- //check the import cache
- TypeSystemDescription desc;
- String urlString = aURL.toString();
- Map<String, XMLizable> importCache = ((ResourceManager_impl)aResourceManager).getImportCache();
- Map<String, Set<String>> importUrlsCache = ((ResourceManager_impl)aResourceManager).getImportUrlsCache();
- synchronized(importCache) {
- XMLizable cachedObject = importCache.get(urlString);
- if (cachedObject instanceof TypeSystemDescription) {
- desc = (TypeSystemDescription)cachedObject;
- // Add the URLs parsed for this cached object to the list already-parsed (UIMA-5058)
- aAlreadyImportedTypeSystemURLs.addAll(importUrlsCache.get(urlString));
- } else {
- XMLInputSource input;
- input = new XMLInputSource(aURL);
- desc = UIMAFramework.getXMLParser().parseTypeSystemDescription(input);
- TreeSet<String> previouslyImported = new TreeSet<>(aAlreadyImportedTypeSystemURLs);
- desc.resolveImports(aAlreadyImportedTypeSystemURLs, aResourceManager);
- importCache.put(urlString, desc);
- // Save the URLS parsed by this import
- TreeSet<String> locallyImported = new TreeSet<>(aAlreadyImportedTypeSystemURLs);
- locallyImported.removeAll(previouslyImported);
- importUrlsCache.put(urlString, locallyImported);
+ private static TypeSystemDescription_impl loadImportedTypeSystem(Import imp, URL impUrl)
+ throws InvalidXMLException {
+ try {
+ return (TypeSystemDescription_impl) getXMLParser()
+ .parseTypeSystemDescription(new XMLInputSource(impUrl));
+ } catch (IOException e) {
+ throw new InvalidXMLException(InvalidXMLException.IMPORT_FAILED_COULD_NOT_READ_FROM_URL,
+ new Object[] { impUrl, imp.getSourceUrlString() }, e);
+ }
+
+ }
+
+ private static class Loop {
+ final int entryPoint;
+ final String[] members;
+
+ /**
+ * Create new loop.
+ */
+ Loop(String aUrl, List<String> aStack) {
+ entryPoint = aStack.indexOf(aUrl);
+ members = aStack.subList(entryPoint, aStack.size()).stream().toArray(String[]::new);
+ }
+
+ private Loop(int aEntryPoint, String aUrl, Loop aOtherLoop) {
+ entryPoint = Math.min(aEntryPoint, aOtherLoop.entryPoint);
+ members = new String[aOtherLoop.members.length + 1];
+ members[0] = aUrl;
+ System.arraycopy(aOtherLoop.members, 0, members, 1, aOtherLoop.members.length);
+ }
+
+ Loop extend(String aUrl, List<String> aStack, Loop aOtherLoop) {
+ if (asList(aOtherLoop.members).indexOf(aUrl) != -1) {
+ return aOtherLoop;
}
-
+
+ return new Loop(aStack.indexOf(aUrl), aUrl, aOtherLoop);
+ }
+ }
+
+ private void addTypes(Collection<TypeDescription> aTypes) {
+ if (aTypes == null || aTypes.isEmpty()) {
+ return;
+ }
+
+ TypeDescription[] existingTypes = this.getTypes();
+ if (existingTypes == null) {
+ this.setTypes(existingTypes = TypeDescription.EMPTY_TYPE_DESCRIPTIONS);
+ }
+
+ TypeDescription[] newTypes = new TypeDescription[existingTypes.length + aTypes.size()];
+ System.arraycopy(existingTypes, 0, newTypes, 0, existingTypes.length);
+ int i = 0;
+ for (TypeDescription d : aTypes) {
+ newTypes[existingTypes.length + i] = d;
+ i++;
}
- aResults.addAll(Arrays.asList(desc.getTypes()));
+ this.setTypes(newTypes);
}
+ @Override
protected XmlizationInfo getXmlizationInfo() {
return XMLIZATION_INFO;
}
diff --git a/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_implTest.java b/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_implTest.java
index 2684808..f010bdd 100644
--- a/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_implTest.java
+++ b/uimaj-core/src/test/java/org/apache/uima/resource/metadata/impl/TypeSystemDescription_implTest.java
@@ -277,24 +277,55 @@ public class TypeSystemDescription_implTest {
@Test
public void thatCircularImportsDoNotCrash() throws Exception {
- File descriptor = getFile("TypeSystemDescriptionImplTest/Circular1.xml");
+ File descriptor = getFile("TypeSystemDescriptionImplTest/Loop-with-2-nodes-1.xml");
TypeSystemDescription ts = xmlParser.parseTypeSystemDescription(new XMLInputSource(descriptor));
ts.resolveImports();
assertEquals(2, ts.getTypes().length);
}
@Test
- public void thatCircularImportsDoNotConfuseResourceManagerCache() throws Exception {
+ public void thatLoopWithTwoNodesDoNotConfuseResourceManagerCache() throws Exception {
ResourceManager resMgr = newDefaultResourceManager();
- File descriptor = getFile("TypeSystemDescriptionImplTest/Circular1.xml");
- TypeSystemDescription ts = xmlParser.parseTypeSystemDescription(new XMLInputSource(descriptor));
+ File circular1 = getFile("TypeSystemDescriptionImplTest/Loop-with-2-nodes-1.xml");
+ File circular2 = getFile("TypeSystemDescriptionImplTest/Loop-with-2-nodes-2.xml");
+ TypeSystemDescription ts = xmlParser.parseTypeSystemDescription(new XMLInputSource(circular1));
ts.resolveImports(resMgr);
assertThat(ts.getTypes()).hasSize(2);
Map<String, XMLizable> cache = resMgr.getImportCache();
- assertThat(cache).hasSize(1);
- TypeSystemDescription cachedTsd = (TypeSystemDescription) cache.values().iterator().next();
- assertThat(cachedTsd.getTypes()).hasSize(2);
+ assertThat(cache).containsOnlyKeys(circular1.toURL().toString(), circular2.toURL().toString());
+
+ TypeSystemDescription cachedCircular1Tsd = (TypeSystemDescription) cache
+ .get(circular1.toURL().toString());
+ TypeSystemDescription cachedCircular2Tsd = (TypeSystemDescription) cache
+ .get(circular2.toURL().toString());
+ assertThat(cachedCircular1Tsd.getTypes()).hasSize(2);
+ assertThat(cachedCircular2Tsd.getTypes()).hasSize(2);
+ }
+
+ @Test
+ public void thatLoopWithThreeNodesDoNotConfuseResourceManagerCache() throws Exception {
+ ResourceManager resMgr = newDefaultResourceManager();
+ File circular1 = getFile("TypeSystemDescriptionImplTest/Loop-with-3-nodes-1.xml");
+ File circular2 = getFile("TypeSystemDescriptionImplTest/Loop-with-3-nodes-2.xml");
+ File circular3 = getFile("TypeSystemDescriptionImplTest/Loop-with-3-nodes-3.xml");
+ TypeSystemDescription ts = xmlParser.parseTypeSystemDescription(new XMLInputSource(circular1));
+ ts.resolveImports(resMgr);
+ assertThat(ts.getTypes()).hasSize(3);
+
+ Map<String, XMLizable> cache = resMgr.getImportCache();
+ assertThat(cache).containsOnlyKeys(circular1.toURL().toString(), circular2.toURL().toString(),
+ circular3.toURL().toString());
+
+ TypeSystemDescription cachedCircular1Tsd = (TypeSystemDescription) cache
+ .get(circular1.toURL().toString());
+ TypeSystemDescription cachedCircular2Tsd = (TypeSystemDescription) cache
+ .get(circular2.toURL().toString());
+ TypeSystemDescription cachedCircular3Tsd = (TypeSystemDescription) cache
+ .get(circular3.toURL().toString());
+ assertThat(cachedCircular1Tsd.getTypes()).hasSize(3);
+ assertThat(cachedCircular2Tsd.getTypes()).hasSize(3);
+ assertThat(cachedCircular3Tsd.getTypes()).hasSize(3);
}
@Test
diff --git a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-1.xml
similarity index 96%
copy from uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
copy to uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-1.xml
index 8be9616..41c376f 100644
--- a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
+++ b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-1.xml
@@ -27,7 +27,7 @@
<version>0.1</version>
<vendor>The Apache Software Foundation</vendor>
<imports>
-<import location="Circular2.xml"/>
+<import location="Loop-with-2-nodes-2.xml"/>
</imports>
<types>
<typeDescription>
diff --git a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular2.xml b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-2.xml
similarity index 96%
rename from uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular2.xml
rename to uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-2.xml
index a633781..bb806e8 100644
--- a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular2.xml
+++ b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-2-nodes-2.xml
@@ -26,7 +26,7 @@
<version>0.1</version>
<vendor>The Apache Software Foundation</vendor>
<imports>
-<import location="Circular1.xml"/>
+<import location="Loop-with-2-nodes-1.xml"/>
</imports>
<types>
<typeDescription>
diff --git a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-1.xml
similarity index 95%
copy from uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
copy to uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-1.xml
index 8be9616..77903b4 100644
--- a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
+++ b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-1.xml
@@ -22,12 +22,12 @@
-->
-<name>Circular1</name>
+<name>Circular3-1</name>
<description>For testing circular imports.</description>
<version>0.1</version>
<vendor>The Apache Software Foundation</vendor>
<imports>
-<import location="Circular2.xml"/>
+<import location="Loop-with-3-nodes-2.xml"/>
</imports>
<types>
<typeDescription>
diff --git a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-2.xml
similarity index 95%
copy from uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
copy to uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-2.xml
index 8be9616..451947a 100644
--- a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
+++ b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-2.xml
@@ -22,12 +22,12 @@
-->
-<name>Circular1</name>
+<name>Circular3-2</name>
<description>For testing circular imports.</description>
<version>0.1</version>
<vendor>The Apache Software Foundation</vendor>
<imports>
-<import location="Circular2.xml"/>
+<import location="Loop-with-3-nodes-3.xml"/>
</imports>
<types>
<typeDescription>
diff --git a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-3.xml
similarity index 95%
rename from uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
rename to uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-3.xml
index 8be9616..f9e29bc 100644
--- a/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Circular1.xml
+++ b/uimaj-core/src/test/resources/TypeSystemDescriptionImplTest/Loop-with-3-nodes-3.xml
@@ -22,12 +22,12 @@
-->
-<name>Circular1</name>
+<name>Circular3-3</name>
<description>For testing circular imports.</description>
<version>0.1</version>
<vendor>The Apache Software Foundation</vendor>
<imports>
-<import location="Circular2.xml"/>
+<import location="Loop-with-3-nodes-1.xml"/>
</imports>
<types>
<typeDescription>