[UIMA-6232] Reduce overhead of createTypeSystemDescription() and friends

* Update branch for merging into master

Merge branch 'master' into feature/UIMA-6232-Reduce-overhead-of-createTypeSystemDescription-and-friends

* master: (124 commits)
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release uimafit-3.1.0
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release uimafit-3.1.0
  [NO JIRA] Adjust UIMA version in the NOTICE file of the binary distribution
  [maven-release-plugin] prepare for next development iteration
  [maven-release-plugin] prepare release uimafit-3.1.0
  [NO JIRA] Update documentation to changed API in ExternalResourceFactory and removed a spurious character
  [NO JIRA] Adjusted comparison version for API change report
  [NO JIRA] Updated README file for release.
  [NO JIRA] Updated README file for release.
  [UIMA-6214] Method signature class in ExternalResourceFactory
  Update an Eclipse metadata file.
  [UIMA-5847] Remove UV3 bug workarounds in FSCollectionFactory
  [UIMA-6216] Upgrade to UIMA Core 3.1.1
  [NO JIRA] Fix NOTICE file
  No issue. Make japicmp pick up the right post-analysis file.
  No issue. Set version to 3.1.0-SNAPSHOT
  [UIMA-6181]b Fix complaints about maven-plugin-plugin in Eclipse
  [No Issue] Minor improvement of unit test illustrating error when binding resources to aggregates with nested dependent AEs.
  ...

# Conflicts:
#	uimafit-core/src/main/java/org/apache/uima/fit/factory/FsIndexFactory.java
#	uimafit-core/src/main/java/org/apache/uima/fit/factory/TypePrioritiesFactory.java
#	uimafit-core/src/main/java/org/apache/uima/fit/factory/TypeSystemDescriptionFactory.java
diff --git a/uimafit-core/src/main/java/org/apache/uima/fit/factory/FsIndexFactory.java b/uimafit-core/src/main/java/org/apache/uima/fit/factory/FsIndexFactory.java
index 0b32eb1..a8fa976 100644
--- a/uimafit-core/src/main/java/org/apache/uima/fit/factory/FsIndexFactory.java
+++ b/uimafit-core/src/main/java/org/apache/uima/fit/factory/FsIndexFactory.java
@@ -6,9 +6,9 @@
  * 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
@@ -26,6 +26,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.WeakHashMap;
 
 import org.apache.uima.fit.descriptor.FsIndex;
 import org.apache.uima.fit.descriptor.FsIndexKey;
@@ -43,6 +44,7 @@
 import org.apache.uima.util.XMLInputSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ClassUtils;
 
 /**
  */
@@ -64,6 +66,17 @@
 
   private static final Object SCAN_LOCK = new Object();
 
+  private static final Object CREATE_LOCK = new Object();
+
+  private static WeakHashMap<ClassLoader, String[]> fsIndexLocationsByClassloader;
+
+  private static WeakHashMap<ClassLoader, FsIndexCollection> fsIndexCollectionsByClassloader;
+
+  static {
+    fsIndexLocationsByClassloader = new WeakHashMap<>();
+    fsIndexCollectionsByClassloader = new WeakHashMap<>();
+  }
+
   private FsIndexFactory() {
     // Factory class
   }
@@ -71,13 +84,13 @@
   /**
    * Create index configuration data for a given class definition using reflection and the
    * configuration parameter annotation.
-   * 
+   *
    * @param componentClass
    *          the class to analyze
    * @return the index collection
    */
   public static FsIndexCollection createFsIndexCollection(Class<?> componentClass) {
-    List<FsIndex> anFsIndexList = new ArrayList<FsIndex>();
+    List<FsIndex> anFsIndexList = new ArrayList<>();
 
     // Check FsIndexCollection annotation
     org.apache.uima.fit.descriptor.FsIndexCollection anIndexCollection = getInheritableAnnotation(
@@ -91,10 +104,10 @@
             componentClass);
     if (anFsIndex != null) {
       if (anIndexCollection != null) {
-        throw new IllegalStateException("Class [" + componentClass.getName() + "] must not "
-                + "declare "
-                + org.apache.uima.fit.descriptor.FsIndexCollection.class.getSimpleName() + " and "
-                + FsIndex.class.getSimpleName() + " at the same time.");
+        throw new IllegalStateException(
+                "Class [" + componentClass.getName() + "] must not " + "declare "
+                        + org.apache.uima.fit.descriptor.FsIndexCollection.class.getSimpleName()
+                        + " and " + FsIndex.class.getSimpleName() + " at the same time.");
       }
 
       anFsIndexList.add(anFsIndex);
@@ -105,7 +118,7 @@
     // Process collected FsIndex annotations
     for (FsIndex anIdx : anFsIndexList) {
       // Collect index keys
-      List<FsIndexKeyDescription> keys = new ArrayList<FsIndexKeyDescription>();
+      List<FsIndexKeyDescription> keys = new ArrayList<>();
       for (FsIndexKey anIndexKey : anIdx.keys()) {
         keys.add(createFsIndexKeyDescription(anIndexKey.featureName(), anIndexKey.comparator()));
       }
@@ -161,7 +174,7 @@
 
   /**
    * Create a index collection from a set of descriptions.
-   * 
+   *
    * @param descriptions
    *          the index descriptions
    * @return the index collection
@@ -188,7 +201,8 @@
    *          the index comparator
    * @return the index key description
    */
-  public static FsIndexKeyDescription createFsIndexKeyDescription(String featureName, int comparator) {
+  public static FsIndexKeyDescription createFsIndexKeyDescription(String featureName,
+          int comparator) {
     FsIndexKeyDescription_impl key = new FsIndexKeyDescription_impl();
     key.setFeatureName(featureName);
     key.setComparator(comparator);
@@ -196,17 +210,15 @@
     return key;
   }
 
-  private static String[] indexDescriptorLocations;
-
   /**
    * Creates a {@link FsIndexCollection} from descriptor names.
-   * 
+   *
    * @param descriptorNames
    *          The fully qualified, Java-style, dotted descriptor names.
    * @return a {@link FsIndexCollection} that includes the indexes from all of the specified files.
    */
   public static FsIndexCollection createFsIndexCollection(String... descriptorNames) {
-    List<Import> imports = new ArrayList<Import>();
+    List<Import> imports = new ArrayList<>();
     for (String descriptorName : descriptorNames) {
       Import imp = new Import_impl();
       imp.setName(descriptorName);
@@ -221,13 +233,13 @@
 
   /**
    * Creates a {@link FsIndexCollection} from a descriptor file
-   * 
+   *
    * @param descriptorURIs
    *          The descriptor file paths.
    * @return A {@link FsIndexCollection} that includes the indexes from all of the specified files.
    */
   public static FsIndexCollection createTypeSystemDescriptionFromPath(String... descriptorURIs) {
-    List<Import> imports = new ArrayList<Import>();
+    List<Import> imports = new ArrayList<>();
     for (String descriptorURI : descriptorURIs) {
       Import imp = new Import_impl();
       imp.setLocation(descriptorURI);
@@ -244,45 +256,58 @@
    * Creates a {@link FsIndexCollection} from all index descriptions that can be found via the
    * pattern specified in the system property {@code org.apache.uima.fit.fsindex.import_pattern} or
    * via the {@code META-INF/org.apache.uima.fit/fsindexes.txt} files in the classpath.
-   * 
+   *
    * @return the auto-scanned indexes.
    * @throws ResourceInitializationException
    *           if the index collection could not be assembled
    */
   public static FsIndexCollection createFsIndexCollection() throws ResourceInitializationException {
-    List<FsIndexDescription> fsIndexList = new ArrayList<FsIndexDescription>();
-    for (String location : scanIndexDescriptors()) {
-      try {
-        XMLInputSource xmlInput = new XMLInputSource(location);
-        FsIndexCollection fsIdxCol = getXMLParser().parseFsIndexCollection(xmlInput);
-        fsIdxCol.resolveImports();
-        fsIndexList.addAll(asList(fsIdxCol.getFsIndexes()));
-        LOG.debug("Detected index at [{}]", location);
-      } catch (IOException e) {
-        throw new ResourceInitializationException(e);
-      } catch (InvalidXMLException e) {
-        LOG.warn("[{}] is not a index descriptor file. Ignoring.", location, e);
+    ClassLoader cl = ClassUtils.getDefaultClassLoader();
+    FsIndexCollection aggFsIdxCol = fsIndexCollectionsByClassloader.get(cl);
+    if (aggFsIdxCol == null) {
+      synchronized (CREATE_LOCK) {
+        List<FsIndexDescription> fsIndexList = new ArrayList<>();
+        for (String location : scanIndexDescriptors()) {
+          try {
+            XMLInputSource xmlInput = new XMLInputSource(location);
+            FsIndexCollection fsIdxCol = getXMLParser().parseFsIndexCollection(xmlInput);
+            fsIdxCol.resolveImports();
+            fsIndexList.addAll(asList(fsIdxCol.getFsIndexes()));
+            LOG.debug("Detected index at [{}]", location);
+          } catch (IOException e) {
+            throw new ResourceInitializationException(e);
+          } catch (InvalidXMLException e) {
+            LOG.warn("[{}] is not a index descriptor file. Ignoring.", location, e);
+          }
+        }
+
+        aggFsIdxCol = createFsIndexCollection(
+                fsIndexList.toArray(new FsIndexDescription[fsIndexList.size()]));
+        fsIndexCollectionsByClassloader.put(cl, aggFsIdxCol);
       }
     }
 
-    return createFsIndexCollection(fsIndexList.toArray(new FsIndexDescription[fsIndexList.size()]));
+    return (FsIndexCollection) aggFsIdxCol.clone();
   }
 
   /**
    * Get all currently accessible index descriptor locations. A scan is actually only performed on
    * the first call and the locations are cached. To force a re-scan use
    * {@link #forceIndexDescriptorsScan()}.
-   * 
+   *
    * @return an array of locations.
    * @throws ResourceInitializationException
    *           if the locations could not be resolved.
    */
   public static String[] scanIndexDescriptors() throws ResourceInitializationException {
     synchronized (SCAN_LOCK) {
-      if (indexDescriptorLocations == null) {
-        indexDescriptorLocations = scanDescriptors(MetaDataType.FS_INDEX);
+      ClassLoader cl = ClassUtils.getDefaultClassLoader();
+      String[] indexLocations = fsIndexLocationsByClassloader.get(cl);
+      if (indexLocations == null) {
+        indexLocations = scanDescriptors(MetaDataType.FS_INDEX);
+        fsIndexLocationsByClassloader.put(cl, indexLocations);
       }
-      return indexDescriptorLocations;
+      return indexLocations;
     }
   }
 
@@ -291,6 +316,9 @@
    * all auto-import locations.
    */
   public static void forceIndexDescriptorsScan() {
-    indexDescriptorLocations = null;
+    synchronized (SCAN_LOCK) {
+      fsIndexLocationsByClassloader.clear();
+      fsIndexCollectionsByClassloader.clear();
+    }
   }
 }
diff --git a/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypePrioritiesFactory.java b/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypePrioritiesFactory.java
index f75abf7..2073441 100644
--- a/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypePrioritiesFactory.java
+++ b/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypePrioritiesFactory.java
@@ -6,9 +6,9 @@
  * 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
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.WeakHashMap;
 
 import org.apache.uima.fit.internal.MetaDataType;
 import org.apache.uima.fit.internal.ResourceManagerFactory;
@@ -39,13 +40,23 @@
 import org.apache.uima.util.XMLInputSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ClassUtils;
 
 public final class TypePrioritiesFactory {
   private static final Logger LOG = LoggerFactory.getLogger(TypePrioritiesFactory.class);
     
   private static final Object SCAN_LOCK = new Object();
 
-  private static String[] typePriorityDescriptorLocations;
+  private static final Object CREATE_LOCK = new Object();
+
+  private static WeakHashMap<ClassLoader, String[]> typePrioritesLocationsByClassloader;
+
+  private static WeakHashMap<ClassLoader, TypePriorities> typePrioritiesByClassloader;
+
+  static {
+    typePrioritesLocationsByClassloader = new WeakHashMap<>();
+    typePrioritiesByClassloader = new WeakHashMap<>();
+  }
 
   private TypePrioritiesFactory() {
     // This class is not meant to be instantiated
@@ -53,7 +64,7 @@
 
   /**
    * Create a TypePriorities given a sequence of ordered type classes
-   * 
+   *
    * @param prioritizedTypes
    *          a sequence of ordered type classes
    * @return type priorities created from the ordered JCas classes
@@ -77,8 +88,8 @@
   }
 
   /**
-   * Create a TypePriorities given a sequence of ordered type names
-   * 
+   * Create a {@link TypePriorities} given a sequence of ordered type names
+   *
    * @param prioritizedTypeNames
    *          a sequence of ordered type names
    * @return type priorities created from the ordered type names
@@ -97,49 +108,58 @@
    * the pattern specified in the system property
    * {@code org.apache.uima.fit.typepriorities.import_pattern} or via the
    * {@code META-INF/org.apache.uima.fit/typepriorities.txt} files in the classpath.
-   * 
+   *
    * @return the auto-scanned type priorities.
    * @throws ResourceInitializationException
    *           if the collected type priorities cannot be merged.
    */
   public static TypePriorities createTypePriorities() throws ResourceInitializationException {
-    List<TypePriorities> typePrioritiesList = new ArrayList<TypePriorities>();
-    for (String location : scanTypePrioritiesDescriptors()) {
-      try {
-        XMLInputSource xmlInput = new XMLInputSource(location);
-        TypePriorities typePriorities = getXMLParser().parseTypePriorities(xmlInput);
-        typePriorities.resolveImports();
-        typePrioritiesList.add(typePriorities);
-        LOG.debug("Detected type priorities at [{}]", location);
-      } catch (IOException e) {
-        throw new ResourceInitializationException(e);
-      } catch (InvalidXMLException e) {
-        LOG.warn("[{}] is not a type priorities descriptor file. Ignoring.", location, e);
+    ClassLoader cl = ClassUtils.getDefaultClassLoader();
+    TypePriorities aggTypePriorities = typePrioritiesByClassloader.get(cl);
+    if (aggTypePriorities == null) {
+      synchronized (CREATE_LOCK) {
+        List<TypePriorities> typePrioritiesList = new ArrayList<>();
+        for (String location : scanTypePrioritiesDescriptors()) {
+          try {
+            XMLInputSource xmlInput = new XMLInputSource(location);
+            TypePriorities typePriorities = getXMLParser().parseTypePriorities(xmlInput);
+            typePriorities.resolveImports();
+            typePrioritiesList.add(typePriorities);
+            LOG.debug("Detected type priorities at [{}]", location);
+          } catch (IOException e) {
+            throw new ResourceInitializationException(e);
+          } catch (InvalidXMLException e) {
+            LOG.warn("[{}] is not a type priorities descriptor file. Ignoring.", location, e);
+          }
+        }
+
+        ResourceManager resMgr = ResourceManagerFactory.newResourceManager();
+        aggTypePriorities = CasCreationUtils.mergeTypePriorities(typePrioritiesList, resMgr);
+        typePrioritiesByClassloader.put(cl, aggTypePriorities);
       }
     }
 
-    ResourceManager resMgr = ResourceManagerFactory.newResourceManager();
-    TypePriorities aggTypePriorities = CasCreationUtils.mergeTypePriorities(typePrioritiesList,
-            resMgr);
-
-    return aggTypePriorities;
+    return (TypePriorities) aggTypePriorities.clone();
   }
 
   /**
    * Get all currently accessible type priorities descriptor locations. A scan is actually only
    * performed on the first call and the locations are cached. To force a re-scan use
    * {@link #forceTypePrioritiesDescriptorsScan()}.
-   * 
+   *
    * @return an array of locations.
    * @throws ResourceInitializationException
    *           if the locations could not be resolved.
    */
   public static String[] scanTypePrioritiesDescriptors() throws ResourceInitializationException {
     synchronized (SCAN_LOCK) {
-      if (typePriorityDescriptorLocations == null) {
-        typePriorityDescriptorLocations = scanDescriptors(MetaDataType.TYPE_PRIORITIES);
+      ClassLoader cl = ClassUtils.getDefaultClassLoader();
+      String[] typePrioritesLocations = typePrioritesLocationsByClassloader.get(cl);
+      if (typePrioritesLocations == null) {
+        typePrioritesLocations = scanDescriptors(MetaDataType.TYPE_PRIORITIES);
+        typePrioritesLocationsByClassloader.put(cl, typePrioritesLocations);
       }
-      return typePriorityDescriptorLocations;
+      return typePrioritesLocations;
     }
   }
 
@@ -148,6 +168,9 @@
    * {@link #scanTypePrioritiesDescriptors()} will rescan all auto-import locations.
    */
   public static void forceTypePrioritiesDescriptorsScan() {
-    typePriorityDescriptorLocations = null;
+    synchronized (SCAN_LOCK) {
+      typePrioritesLocationsByClassloader.clear();
+      typePrioritiesByClassloader.clear();
+    }
   }
 }
diff --git a/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypeSystemDescriptionFactory.java b/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypeSystemDescriptionFactory.java
index a0a2f98..dc05a40 100644
--- a/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypeSystemDescriptionFactory.java
+++ b/uimafit-core/src/main/java/org/apache/uima/fit/factory/TypeSystemDescriptionFactory.java
@@ -6,9 +6,9 @@
  * 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
@@ -25,6 +25,7 @@
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.WeakHashMap;
 
 import org.apache.uima.fit.internal.MetaDataType;
 import org.apache.uima.fit.internal.ResourceManagerFactory;
@@ -38,13 +39,23 @@
 import org.apache.uima.util.XMLInputSource;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.springframework.util.ClassUtils;
 
 public final class TypeSystemDescriptionFactory {
   private static final Logger LOG = LoggerFactory.getLogger(TypeSystemDescriptionFactory.class);
   
   private static final Object SCAN_LOCK = new Object();
 
-  private static String[] typeDescriptorLocations;
+  private static final Object CREATE_LOCK = new Object();
+
+  private static WeakHashMap<ClassLoader, String[]> typeDescriptorLocationsByClassloader;
+
+  private static WeakHashMap<ClassLoader, TypeSystemDescription> typeDescriptorByClassloader;
+
+  static {
+    typeDescriptorLocationsByClassloader = new WeakHashMap<>();
+    typeDescriptorByClassloader = new WeakHashMap<>();
+  }
 
   private TypeSystemDescriptionFactory() {
     // This class is not meant to be instantiated
@@ -52,14 +63,14 @@
 
   /**
    * Creates a TypeSystemDescription from descriptor names.
-   * 
+   *
    * @param descriptorNames
    *          The fully qualified, Java-style, dotted descriptor names.
    * @return A TypeSystemDescription that includes the types from all of the specified files.
    */
   public static TypeSystemDescription createTypeSystemDescription(String... descriptorNames) {
     TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
-    List<Import> imports = new ArrayList<Import>();
+    List<Import> imports = new ArrayList<>();
     for (String descriptorName : descriptorNames) {
       Import imp = new Import_impl();
       imp.setName(descriptorName);
@@ -71,15 +82,16 @@
   }
 
   /**
-   * Creates a TypeSystemDescription from a descriptor file
-   * 
+   * Creates a {@link TypeSystemDescription} from a descriptor file
+   *
    * @param descriptorURIs
    *          The descriptor file paths.
    * @return A TypeSystemDescription that includes the types from all of the specified files.
    */
-  public static TypeSystemDescription createTypeSystemDescriptionFromPath(String... descriptorURIs) {
+  public static TypeSystemDescription createTypeSystemDescriptionFromPath(
+          String... descriptorURIs) {
     TypeSystemDescription typeSystem = new TypeSystemDescription_impl();
-    List<Import> imports = new ArrayList<Import>();
+    List<Import> imports = new ArrayList<>();
     for (String descriptorURI : descriptorURIs) {
       Import imp = new Import_impl();
       imp.setLocation(descriptorURI);
@@ -94,43 +106,54 @@
    * Creates a {@link TypeSystemDescription} from all type descriptions that can be found via the
    * default import pattern or via the {@code META-INF/org.apache.uima.fit/types.txt} files in the
    * classpath.
-   * 
+   *
    * @return the auto-scanned type system.
    * @throws ResourceInitializationException
    *           if the collected type system descriptions cannot be merged.
    */
   public static TypeSystemDescription createTypeSystemDescription()
           throws ResourceInitializationException {
-    List<TypeSystemDescription> tsdList = new ArrayList<TypeSystemDescription>();
-    for (String location : scanTypeDescriptors()) {
-      try {
-        XMLInputSource xmlInputType1 = new XMLInputSource(location);
-        tsdList.add(getXMLParser().parseTypeSystemDescription(xmlInputType1));
-        LOG.debug("Detected type system at [{}]", location);
-      } catch (IOException e) {
-        throw new ResourceInitializationException(e);
-      } catch (InvalidXMLException e) {
-        LOG.warn("[{}] is not a type file. Ignoring.", location, e);
+    ClassLoader cl = ClassUtils.getDefaultClassLoader();
+    TypeSystemDescription tsd = typeDescriptorByClassloader.get(cl);
+    if (tsd == null) {
+      synchronized (CREATE_LOCK) {
+        List<TypeSystemDescription> tsdList = new ArrayList<>();
+        for (String location : scanTypeDescriptors()) {
+          try {
+            XMLInputSource xmlInputType1 = new XMLInputSource(location);
+            tsdList.add(getXMLParser().parseTypeSystemDescription(xmlInputType1));
+            LOG.debug("Detected type system at [{}]", location);
+          } catch (IOException e) {
+            throw new ResourceInitializationException(e);
+          } catch (InvalidXMLException e) {
+            LOG.warn("[{}] is not a type file. Ignoring.", location, e);
+          }
+        }
+
+        ResourceManager resMgr = ResourceManagerFactory.newResourceManager();
+        tsd = mergeTypeSystems(tsdList, resMgr);
+        typeDescriptorByClassloader.put(cl, tsd);
       }
     }
-
-    ResourceManager resMgr = ResourceManagerFactory.newResourceManager();
-    return mergeTypeSystems(tsdList, resMgr);
+    return (TypeSystemDescription) tsd.clone();
   }
 
   /**
    * Get all currently accessible type system descriptor locations. A scan is actually only
    * performed on the first call and the locations are cached. To force a re-scan use
    * {@link #forceTypeDescriptorsScan()}.
-   * 
+   *
    * @return an array of locations.
    * @throws ResourceInitializationException
    *           if the locations could not be resolved.
    */
   public static String[] scanTypeDescriptors() throws ResourceInitializationException {
     synchronized (SCAN_LOCK) {
+      ClassLoader cl = ClassUtils.getDefaultClassLoader();
+      String[] typeDescriptorLocations = typeDescriptorLocationsByClassloader.get(cl);
       if (typeDescriptorLocations == null) {
         typeDescriptorLocations = scanDescriptors(MetaDataType.TYPE_SYSTEM);
+        typeDescriptorLocationsByClassloader.put(cl, typeDescriptorLocations);
       }
       return typeDescriptorLocations;
     }
@@ -141,6 +164,9 @@
    * all auto-import locations.
    */
   public static void forceTypeDescriptorsScan() {
-    typeDescriptorLocations = null;
+    synchronized (SCAN_LOCK) {
+      typeDescriptorLocationsByClassloader.clear();
+      typeDescriptorByClassloader.clear();
+    }
   }
 }
diff --git a/uimafit-core/src/main/java/org/apache/uima/fit/internal/MetaDataUtil.java b/uimafit-core/src/main/java/org/apache/uima/fit/internal/MetaDataUtil.java
index 73528a1..6b13c56 100644
--- a/uimafit-core/src/main/java/org/apache/uima/fit/internal/MetaDataUtil.java
+++ b/uimafit-core/src/main/java/org/apache/uima/fit/internal/MetaDataUtil.java
@@ -6,9 +6,9 @@
  * 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
@@ -45,7 +45,7 @@
 
   /**
    * Scan patterns from manifest files and from the specified system property.
-   * 
+   *
    * @param aType
    *          the type of metadata to scan for
    * @return array or all patterns found.
@@ -54,7 +54,7 @@
    */
   public static String[] scanImportsAndManifests(MetaDataType aType)
           throws ResourceInitializationException {
-    ArrayList<String> patterns = new ArrayList<String>();
+    ArrayList<String> patterns = new ArrayList<>();
 
     // Scan auto-import locations
     for (String property : getImportProperties(aType)) {
@@ -80,7 +80,7 @@
 
   /**
    * Resolve a list of patterns to a set of URLs.
-   * 
+   *
    * @param patterns
    *          the patterns to resolve
    * @return an array of locations.
@@ -88,11 +88,11 @@
    *           if the locations could not be resolved.
    */
   public static String[] resolve(String... patterns) throws ResourceInitializationException {
-    Set<String> locations = new HashSet<String>();
+    Set<String> locations = new HashSet<>();
     PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
     try {
       // Scan auto-import locations. Using a set to avoid scanning a pattern twice.
-      for (String pattern : new TreeSet<String>(Arrays.asList(patterns))) {
+      for (String pattern : new TreeSet<>(Arrays.asList(patterns))) {
         String p = pattern.trim();
         if (p.length() == 0) {
           continue;
@@ -109,13 +109,13 @@
 
   /**
    * Get manifest locations for the specified type.
-   * 
+   *
    * @param aType
    *          the type of metadata to scan for
    * @return the manifest locations for this kind of metadata to scan
    */
   public static String[] getManifestLocations(MetaDataType aType) {
-    List<String> locations = new ArrayList<String>();
+    List<String> locations = new ArrayList<>();
     switch (aType) {
       case FS_INDEX:
         locations.add("classpath*:META-INF/org.apache.uima.fit/fsindexes.txt");
@@ -125,7 +125,7 @@
         break;
       case TYPE_PRIORITIES:
         locations.add("classpath*:META-INF/org.apache.uima.fit/typepriorities.txt");
-        break;        
+        break;
     }
 
     return locations.toArray(new String[locations.size()]);
@@ -134,13 +134,13 @@
   /**
    * Get system properties indicating which locations to scan for descriptions of the given type. A
    * list of locations may be given separated by ";".
-   * 
+   *
    * @param aType
    *          the type of metadata to scan for
    * @return the locations for this kind of metadata to scan
    */
   public static String[] getImportProperties(MetaDataType aType) {
-    List<String> locations = new ArrayList<String>();
+    List<String> locations = new ArrayList<>();
     switch (aType) {
       case FS_INDEX:
         locations.add("org.apache.uima.fit.fsindex.import_pattern");
@@ -150,18 +150,18 @@
         break;
       case TYPE_PRIORITIES:
         locations.add("org.apache.uima.fit.typepriorities.import_pattern");
-        break;        
+        break;
     }
 
     return locations.toArray(new String[locations.size()]);
   }
-  
+
   /**
    * Get all currently accessible descriptor locations for the given type.
-   * 
+   *
    * @param aType
    *          the type of metadata to scan for
-      * @return an array of locations.
+   * @return an array of locations.
    * @throws ResourceInitializationException
    *           if the locations could not be resolved.
    */
diff --git a/uimafit-maven-plugin/src/main/java/org/apache/uima/fit/maven/GenerateDescriptorsMojo.java b/uimafit-maven-plugin/src/main/java/org/apache/uima/fit/maven/GenerateDescriptorsMojo.java
index 797eae8..8cda479 100644
--- a/uimafit-maven-plugin/src/main/java/org/apache/uima/fit/maven/GenerateDescriptorsMojo.java
+++ b/uimafit-maven-plugin/src/main/java/org/apache/uima/fit/maven/GenerateDescriptorsMojo.java
@@ -6,9 +6,9 @@
  * 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
@@ -23,6 +23,7 @@
 import java.io.IOException;
 import java.io.OutputStream;
 import java.lang.reflect.Modifier;
+
 import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.exception.ExceptionUtils;
 import org.apache.maven.plugin.AbstractMojo;
@@ -66,7 +67,7 @@
    */
   @Parameter(defaultValue = "${project.build.directory}/classes", required = true)
   private File outputDirectory;
-  
+
   /**
    * Skip generation of META-INF/org.apache.uima.fit/components.txt
    */
@@ -97,7 +98,7 @@
       outputDirectory.mkdirs();
       buildContext.refresh(outputDirectory);
     }
-    
+
     // Get the compiled classes from this project
     String[] files = FileUtils.getFilesFromExtension(project.getBuild().getOutputDirectory(),
             new String[] { "class" });
@@ -112,14 +113,19 @@
       String base = file.substring(0, file.length() - 6);
       String clazzPath = base.substring(project.getBuild().getOutputDirectory().length() + 1);
       String clazzName = clazzPath.replace(File.separator, ".");
+
+      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
       try {
         Class clazz = componentLoader.loadClass(clazzName);
-        
+
+        // Make the componentLoader available to uimaFIT e.g. to resolve imports
+        Thread.currentThread().setContextClassLoader(componentLoader);
+
         // Do not generate descriptors for abstract classes, they cannot be instantiated.
         if (Modifier.isAbstract(clazz.getModifiers())) {
           continue;
         }
-        
+
         ResourceCreationSpecifier desc = null;
         ProcessingResourceMetaData metadata = null;
         switch (Util.getType(componentLoader, clazz)) {
@@ -151,7 +157,7 @@
           out.getParentFile().mkdirs();
           toXML(desc, out.getPath());
           countGenerated++;
-          
+
           // Remember component
           componentsManifest.append("classpath*:").append(clazzPath + ".xml").append('\n');
         }
@@ -163,12 +169,14 @@
         getLog().warn("Cannot analyze class [" + clazzName + "]", e);
       } catch (ResourceInitializationException e) {
         getLog().warn("Cannot generate descriptor for [" + clazzName + "]", e);
+      } finally {
+        Thread.currentThread().setContextClassLoader(classLoader);
       }
     }
-    
+
     getLog().info(
             "Generated " + countGenerated + " descriptor" + (countGenerated != 1 ? "s." : "."));
-    
+
     // Write META-INF/org.apache.uima.fit/components.txt unless skipped and unless there are no
     // components
     if (!skipComponentsManifest && componentsManifest.length() > 0) {
@@ -185,15 +193,9 @@
 
   private void embedTypeSystems(ProcessingResourceMetaData metadata)
           throws ResourceInitializationException {
-    ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-    Thread.currentThread().setContextClassLoader(componentLoader);
-    try {
-      TypeSystemDescriptionFactory.forceTypeDescriptorsScan();
-      TypeSystemDescription tsDesc = TypeSystemDescriptionFactory.createTypeSystemDescription();
-      metadata.setTypeSystem(tsDesc);
-    } finally {
-      Thread.currentThread().setContextClassLoader(classLoader);
-    }
+    TypeSystemDescriptionFactory.forceTypeDescriptorsScan();
+    TypeSystemDescription tsDesc = TypeSystemDescriptionFactory.createTypeSystemDescription();
+    metadata.setTypeSystem(tsDesc);
   }
 
   /**