uima-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From sc...@apache.org
Subject svn commit: r997044 - in /uima/uimaj/trunk: uimaj-core/src/main/java/org/apache/uima/resource/impl/ uimaj-core/src/main/java/org/apache/uima/util/ uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/
Date Tue, 14 Sep 2010 19:10:21 GMT
Author: schor
Date: Tue Sep 14 19:10:21 2010
New Revision: 997044

URL: http://svn.apache.org/viewvc?rev=997044&view=rev
Log:
[UIMA-1722] Change CasCreationUtils part where to get metadata info, it instantiates (produceResource)
a resource, gets the meta data info, and then destroys the resource - to cache these for 30
-60 seconds, to avoid repeatedly doing this for the same resource, since it can be very expensive
(e.g., if the resource is a "custom Resource Specifier" used to denote a UIMA-AS remote service,
this entails setting up a connection to that remote resource, and querying it, etc.). The
short timeout insures that if a service becomes available, it won't be too long before the
CDE would notice this ...

Change CDE to increase the odds of reusing things (like the class loader built for the particular
project / datapath), and reducing the number of times it requests getting the metadata.

These two changes greatly speed up CDE opening when it has references to remote async services.

Modified:
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/impl/ResourceManager_impl.java
    uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/CasCreationUtils.java
    uima/uimaj/trunk/uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/MultiPageEditor.java

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/impl/ResourceManager_impl.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/impl/ResourceManager_impl.java?rev=997044&r1=997043&r2=997044&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/impl/ResourceManager_impl.java
(original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/resource/impl/ResourceManager_impl.java
Tue Sep 14 19:10:21 2010
@@ -119,6 +119,25 @@ public class ResourceManager_impl implem
     mRelativePathResolver = new RelativePathResolver_impl();
   }
 
+ /**
+  * Support reusing UIMA Class Loader instances to speed up
+  * things including the Component Description Editor when
+  * obtaining info from CustomResourceSpecifiers
+  * https://issues.apache.org/jira/browse/UIMA-1722
+  * @param uimaCL
+  * @param resolveResource
+  */
+ public void setExtensionClassPath(UIMAClassLoader uimaCL, boolean resolveResource) {
+   this.uimaCL = uimaCL;
+   
+   if (resolveResource) {
+     // set UIMA extension ClassLoader also to resolve resources
+     getRelativePathResolver().setPathResolverClassLoader(uimaCL);
+   }
+ }
+
+ /**
+
   /**
    * @see org.apache.uima.resource.ResourceManager#setExtensionClassPath(java.lang.String,
boolean)
    */

Modified: uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/CasCreationUtils.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/CasCreationUtils.java?rev=997044&r1=997043&r2=997044&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/CasCreationUtils.java (original)
+++ uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/util/CasCreationUtils.java Tue
Sep 14 19:10:21 2010
@@ -22,7 +22,6 @@ package org.apache.uima.util;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.LinkedList;
@@ -30,8 +29,11 @@ import java.util.List;
 import java.util.Map;
 import java.util.Properties;
 import java.util.Set;
+import java.util.Timer;
+import java.util.TimerTask;
 import java.util.TreeMap;
 import java.util.TreeSet;
+import java.util.Map.Entry;
 
 import org.apache.uima.UIMAFramework;
 import org.apache.uima.analysis_engine.AnalysisEngineDescription;
@@ -1673,6 +1675,129 @@ public class CasCreationUtils {
     ;
   }
 
+  /*************************************************************************************************
+   * Caching of getMeta info that requires producing the resource                       
          *
+   *   - done because producing the resource can be very expensive                      
          *                        
+   *     including accessing remote things on the network                               
          *
+   * Cache is cleared approximately every 30 seconds because remote resource's statuses may
change *
+   *                                                                                    
          *
+   * Cache key is the ResourceSpecifier's class loaders and the ResourceManager         
          *
+   *   Both the DataPath and the uima extension class loader are used as part of the key
          *
+   *   because differences in these could cause different metadata to be loaded         
          *
+   *************************************************************************************************/
+  
+  private static class MetaDataCacheKey {
+    final ResourceSpecifier resourceSpecifier;
+    final ClassLoader rmClassLoader;
+    final String rmDataPath;
+    
+    MetaDataCacheKey(ResourceSpecifier resourceSpecifier, ResourceManager resourceManager)
{
+      this.resourceSpecifier = resourceSpecifier;
+      this.rmClassLoader = (null == resourceManager) ? null : resourceManager.getExtensionClassLoader();
// can be null
+      this.rmDataPath = (null == resourceManager) ? null : resourceManager.getDataPath();
+    }
+
+    @Override
+    public int hashCode() {
+      return ((rmClassLoader == null) ? 0 : rmClassLoader.hashCode()) 
+             + ((rmDataPath == null)  ? 0 : rmDataPath.hashCode()) 
+             + resourceSpecifier.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+      MetaDataCacheKey k = (MetaDataCacheKey) obj;
+      if (rmDataPath == null) {
+        if (k.rmDataPath != null) {
+          return false;
+        }
+        return resourceSpecifier.equals(k.resourceSpecifier) && 
+               rmClassLoader == k.rmClassLoader;
+      }
+      return resourceSpecifier.equals(k.resourceSpecifier) && 
+             rmClassLoader == k.rmClassLoader &&
+             rmDataPath.equals(k.rmDataPath);
+    }
+
+    @Override
+    public String toString() {
+      return "MetaDataCacheKey [resourceSpecifier=" + resourceSpecifier + ", rmClassLoader="
+          + rmClassLoader + ", rmDataPath=" + rmDataPath + "]";
+    }
+  }
+  
+  private static final boolean cacheDebug = false; // set true for debugging info
+  private static final int HOLD_TIME = 30000;  // keep cache for 30 seconds, approx., in
case a remote resource changes state
+  
+  /**
+   * This is the cache.
+   * All references to it are synchronized, using it as the object.
+   */
+  private static final transient Map<MetaDataCacheKey, MetaDataCacheEntry> metaDataCache
= new HashMap<MetaDataCacheKey, MetaDataCacheEntry>();
+
+  /** This holds an instance of a Timer object
+   * This object is nulled out and gets gc'd when it's timertask finishes, when the
+   * cache is empty.  
+   * 
+   * All references to it are synchronized under the metaDataCache.
+   */
+  private static Timer cleanupTimer = null;
+
+  /**
+   * This class holds the processing Resource Metadata, or null if there is none, and
+   * a timestamp when the metadata was obtained.
+   */
+  private static class MetaDataCacheEntry {
+    ProcessingResourceMetaData processingResourceMetaData;
+    long creationTime;
+    
+    MetaDataCacheEntry(ResourceMetaData resourceMetaData) {
+      processingResourceMetaData = (resourceMetaData instanceof ProcessingResourceMetaData)
? (ProcessingResourceMetaData) resourceMetaData : null;
+      creationTime = System.currentTimeMillis(); 
+      if (null == cleanupTimer) {
+        if (cacheDebug) {
+          System.err.format("GetMetaDataCache: Scheduling new cleanup task%n");
+        }
+
+        cleanupTimer = new Timer("metaDataCacheCleanup", true);  // run as daemon
+        // create a new instance of the timer task, because a previous one may 
+        // still be running
+        TimerTask metaDataCacheCleanupTask = new TimerTask() {   
+          @Override
+          public void run() {
+            synchronized (metaDataCache) {
+              long now = System.currentTimeMillis();
+              if (cacheDebug) {
+                System.err.format("GetMetaDataCache: cleanup task running%n");
+              }
+              for (Iterator<Entry<MetaDataCacheKey, MetaDataCacheEntry>> it =
metaDataCache.entrySet().iterator(); it.hasNext();) {
+                Entry<MetaDataCacheKey, MetaDataCacheEntry> e = it.next();
+                if (e.getValue().creationTime + HOLD_TIME < now) {
+                  if (cacheDebug) {
+                    System.err.format("GetMetaDataCache: cleanup task removing entry %s%n",
e.getKey().toString() );
+                  }
+                  it.remove();
+                }
+              }
+              if (metaDataCache.size() == 0) {
+                if (cacheDebug) {
+                  System.err.format("GetMetaDataCache: cleanup task terminating, cache empty%n");
+                }
+                cancel();
+                cleanupTimer = null;
+              }
+              if (cacheDebug) {
+                System.err.format("GetMetaDataCache: cleanup task finished a cycle%n");
+              }
+            }
+          }
+        };
+        cleanupTimer.scheduleAtFixedRate(metaDataCacheCleanupTask, HOLD_TIME, HOLD_TIME);
+      }
+    }
+  }
+   
+  
   /**
    * Gets a list of ProcessingResourceMetadata objects from a list containing either
    * ResourceSpecifiers, ProcessingResourceMetadata objects, or subparts of
@@ -1690,6 +1815,13 @@ public class CasCreationUtils {
    * service will be queries for its metadata. An exception will be thrown if the connection
can not
    * be opened.
    * 
+   * Note that this last kind of lookup may be expensive (calling produceResource, which
in turn may
+   * query remote connections etc.).  Because of this, a cache is maintained for these, 
+   * (because some scenarios end up requesting the same metadata multiple times, in rapid
succession).
+   * 
+   * Because remote resource may become available, the cache entries are removed 30 seconds
+   * after they are created.  This also reclaims space from the cache.
+   *  
    * @param aComponentDescriptionOrMetaData
    *                a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
    *                {@link TypeSystemDescription}, {@link FsIndexCollection}, or
@@ -1770,7 +1902,24 @@ public class CasCreationUtils {
         md.setTypePriorities((TypePriorities) current);
         mdList.add(md);
       } else if (current instanceof ResourceSpecifier) {
+                
+        // first try the cache
+        MetaDataCacheKey metaDataCacheKey = new MetaDataCacheKey((ResourceSpecifier)current,
aResourceManager);
+        synchronized(metaDataCache) {
+          MetaDataCacheEntry metaData = metaDataCache.get(metaDataCacheKey);
+          if (null != metaData) {
+            if (cacheDebug) {
+              System.err.format("GetMetaDataCache: using cached entry%n");
+            }
+            if (null != metaData.processingResourceMetaData) {
+              mdList.add(metaData.processingResourceMetaData);
+            }
+            continue;
+          } 
+        }
+        
         // try to instantiate the resource
+        
         Resource resource = null;
         Map<String, Object> prParams = new HashMap<String, Object>();
         if (aResourceManager != null) {
@@ -1781,6 +1930,13 @@ public class CasCreationUtils {
           resource = UIMAFramework.produceResource((ResourceSpecifier) current, prParams);
 //              (null == aResourceManager) ? Collections.<String, Object>emptyMap()
: resourceMgrInMap);
         } catch (Exception e) {
+          // record failure, so we don't ask for this again, for a while
+          synchronized (metaDataCache) {
+            if (cacheDebug) {
+              System.err.format("GetMetaDataCache: saving entry in cache%n");
+            }
+            metaDataCache.put(metaDataCacheKey, new MetaDataCacheEntry(null));
+          }
           // failed. If aOutputFailedRemotes is non-null, add an entry to it to it, else
throw the
           // exception.
           if (aOutputFailedRemotes != null) {
@@ -1794,8 +1950,16 @@ public class CasCreationUtils {
               throw new RuntimeException(e);
           }
         }
+        ResourceMetaData metadata = (resource == null) ? null : resource.getMetaData();
+
+        synchronized (metaDataCache) {
+          if (cacheDebug) {
+            System.err.format("GetMetaDataCache: saving entry in cache%n");
+          }
+          metaDataCache.put(metaDataCacheKey, new MetaDataCacheEntry(metadata));
+        }
+
         if (resource != null) {
-          ResourceMetaData metadata = resource.getMetaData();
           if (metadata instanceof ProcessingResourceMetaData) {
             mdList.add((ProcessingResourceMetaData) metadata);
           }

Modified: uima/uimaj/trunk/uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/MultiPageEditor.java
URL: http://svn.apache.org/viewvc/uima/uimaj/trunk/uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/MultiPageEditor.java?rev=997044&r1=997043&r2=997044&view=diff
==============================================================================
--- uima/uimaj/trunk/uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/MultiPageEditor.java
(original)
+++ uima/uimaj/trunk/uimaj-ep-configurator/src/main/java/org/apache/uima/taeconfigurator/editors/MultiPageEditor.java
Tue Sep 14 19:10:21 2010
@@ -29,6 +29,7 @@ import java.io.InputStream;
 import java.io.PrintStream;
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
+import java.lang.ref.SoftReference;
 import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
@@ -58,12 +59,14 @@ import org.apache.uima.collection.CasCon
 import org.apache.uima.collection.CasInitializerDescription;
 import org.apache.uima.collection.CollectionReaderDescription;
 import org.apache.uima.flow.FlowControllerDescription;
+import org.apache.uima.internal.util.UIMAClassLoader;
 import org.apache.uima.jcas.jcasgenp.MergerImpl;
 import org.apache.uima.resource.ResourceCreationSpecifier;
 import org.apache.uima.resource.ResourceInitializationException;
 import org.apache.uima.resource.ResourceManager;
 import org.apache.uima.resource.ResourceServiceSpecifier;
 import org.apache.uima.resource.ResourceSpecifier;
+import org.apache.uima.resource.impl.ResourceManager_impl;
 import org.apache.uima.resource.metadata.Capability;
 import org.apache.uima.resource.metadata.FsIndexCollection;
 import org.apache.uima.resource.metadata.Import;
@@ -1087,18 +1090,41 @@ public class MultiPageEditor extends For
     return rm;
   }
 
+  private String cachedRMclassPath = null; 
+  private SoftReference<UIMAClassLoader> cachedRMcl = new SoftReference<UIMAClassLoader>(null);
+
   public ResourceManager createResourceManager(String classPath) {
-    // workspacePath = TAEConfiguratorPlugin.getWorkspace().getRoot().getLocation().toString();
     ResourceManager resourceManager = UIMAFramework.newDefaultResourceManager();
 
     try {
-      if (null == classPath)
+      if (null == classPath) {
         classPath = getProjectClassPath();
-      // first arg in next is the parent of the class loader.  Make it be the
-      //   uima framework's class loader (not this class's class loader)
-      //   so the validation tests work properly (that test isAssignableFrom)
-      resourceManager.setExtensionClassPath(UIMAFramework.class.getClassLoader(), classPath,
true);
-      resourceManager.setDataPath(CDEpropertyPage.getDataPath(getProject()));
+      }      
+      String dataPath = CDEpropertyPage.getDataPath(getProject());
+      
+      // first try to get the value of the class loader from the last (cached)
+      // value - should succeed frequently because the class loader is only dependent
+      // on the value of the classpath
+      
+      UIMAClassLoader uimaCL = null;
+      if (cachedRMclassPath != null &&
+          cachedRMclassPath.equals(classPath)) {
+        uimaCL = cachedRMcl.get();
+      }
+      
+      if (uimaCL != null) {
+        ((ResourceManager_impl)resourceManager).setExtensionClassPath(uimaCL, true);
+      } else {
+        // first arg in next is the parent of the class loader.  Make it be the
+        //   uima framework's class loader (not this class's class loader)
+        //   so the validation tests work properly (that test isAssignableFrom)
+        resourceManager.setExtensionClassPath(UIMAFramework.class.getClassLoader(), classPath,
true);
+        cachedRMclassPath = classPath;
+        cachedRMcl = new SoftReference<UIMAClassLoader>((UIMAClassLoader) resourceManager.getExtensionClassLoader());
+      }
+      
+      // in any case, set the data path
+      resourceManager.setDataPath(dataPath);
     } catch (MalformedURLException e1) {
       throw new InternalErrorCDE(Messages.getString("MultiPageEditor.14"), e1); //$NON-NLS-1$
     } catch (CoreException e1) {
@@ -1437,19 +1463,20 @@ public class MultiPageEditor extends For
     } catch (InvalidXMLException e) {
       throw new ResourceInitializationException(e);
     }
+    // get the metadata once, because it can be expensive to do     
+    AnalysisEngineMetaData md = aeDescription.getAnalysisEngineMetaData();
+
     // These come before setTypeSystemDescription call because that call
-    // invokeds tcas validate, which uses the merged values for speedup
+    // invokes tcas validate, which uses the merged values for speedup
     // Here we set them to values that won't cause errors. They're set to actual values below.
-    mergedFsIndexCollection = aeDescription.getAnalysisEngineMetaData().getFsIndexCollection();
-    mergedTypePriorities = aeDescription.getAnalysisEngineMetaData().getTypePriorities();
+    mergedFsIndexCollection = md.getFsIndexCollection();
+    mergedTypePriorities = md.getTypePriorities();
     resolvedExternalResourcesAndBindings = aeDescription.getResourceManagerConfiguration();
     resolvedFlowControllerDeclaration = aeDescription.getFlowControllerDeclaration();
 
-    setTypeSystemDescription(aeDescription.isPrimitive() ? aeDescription
-            .getAnalysisEngineMetaData().getTypeSystem() : null); // aggregates have null
-    // tsd. If passed in one
-    // isn't null, make it
-    // null.
+    setTypeSystemDescription(aeDescription.isPrimitive() ? md.getTypeSystem() : null); 
+    // aggregates have null type system descriptors. 
+    // If passed in one that isn't null, make it null.
 
     // These come after setTypeSystemDescription call, even though
     // that call invokeds tcas validate, which uses the merged values for speedup
@@ -2957,7 +2984,7 @@ public class MultiPageEditor extends For
       sb.append("Component key-name(s): ").append(names.get(i))
         .append(": ")
         .append(getMessagesToRootCause((Exception)exceptions.get(i)))
-        .append("\n");
+        .append("\n---------------\n");
     }
     
     Utility.popMessage("Remotes Unavailable", "Note: This message is only shown once.\n\n"
+



Mime
View raw message