Author: schor
Date: Tue Aug 16 21:38:27 2016
New Revision: 1756549

URL: http://svn.apache.org/viewvc?rev=1756549&view=rev
Log:
[UIMA-5055] properly initialize JCas when it's turned on in one class loader 
but not initialized in others.

Modified:
    
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java

Modified: 
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java
URL: 
http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java?rev=1756549&r1=1756548&r2=1756549&view=diff
==============================================================================
--- 
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java
 (original)
+++ 
uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/jcas/impl/JCasImpl.java
 Tue Aug 16 21:38:27 2016
@@ -29,7 +29,9 @@ import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.IdentityHashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.ListIterator;
@@ -295,7 +297,7 @@ public class JCasImpl extends AbstractCa
      */
     private JCasHashMap cAddr2Jfs;
 
-    private final Map<ClassLoader, JCasHashMap> cAddr2JfsByClassLoader = new 
HashMap<ClassLoader, JCasHashMap>();
+    private final Map<ClassLoader, JCasHashMap> cAddr2JfsByClassLoader = new 
IdentityHashMap<ClassLoader, JCasHashMap>();
 
     /* convenience holders of CAS constants that may be useful */
     /* initialization done lazily - on first call to getter */
@@ -312,6 +314,9 @@ public class JCasImpl extends AbstractCa
     public Collection<ErrorReport> errorSet = new ArrayList<ErrorReport>();
 
     public ClassLoader currentClassLoader = null;
+    
+    private ClassLoader cacheClassLoaderInitialized;
+    final private Map<ClassLoader, Boolean> isInitializedForClassLoader = 
Collections.synchronizedMap(new IdentityHashMap<ClassLoader, Boolean>());
 
     private JCasSharedView(CASImpl aCAS, boolean useJcasCache) {
       setupJCasHashMap(aCAS.getJCasClassLoader(), useJcasCache, 
aCAS.getHeap().getInitialSize() / 16);
@@ -422,6 +427,10 @@ public class JCasImpl extends AbstractCa
     return typeArray[i];
   }
   
+  /**
+   * Map from type codes to _Type instances kept per view in the field 
typeArray
+   * @param i
+   */
   private void getTypeInit(final int i) {
     // unknown ID. This may be due to a need to update the typeArray
     // due to switching class loaders. This updating is done
@@ -578,7 +587,7 @@ public class JCasImpl extends AbstractCa
     // * note that many of these may have already been loaded
     // * load all the others. Actually, we ask to load all the types
     // * and the ones already loaded - we just get their loaded versions
-    // returned.
+    //   returned.
     // * Loading will run the static init functions.
 
     while (typeIt.hasNext()) {
@@ -589,8 +598,7 @@ public class JCasImpl extends AbstractCa
         continue;
       if (builtInsWithAltNames.contains(casName))
         // * convert uima.cas.Xxx -> org.apache.uima.jcas.cas.Xxx
-        // * convert uima.tcas.Annotator ->
-        // org.apache.uima.jcas.tcas.Annotator
+        // * convert uima.tcas.Annotator -> org.apache.uima.jcas.tcas.Annotator
         try {
           String nameBase = "org.apache.uima.jcas." + casName.substring(5);
           name_Type = nameBase + "_Type";
@@ -645,7 +653,29 @@ public class JCasImpl extends AbstractCa
       typeArray = newTypeArray;
     }
   }
+  
+  /**
+   * called when switching to JCas and the JCas already exists to check if 
+   * the JCas needs to have classes loaded for this class loader.  This can 
happen when the JCas is first
+   * instantiated while under the scope of a nested UIMA class loader.  This 
could be a Pear class loader, or
+   * even just an ordinary UIMA Class loader for a pipeline, where the 
Framework is running an "exit routine" supplied
+   * by the user, but loaded in a (for example) initial application loader 
context.
+   * @param cl the class loader in use
+   */
+  public void maybeInitializeForClassLoader(ClassLoader cl) {
+    if (sharedView.cacheClassLoaderInitialized == cl) {
+      return;
+    }
+    if (this.sharedView.isInitializedForClassLoader.get(cl) == null) {
+      instantiateJCas_Types(cl);
+    }
+    sharedView.cacheClassLoaderInitialized = cl;
+  }
 
+  /**
+   * 
+   * @param cl the class loader to use as the initiating loader for loading 
JCas classes
+   */
   public void instantiateJCas_Types(ClassLoader cl) {
     Map<String, LoadedJCasType<?>> loadedJCasTypes = null;
     FSClassRegistry fscr = casImpl.getFSClassRegistry();
@@ -668,11 +698,15 @@ public class JCasImpl extends AbstractCa
       // if already loaded, can skip making new generators - 
       //   in this case newFSGeneratorSet is never referenced
       //   Set it to null for "safety"
+      // If not already loaded, initialize the generators to a clone of the 
FSClassRegistry generators.
       newFSGeneratorSet = (alreadyLoaded ? null : fscr.getNewFSGeneratorSet());
       for (Iterator<Map.Entry<String, LoadedJCasType<?>>> it = 
loadedJCasTypes.entrySet().iterator(); it.hasNext();) {
         
         // Explanation for this logic:
         //   Instances of _Types are kept per class loader, per Cas (e.g., in 
the cas pool)
+        //     and per view (to support having the ref to the casImpl be to 
the right view
+        //                   so add-to-indexes works better).
+        //                   The "typeArray" field is per view.
         //   When switching class loaders (e.g. a pear in the pipeline), some 
of the 
         //     _Type instances (e.g. the built-ins) might be already 
instantiated, but others are not
         //   
@@ -682,7 +716,8 @@ public class JCasImpl extends AbstractCa
       }
       
       // speed up - skip rest if nothing to do
-      if (!anyNewInstances) {
+      if (!anyNewInstances && 
+          sharedView.isInitializedForClassLoader.get(cl) != null) {  // don't 
skip if need to install the new generators for this class loader UIMA-5055
         return;
       }
       if (!alreadyLoaded) {
@@ -698,6 +733,7 @@ public class JCasImpl extends AbstractCa
     } else {
       casImpl.setLocalFsGenerators(newFSGeneratorSet);
     }
+    sharedView.isInitializedForClassLoader.put(cl, Boolean.TRUE);
   }
 
   // note all callers are synchronized
@@ -746,6 +782,11 @@ public class JCasImpl extends AbstractCa
    */
   public static JCas getJCas(CASImpl cas) throws CASException {
     JCasImpl jcas = new JCasImpl(cas);
+    reportInitErrors(jcas);
+    return jcas;
+  }
+  
+  private static void reportInitErrors(JCasImpl jcas) throws CASException {
     JCasSharedView sv = jcas.sharedView;
     if (sv.errorSet.size() > 0) {
       boolean doThrow = false;
@@ -769,7 +810,6 @@ public class JCasImpl extends AbstractCa
         }          
       }
     }
-    return jcas;
   }
 
   /**
@@ -941,9 +981,11 @@ public class JCasImpl extends AbstractCa
    * Make the instance of the JCas xxx_Type class for this CAS. Note: not all 
types will have
    * xxx_Type. Instance creation does the typeSystemInit kind of function, as 
well.
    * 
-   * returns true if a new instance of a _Type class was created
+   * @param jcasTypeInfo -
+   * @param alreadyLoaded -
+   * @param fsGenerators updated by side effect with new instances of the 
_Type class
+   * @return true if a new instance of a _Type class was created
    */
-
   private <T extends TOP> boolean makeInstanceOf_Type(LoadedJCasType<T> 
jcasTypeInfo, boolean alreadyLoaded,
       FSGenerator<?>[] fsGenerators) {
     
@@ -954,8 +996,7 @@ public class JCasImpl extends AbstractCa
     //     instantiated _Type instances, but others may be different
     //     (due to different impls of the _Type class loaded by the different
     //     class loader).
-    //   This can also happen in the case where
-    //   JCasImpl.getType is called for a non-existing class
+    //   This can also happen in the case where JCasImpl.getType is called for 
a non-existing class.
     //     What happens in this case is that the getType code has to assume 
that
     //     perhaps none of the _Type instances were made for this JCas (yet), 
because
     //     these are created lazily - so it calls instantiateJCas_Types to 
make them.


Reply via email to