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" +