blob: eabf1de1e06312230f5433929171344df8dbe741 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* 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
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.uima.util;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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 org.apache.uima.UIMAFramework;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.impl.AnalysisEngineImplBase;
import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.CASException;
import org.apache.uima.cas.FSIndex;
import org.apache.uima.cas.Feature;
import org.apache.uima.cas.Type;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.admin.CASFactory;
import org.apache.uima.cas.admin.CASMgr;
import org.apache.uima.cas.admin.FSIndexComparator;
import org.apache.uima.cas.admin.FSIndexRepositoryMgr;
import org.apache.uima.cas.admin.LinearTypeOrderBuilder;
import org.apache.uima.cas.admin.TypeSystemMgr;
import org.apache.uima.cas.impl.CASImpl;
import org.apache.uima.cas.impl.TypeSystemImpl;
import org.apache.uima.cas_data.CasData;
import org.apache.uima.cas_data.FeatureStructure;
import org.apache.uima.cas_data.PrimitiveValue;
import org.apache.uima.collection.CasConsumerDescription;
import org.apache.uima.collection.CasInitializerDescription;
import org.apache.uima.collection.CollectionReaderDescription;
import org.apache.uima.flow.FlowControllerDescription;
import org.apache.uima.resource.CasDefinition;
import org.apache.uima.resource.Resource;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceManager;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.URISpecifier;
import org.apache.uima.resource.metadata.AllowedValue;
import org.apache.uima.resource.metadata.FeatureDescription;
import org.apache.uima.resource.metadata.FsIndexCollection;
import org.apache.uima.resource.metadata.FsIndexDescription;
import org.apache.uima.resource.metadata.FsIndexKeyDescription;
import org.apache.uima.resource.metadata.MetaDataObject;
import org.apache.uima.resource.metadata.ProcessingResourceMetaData;
import org.apache.uima.resource.metadata.ResourceMetaData;
import org.apache.uima.resource.metadata.TypeDescription;
import org.apache.uima.resource.metadata.TypePriorities;
import org.apache.uima.resource.metadata.TypePriorityList;
import org.apache.uima.resource.metadata.TypeSystemDescription;
import org.apache.uima.resource.metadata.impl.ProcessingResourceMetaData_impl;
/**
* Utilities for creating and setting up CASes. Also includes utilities for merging CAS type
* systems.
*/
public class CasCreationUtils {
private final static AllowedValue[] EMPTY_ALLOWED_VALUE_ARRAY = new AllowedValue[0];
private final static FeatureDescription[] EMPTY_FEAT_DESC_ARRAY = new FeatureDescription[0];
/**
* Creates a new CAS instance. Note this method does not work for Aggregate Analysis Engine
* descriptors -- use {@link #createCas(AnalysisEngineDescription)} instead.
*
* @param aMetaData
* metadata for the analysis engine that will process this CAS. This is used to set
* up the CAS's type system and indexes.
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(AnalysisEngineMetaData aMetaData)
throws ResourceInitializationException {
List<AnalysisEngineMetaData> list = new ArrayList<AnalysisEngineMetaData>();
list.add(aMetaData);
return createCas(list);
}
/**
* Creates a new CAS instance.
*
* @param aMetaData
* metadata for the resource that will process this CAS. This is used to set up the
* CAS's type system and indexes.
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(ProcessingResourceMetaData aMetaData)
throws ResourceInitializationException {
List<ProcessingResourceMetaData> list = new ArrayList<ProcessingResourceMetaData>();
list.add(aMetaData);
return createCas(list);
}
/**
* Creates a new CAS instance for an Analysis Engine. This works for both primitive and aggregate
* analysis engines.
*
* @param aDescription
* description of the analysis engine that will process this CAS. This is used to
* set up the CAS's type system and indexes.
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(AnalysisEngineDescription aDescription)
throws ResourceInitializationException {
return createCas(aDescription, UIMAFramework.getDefaultPerformanceTuningProperties());
}
/**
* Creates a new CAS instance for an Analysis Engine. This works for both primitive and aggregate
* analysis engines.
*
* @param aDescription
* description of the analysis engine that will process this CAS. This is used to
* set up the CAS's type system and indexes.
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(AnalysisEngineDescription aDescription,
Properties aPerformanceTuningSettings) throws ResourceInitializationException {
List<AnalysisEngineDescription> list = new ArrayList<AnalysisEngineDescription>();
list.add(aDescription);
return createCas(list, aPerformanceTuningSettings);
}
/**
* Creates a new CAS instance for a collection of CAS Processors. This method correctly handles
* aggregate as well as primitive analysis engines
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionsOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(Collection<? extends MetaDataObject> aComponentDescriptionsOrMetaData)
throws ResourceInitializationException {
return createCas(aComponentDescriptionsOrMetaData, UIMAFramework
.getDefaultPerformanceTuningProperties());
}
/**
* Creates a new CAS instance for a collection of CAS Processors. This method correctly handles
* aggregate as well as primitive analysis engines
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionsOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(Collection<? extends MetaDataObject> aComponentDescriptionsOrMetaData,
Properties aPerformanceTuningSettings) throws ResourceInitializationException {
return createCas(aComponentDescriptionsOrMetaData, aPerformanceTuningSettings, UIMAFramework
.newDefaultResourceManager());
}
/**
* Creates a new CAS instance for a collection of CAS Processors. This method correctly handles
* aggregate as well as primitive analysis engines
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionsOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
* @param aResourceManager
* the resource manager to use to resolve import declarations within the metadata
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(Collection<? extends MetaDataObject> aComponentDescriptionsOrMetaData,
Properties aPerformanceTuningSettings, ResourceManager aResourceManager)
throws ResourceInitializationException {
// build a list of metadata objects
List<ProcessingResourceMetaData> mdList = getMetaDataList(aComponentDescriptionsOrMetaData, aResourceManager);
// extract TypeSystems, TypePriorities, and FsIndexes from metadata
List<TypeSystemDescription> typeSystems = new ArrayList<TypeSystemDescription>();
List<TypePriorities> typePriorities = new ArrayList<TypePriorities>();
List<FsIndexCollection> fsIndexes = new ArrayList<FsIndexCollection>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypeSystem() != null)
typeSystems.add(md.getTypeSystem());
if (md.getTypePriorities() != null)
typePriorities.add(md.getTypePriorities());
if (md.getFsIndexCollection() != null)
fsIndexes.add(md.getFsIndexCollection());
}
// merge
TypeSystemDescription aggTypeDesc = mergeTypeSystems(typeSystems, aResourceManager);
TypePriorities aggTypePriorities = mergeTypePriorities(typePriorities, aResourceManager);
FsIndexCollection aggIndexColl = mergeFsIndexes(fsIndexes, aResourceManager);
return doCreateCas(null, aggTypeDesc, aggTypePriorities, aggIndexColl.getFsIndexes(),
aPerformanceTuningSettings, aResourceManager);
}
/**
* Creates a new CAS instance.
*
* @param aTypeSystem
* type system to install in the CAS
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(TypeSystemDescription aTypeSystem, TypePriorities aTypePriorities,
FsIndexDescription[] aFsIndexes) throws ResourceInitializationException {
return createCas(aTypeSystem, aTypePriorities, aFsIndexes, null, null);
}
/**
* Creates a new CAS instance.
*
* @param aTypeSystem
* type system to install in the CAS
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(TypeSystemDescription aTypeSystem, TypePriorities aTypePriorities,
FsIndexDescription[] aFsIndexes, Properties aPerformanceTuningSettings)
throws ResourceInitializationException {
return createCas(aTypeSystem, aTypePriorities, aFsIndexes, aPerformanceTuningSettings, null);
}
/**
* Creates a new CAS instance.
*
* @param aTypeSystem
* type system to install in the CAS
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
* @param aResourceManager
* the resource manager
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(TypeSystemDescription aTypeSystem, TypePriorities aTypePriorities,
FsIndexDescription[] aFsIndexes, Properties aPerformanceTuningSettings,
ResourceManager aResourceManager) throws ResourceInitializationException {
return doCreateCas(null, aTypeSystem, aTypePriorities, aFsIndexes, aPerformanceTuningSettings,
aResourceManager);
}
/**
* Creates a new CAS instance for a collection of CAS Processors, which. reuses an existing type
* system. Using this method allows several CASes to all share the exact same type system object.
* This method correctly handles aggregate as well as primitive analysis engines.
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionsOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
* @param aTypeSystem
* type system to install in the CAS, null if none
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(Collection<? extends MetaDataObject> aComponentDescriptionsOrMetaData, TypeSystem aTypeSystem,
Properties aPerformanceTuningSettings) throws ResourceInitializationException {
return createCas(aComponentDescriptionsOrMetaData, aTypeSystem, aPerformanceTuningSettings,
UIMAFramework.newDefaultResourceManager());
}
/**
* Creates a new CAS instance for a collection of CAS Processors, which. reuses an existing type
* system. Using this method allows several CASes to all share the exact same type system object.
* This method correctly handles aggregate as well as primitive analysis engines.
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionsOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
* @param aTypeSystem
* type system to install in the CAS, null if none
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
* @param aResourceManager
* the resource manager to use to resolve import declarations within the metadata,
* and also to set the JCas ClassLoader for the new CAS
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(Collection<? extends MetaDataObject> aComponentDescriptionsOrMetaData, TypeSystem aTypeSystem,
Properties aPerformanceTuningSettings, ResourceManager aResourceManager)
throws ResourceInitializationException {
// build a list of metadata objects
List<ProcessingResourceMetaData> mdList = getMetaDataList(aComponentDescriptionsOrMetaData, aResourceManager);
// extract TypeSystems, TypePriorities, and FsIndexes from metadata
List<TypeSystemDescription> typeSystems = new ArrayList<TypeSystemDescription>();
List<TypePriorities> typePriorities = new ArrayList<TypePriorities>();
List<FsIndexCollection> fsIndexes = new ArrayList<FsIndexCollection>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypeSystem() != null)
typeSystems.add(md.getTypeSystem());
if (md.getTypePriorities() != null)
typePriorities.add(md.getTypePriorities());
if (md.getFsIndexCollection() != null)
fsIndexes.add(md.getFsIndexCollection());
}
// merge TypePriorities and FsIndexes
TypePriorities aggTypePriorities = mergeTypePriorities(typePriorities, aResourceManager);
FsIndexCollection aggIndexColl = mergeFsIndexes(fsIndexes, aResourceManager);
if (aTypeSystem != null) // existing type system object was specified; use that
{
return doCreateCas(aTypeSystem, null, aggTypePriorities, aggIndexColl.getFsIndexes(),
aPerformanceTuningSettings, aResourceManager);
} else {
// no type system object specified; merge type system descriptions in metadata
TypeSystemDescription aggTypeDesc = mergeTypeSystems(typeSystems);
return doCreateCas(null, aggTypeDesc, aggTypePriorities, aggIndexColl.getFsIndexes(),
aPerformanceTuningSettings, aResourceManager);
}
}
/**
* Creates a new CAS instance that reuses an existing type system. Using this method allows
* several CASes to all share the exact same type system object.
*
* @param aTypeSystem
* type system to install in the CAS
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
* @param aPerformanceTuningSettings
* the settings for performance tuning
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(TypeSystem aTypeSystem, TypePriorities aTypePriorities,
FsIndexDescription[] aFsIndexes, Properties aPerformanceTuningSettings)
throws ResourceInitializationException {
return createCas(aTypeSystem, aTypePriorities, aFsIndexes, aPerformanceTuningSettings, null);
}
/**
* Creates a new CAS instance that reuses an existing type system. Using this method allows
* several CASes to all share the exact same type system object.
*
* @param aTypeSystem
* type system to install in the CAS
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
* @param aPerformanceTuningSettings
* the settings for performance tuning
* @param aResourceManager
* resource manager, which is used to set the JCas ClassLoader for the new CAS
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(TypeSystem aTypeSystem, TypePriorities aTypePriorities,
FsIndexDescription[] aFsIndexes, Properties aPerformanceTuningSettings,
ResourceManager aResourceManager) throws ResourceInitializationException {
return doCreateCas(aTypeSystem, null, aTypePriorities, aFsIndexes, aPerformanceTuningSettings,
aResourceManager);
}
/**
* Method that does the work for creating a new CAS instance. Other createCas methods in this
* class should all eventually call this method, so that the critical code is not duplicated in
* more than one place.
*
* @param aTypeSystem
* an existing type system to reuse in this CAS, null if none.
* @param aTypeSystemDescription
* description of type system to use for this CAS. This is only used if aTypeSystem
* is null.
* @param aTypePriorities
* type priorities to install in the CAS
* @param aFsIndexes
* indexes to install in the CAS
* @param aPerformanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS instance
*
* @throws ResourceInitializationException
* if CAS creation fails
*/
private static CAS doCreateCas(TypeSystem aTypeSystem, TypeSystemDescription aTypeSystemDesc,
TypePriorities aTypePriorities, FsIndexDescription[] aFsIndexes,
Properties aPerformanceTuningSettings, ResourceManager aResourceManager)
throws ResourceInitializationException {
if (aResourceManager == null) {
aResourceManager = UIMAFramework.newDefaultResourceManager();
}
// resolve imports
try {
if (aTypeSystemDesc != null) {
aTypeSystemDesc.resolveImports(aResourceManager);
//even though there's only one Type System, we still need to do a merge, to handle the
//case where this TypeSystem defines the same type more than once (or has imports that do)
List<TypeSystemDescription> tsList = new ArrayList<TypeSystemDescription>();
tsList.add(aTypeSystemDesc);
aTypeSystemDesc = mergeTypeSystems(tsList, aResourceManager, null);
}
if (aTypePriorities != null) {
aTypePriorities.resolveImports(aResourceManager);
}
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
// get initial heap size
String initialHeapSizeStr = null;
if (aPerformanceTuningSettings != null) {
initialHeapSizeStr = aPerformanceTuningSettings
.getProperty(UIMAFramework.CAS_INITIAL_HEAP_SIZE);
}
int initialHeapSize = (null == initialHeapSizeStr)
? CASImpl.DEFAULT_INITIAL_HEAP_SIZE
: Integer.parseInt(initialHeapSizeStr);
// Check Jcas cache performance setting. Defaults to true.
boolean useJcasCache = true;
if (aPerformanceTuningSettings != null) {
String useJcasCacheString = aPerformanceTuningSettings.getProperty(
UIMAFramework.JCAS_CACHE_ENABLED, "true");
if ("false".equalsIgnoreCase(useJcasCacheString)) {
useJcasCache = false;
}
}
// create CAS using either aTypeSystem or aTypeSystemDesc
CASMgr casMgr;
if (aTypeSystem != null) {
casMgr = CASFactory.createCAS(initialHeapSize, aTypeSystem, useJcasCache);
// Set JCas ClassLoader - before setupTypeSystem
if (aResourceManager.getExtensionClassLoader() != null) {
casMgr.setJCasClassLoader(aResourceManager.getExtensionClassLoader());
}
} else // no TypeSystem to reuse - create a new one
{
casMgr = CASFactory.createCAS();
if (aResourceManager.getExtensionClassLoader() != null) {
casMgr.setJCasClassLoader(aResourceManager.getExtensionClassLoader());
}
// install type system
setupTypeSystem(casMgr, aTypeSystemDesc);
// Commit the type system
((CASImpl) casMgr).commitTypeSystem();
}
try {
// install TypePriorities into CAS
setupTypePriorities(casMgr, aTypePriorities);
// install Built-in indexes into CAS
casMgr.initCASIndexes();
} catch (CASException e) {
throw new ResourceInitializationException(e);
}
// install AnalysisEngine's custom indexes into CAS
setupIndexes(casMgr, aFsIndexes);
// Commit the index repository
casMgr.getIndexRepositoryMgr().commit();
return casMgr.getCAS().getView(CAS.NAME_DEFAULT_SOFA);
}
/**
* Create a CAS from a CAS Definition.
*
* In V3, creating the type system is expensive (due to loading and setting up of JCas classes), so
* we do the type system creation once per CasDefinition and store it with the CAS definition
*
* @param casDef
* completely describes the CAS to be created
* @param performanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
*
* @return a new CAS matching the given CasDefinition
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(CasDefinition casDef, Properties performanceTuningSettings)
throws ResourceInitializationException {
TypeSystemImpl tsi = casDef.getTypeSystemImpl();
CAS cas;
if (null == tsi) {
synchronized (casDef) {
if (null == tsi) { // retest under sync lock
cas = createCas(casDef.getTypeSystemDescription(), casDef.getTypePriorities(),
casDef.getFsIndexDescriptions(), performanceTuningSettings, casDef.getResourceManager());
casDef.setTypeSystemImpl((TypeSystemImpl) cas.getTypeSystem());
return cas;
}
}
}
return doCreateCas(tsi, casDef.getTypeSystemDescription(), casDef.getTypePriorities(),
casDef.getFsIndexDescriptions(), performanceTuningSettings, casDef.getResourceManager());
}
/**
* Create a CAS from a CAS Definition, but reuse the provided TypeSystem object.
*
* @param casDef
* completely describes the CAS to be created
* @param performanceTuningSettings
* Properties object containing framework performance tuning settings using key
* names defined on {@link UIMAFramework} interface
* @param typeSystem
* type system object to reuse
*
* @return a new CAS matching the given CasDefinition
* @throws ResourceInitializationException
* if CAS creation fails
*/
public static CAS createCas(CasDefinition casDef, Properties performanceTuningSettings,
TypeSystem typeSystem) throws ResourceInitializationException {
return createCas(typeSystem, casDef.getTypePriorities(), casDef.getFsIndexDescriptions(),
performanceTuningSettings, casDef.getResourceManager());
}
/**
* Installs a TypeSystem in a CAS.
*
* @param aCASMgr
* the <code>CASMgr</code> object whose type system is to be modified.
* @param aTypeSystem
* description of type system to install
*
* @throws ResourceInitializationException
* if an error occurs during modification of the type system
*/
public static void setupTypeSystem(CASMgr aCASMgr, TypeSystemDescription aTypeSystem)
throws ResourceInitializationException {
TypeSystemMgr typeSystemMgr = aCASMgr.getTypeSystemMgr();
if (aTypeSystem != null) {
TypeDescription[] types = aTypeSystem.getTypes();
if (types != null) {
// add all Types first (so that we can handle forward references) - note
// that it isn't guaranteed that a supertype will occur in the Types list
// before its subtype.
// Build a linked list of type descriptions. We will make multiple passes
// over this, adding types to the CAS and removing them from the linked
// list. We continue until the list is empty or we cannot make any
// progress.
LinkedList<TypeDescription> typeList = new LinkedList<TypeDescription>();
typeList.addAll(Arrays.asList(types));
int numTypes = typeList.size();
int lastNumTypes;
List<TypeDescription> typesInOrderOfCreation = new LinkedList<TypeDescription>();
do {
lastNumTypes = numTypes;
Iterator<TypeDescription> it = typeList.iterator();
while (it.hasNext()) {
TypeDescription curTypeDesc = it.next();
String typeName = curTypeDesc.getName();
// type does not exist - add it under the appropriate supertype
String superTypeName = curTypeDesc.getSupertypeName();
if (superTypeName == null) {
throw new ResourceInitializationException(
ResourceInitializationException.NO_SUPERTYPE, new Object[] { typeName,
curTypeDesc.getSourceUrlString() });
}
// Check if it's a built-in type: must not change supertype!
Type builtIn = typeSystemMgr.getType(typeName);
if (builtIn != null) {
if (!superTypeName.equals(typeSystemMgr.getParent(builtIn).getName())) {
throw new ResourceInitializationException(
ResourceInitializationException.REDEFINING_BUILTIN_TYPE, new Object[] {
typeSystemMgr.getParent(builtIn), typeName, superTypeName,
curTypeDesc.getSourceUrlString() });
}
}
Type supertype = typeSystemMgr.getType(superTypeName);
if (supertype != null) {
// supertype is defined, so add to CAS type system
// check for special "enumerated types" that extend String
if (curTypeDesc.getSupertypeName().equals(CAS.TYPE_NAME_STRING)) {
AllowedValue[] vals = curTypeDesc.getAllowedValues();
if (vals == null) {
throw new ResourceInitializationException(
ResourceInitializationException.MISSING_ALLOWED_VALUES, new Object[] {
typeName, curTypeDesc.getSourceUrlString() });
}
String[] valStrs = new String[vals.length];
for (int i = 0; i < valStrs.length; i++) {
valStrs[i] = vals[i].getString();
}
typeSystemMgr.addStringSubtype(typeName, valStrs);
} else // a "normal" type
{
// make sure that allowed values are NOT specified for non-string subtypes
if (curTypeDesc.getAllowedValues() != null
&& curTypeDesc.getAllowedValues().length > 0) {
throw new ResourceInitializationException(
ResourceInitializationException.ALLOWED_VALUES_ON_NON_STRING_TYPE,
new Object[] { typeName, curTypeDesc.getSourceUrlString() });
}
typeSystemMgr.addType(typeName, supertype);
}
// remove from list of type descriptions and add it to the typesInOrderOfCreation list
// for later processing
it.remove();
typesInOrderOfCreation.add(curTypeDesc);
}
}
numTypes = typeList.size();
} while (numTypes > 0 && numTypes != lastNumTypes);
// we quit the above loop either when we've added all types or when
// we went through the entire list without successfully finding any
// supertypes. In the latter case, throw an exception. Since there
// can be more than one such type, we look for one that does not have
// ancestor in the list as it is the likely cause of the issue. The
// implementation of this is not as efficient as it could be but avoids
// issues with cyclic definitions.
for (int i = 0; i < typeList.size(); i++) {
TypeDescription td_i = typeList.get(i);
boolean foundSuperType = false;
for (int j = 0; j < typeList.size(); j++) {
if (i == j) {
continue;
}
TypeDescription td_j = typeList.get(j);
if (td_j.getName().equals(td_i.getSupertypeName())) {
foundSuperType = true;
break;
}
}
if (!foundSuperType) {
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_SUPERTYPE, new Object[] {
td_i.getSupertypeName(), td_i.getName(), td_i.getSourceUrlString() });
}
}
if (numTypes > 0) {
// We get here in either of two cases: there was only one problematic
// type definition, or there was a cycle.
TypeDescription firstFailed = typeList.getFirst();
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_SUPERTYPE, new Object[] {
firstFailed.getSupertypeName(), firstFailed.getName(),
firstFailed.getSourceUrlString() });
}
// now for each type, add its features. We add features to supertypes before subtypes. This
// is done so that
// if we have a duplicate feature name on both a supertype and a subtype, it is added to the
// supertype and then
// ignored when we get to the subtype. Although this is a dubious type system, we support it
// for backwards
// compatibility (but we might want to think about generating a warning).
Iterator<TypeDescription> typeIter = typesInOrderOfCreation.iterator();
while (typeIter.hasNext()) {
TypeDescription typeDesc = typeIter.next();
Type type = typeSystemMgr.getType(typeDesc.getName());
// assert type != null;
FeatureDescription[] features = typeDesc.getFeatures();
if (features != null) {
for (int j = 0; j < features.length; j++) {
String featName = features[j].getName();
String rangeTypeName = features[j].getRangeTypeName();
Type rangeType = typeSystemMgr.getType(rangeTypeName);
if (rangeType == null) {
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_RANGE_TYPE, new Object[] {
rangeTypeName, featName, typeDesc.getName(),
features[j].getSourceUrlString() });
}
if (rangeType.isArray()) // TODO: also List?
{
// if an element type is specified, get the specific
// array subtype for that element type
String elementTypeName = features[j].getElementType();
if (elementTypeName != null && elementTypeName.length() > 0) {
Type elementType = typeSystemMgr.getType(elementTypeName);
if (elementType == null) {
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_RANGE_TYPE, new Object[] {
elementTypeName, featName, typeDesc.getName(),
features[j].getSourceUrlString() });
}
rangeType = typeSystemMgr.getArrayType(elementType);
}
}
Boolean multiRefAllowed = features[j].getMultipleReferencesAllowed();
if (multiRefAllowed == null) {
multiRefAllowed = Boolean.FALSE; // default to false if unspecified
}
typeSystemMgr.addFeature(featName, type, rangeType, multiRefAllowed.booleanValue());
}
}
}
}
}
}
/**
* Adds TypePriorities to a CAS.
*
* @param aCASMgr
* the <code>CASMgr</code> object to be modified
* @param aTypePriorities
* description of the type priorities to add
*
* @throws ResourceInitializationException
* if an error occurs during type priority setup
*/
public static void setupTypePriorities(CASMgr aCASMgr, TypePriorities aTypePriorities)
throws ResourceInitializationException {
if (aTypePriorities != null) {
LinearTypeOrderBuilder typeOrderBuilder = aCASMgr.getIndexRepositoryMgr()
.getDefaultOrderBuilder();
TypePriorityList[] priorityLists = aTypePriorities.getPriorityLists();
for (int i = 0; i < priorityLists.length; i++) {
// check that all types exist. This error would be caught in
// typeOrderBuilder.getOrder(), but that's too late to indicate
// the location of the faulty descriptor in the error message.
String[] typeList = priorityLists[i].getTypes();
for (int j = 0; j < typeList.length; j++) {
if (aCASMgr.getTypeSystemMgr().getType(typeList[j]) == null) {
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_TYPE_FOR_PRIORITY_LIST, new Object[] {
typeList[j], priorityLists[i].getSourceUrlString() });
}
}
try {
typeOrderBuilder.add(priorityLists[i].getTypes());
} catch (CASException e) {
// typically caused by a cycle in the priorities - the caused-by message
// will clarify.
throw new ResourceInitializationException(
ResourceInitializationException.INVALID_TYPE_PRIORITIES,
new Object[] { priorityLists[i].getSourceUrlString() }, e);
}
}
}
}
/**
* Adds FeatureStructure indexes to a CAS.
*
* @param aCASMgr
* the <code>CASMgr</code> object to be modified
* @param aIndexes
* descriptions of the indexes to add
*
* @throws ResourceInitializationException
* if an error occurs during index creation
*/
public static void setupIndexes(CASMgr aCASMgr, FsIndexDescription[] aIndexes)
throws ResourceInitializationException {
if (aIndexes != null) {
TypeSystemMgr tsm = aCASMgr.getTypeSystemMgr();
FSIndexRepositoryMgr irm = aCASMgr.getIndexRepositoryMgr();
for (int i = 0; i < aIndexes.length; i++) {
int kind = FSIndex.SORTED_INDEX;
String kindStr = aIndexes[i].getKind();
if (kindStr != null) {
if (kindStr.equals(FsIndexDescription.KIND_BAG))
kind = FSIndex.BAG_INDEX;
else if (kindStr.equals(FsIndexDescription.KIND_SET))
kind = FSIndex.SET_INDEX;
else if (kindStr.equals(FsIndexDescription.KIND_SORTED))
kind = FSIndex.SORTED_INDEX;
}
Type type = tsm.getType(aIndexes[i].getTypeName());
if (type == null) {
throw new ResourceInitializationException(
ResourceInitializationException.UNDEFINED_TYPE_FOR_INDEX, new Object[] {
aIndexes[i].getTypeName(), aIndexes[i].getLabel(),
aIndexes[i].getSourceUrlString() });
}
FSIndexComparator comparator = irm.createComparator();
comparator.setType(type);
FsIndexKeyDescription[] keys = aIndexes[i].getKeys();
if (keys != null) {
for (int j = 0; j < keys.length; j++) {
if (keys[j].isTypePriority()) {
comparator.addKey(irm.getDefaultTypeOrder(), FSIndexComparator.STANDARD_COMPARE);
} else {
Feature feature = type.getFeatureByBaseName(keys[j].getFeatureName());
if (feature == null) {
throw new ResourceInitializationException(
ResourceInitializationException.INDEX_KEY_FEATURE_NOT_FOUND, new Object[] {
keys[j].getFeatureName(), aIndexes[i].getLabel(),
aIndexes[i].getSourceUrlString() });
}
comparator.addKey(feature, keys[j].getComparator());
}
}
}
irm.createIndex(comparator, aIndexes[i].getLabel(), kind);
}
}
}
/**
* Extracts a TypeSystem definition from a CasData.
*
* @param aCasData
* the CAS Data from which to extract the type system
*
* @return a description of a TypeSystem to which the CAS Data conforms
*/
public static TypeSystemDescription convertData2TypeSystem(CasData aCasData) {
TypeSystemDescription result = UIMAFramework.getResourceSpecifierFactory()
.createTypeSystemDescription();
Iterator<FeatureStructure> iter = aCasData.getFeatureStructures();
List<TypeDescription> typesArr = new ArrayList<TypeDescription>();
while (iter.hasNext()) {
FeatureStructure casFS = iter.next();
TypeDescription newType = UIMAFramework.getResourceSpecifierFactory().createTypeDescription();
newType.setName(casFS.getType());
newType.setSupertypeName("uima.tcas.annotation");
newType.setDescription("CasData Type");
String features[] = casFS.getFeatureNames();
if (features != null) {
for (int i = 0; i < features.length; i++) {
String featName = features[i];
String rangeName = "";
String description = "";
PrimitiveValue pVal = (PrimitiveValue) casFS.getFeatureValue(featName);
if (pVal.get().getClass().getName().equals("java.lang.String")) {
System.out.println(" the feature is a String ");
rangeName = "uima.cas.String";
description = " featue of the casDataType";
}
newType.addFeature(featName, description, rangeName);
}
}
typesArr.add(newType);
}
TypeDescription td[] = new TypeDescription[typesArr.size()];
for (int j = 0; j < typesArr.size(); j++) {
td[j] = typesArr.get(j);
}
result.setTypes(td);
return result;
}
/**
* Merges several TypeSystemDescriptions into one. Also resolves imports in the
* TypeSystemDescription objects.
*
* @param aTypeSystems
* a collection of TypeSystems to be merged
*
* @return a new TypeSystemDescription that is the result of merging all of the type systems
* together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeTypeSystems(Collection<? extends TypeSystemDescription> aTypeSystems)
throws ResourceInitializationException {
return mergeTypeSystems(aTypeSystems, UIMAFramework.newDefaultResourceManager());
}
/**
* Merges several TypeSystemDescriptions into one. Also resolves imports in the
* TypeSystemDescription objects.
*
* @param aTypeSystems
* a collection of TypeSystems to be merged
* @param aResourceManager
* Resource Manager to use to locate type systems imported by name
*
* @return a new TypeSystemDescription that is the result of merging all of the type systems
* together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeTypeSystems(Collection<? extends TypeSystemDescription> aTypeSystems,
ResourceManager aResourceManager) throws ResourceInitializationException {
return mergeTypeSystems(aTypeSystems, aResourceManager, null);
}
/**
* Merges several TypeSystemDescriptions into one. Also resolves imports in the
* TypeSystemDescription objects.
* <p>
* This version of this method takes an argument <code>aOutputMergedTypes</code>, which this
* method will populate with the names and descriptor locations of any types whose definitions
* have been merged from multiple non-identical sources. That is, types that are declared more
* than once, with different (but compatible) sets of features in each declaration, or with
* different (but compatible) supertypes.
*
* @param aTypeSystems
* a collection of TypeSystems to be merged
* @param aResourceManager
* Resource Manager to use to locate type systems imported by name
* @param aOutputMergedTypes
* A Map that this method will populate with information about the set of types
* whose definitions were merged from multiple non-identical sources. The keys in
* the Map will be the type names (Strings) and the values will be {link Set}s
* containing Descriptor URLs (Strings) where those types are declared. You may
* pass null if you are not interested in this information.
*
* @return a new TypeSystemDescription that is the result of merging all of the type systems
* together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeTypeSystems(Collection<? extends TypeSystemDescription> aTypeSystems,
ResourceManager aResourceManager, Map<String, Set<String>> aOutputMergedTypes)
throws ResourceInitializationException {
// also build a Map from Type names to Types. Use a TreeMap so we get a consistent ordering of types.
Map<String, TypeDescription> typeNameMap = new TreeMap<String,TypeDescription>();
// Iterate through all type systems and add types to the merged TypeSystem.
// If a type is defined more than once, we need to check if the superType
// declarations are compatible (one inherits from another), and merge the
// features.
// In order to properly handle the supertype merging, we need to make sure
// that we process the supertype definitions before the subtypes. To do this,
// we build a linked list of type descriptions, and make multiple passes
// over this, adding types to the merged type system when their supertypes
// become defined. We continue until the list is empty or we cannot make any
// progress.
LinkedList<TypeDescription> typeList = new LinkedList<TypeDescription>();
Iterator<? extends TypeSystemDescription> it = aTypeSystems.iterator();
while (it.hasNext()) {
TypeSystemDescription ts = it.next();
if (ts != null) {
try {
ts.resolveImports(aResourceManager);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
TypeDescription[] types = ts.getTypes();
typeList.addAll(Arrays.asList(types));
}
}
int lastNumTypes;
do {
lastNumTypes = typeList.size();
Iterator<TypeDescription> typeIter = typeList.iterator();
while (typeIter.hasNext()) {
TypeDescription type = typeIter.next();
String supertypeName = type.getSupertypeName();
if (supertypeName.startsWith("uima.cas") || supertypeName.startsWith("uima.tcas") || typeNameMap.containsKey(supertypeName)) {
//supertype is defined, ok to proceed
//check if type is already defined
addTypeToMergedTypeSystem(aOutputMergedTypes, typeNameMap, type);
typeIter.remove();
}
}
} while (typeList.size() > 0 && typeList.size() != lastNumTypes);
//At this point, if the typeList is not empty, then we either have a type with an undefined supertype, or a cycle.
//We go ahead and merge the type definitions anyway - these problems will be caught at CAS creation time. Undefined supertypes
//may be OK at this stage - this type system will have to be further merged before it can be used.
Iterator<TypeDescription> typeIter = typeList.iterator();
while (typeIter.hasNext()) {
TypeDescription type = typeIter.next();
addTypeToMergedTypeSystem(aOutputMergedTypes, typeNameMap, type);
}
// create the type system and populate from the typeNamesMap
TypeSystemDescription result = UIMAFramework.getResourceSpecifierFactory()
.createTypeSystemDescription();
TypeDescription[] types = new TypeDescription[typeNameMap.values().size()];
typeNameMap.values().toArray(types);
result.setTypes(types);
return result;
}
private static void addTypeToMergedTypeSystem(Map<String, Set<String>> aOutputMergedTypes, Map<String,TypeDescription> typeNameMap, TypeDescription type) throws ResourceInitializationException {
String typeName = type.getName();
String supertypeName = type.getSupertypeName();
TypeDescription existingType = typeNameMap.get(typeName);
if (existingType == null) {
// create new type
existingType = UIMAFramework.getResourceSpecifierFactory().createTypeDescription();
existingType.setName(typeName);
existingType.setDescription(type.getDescription());
existingType.setSupertypeName(supertypeName);
existingType.setAllowedValues(type.getAllowedValues());
existingType.setSourceUrl(type.getSourceUrl());
typeNameMap.put(type.getName(), existingType);
FeatureDescription[] features = type.getFeatures();
if (features != null) {
mergeFeatures(existingType, type.getFeatures());
}
} else {
// type already existed - check that supertypes are compatible
String existingSupertypeName = existingType.getSupertypeName();
if (!existingSupertypeName.equals(supertypeName)) {
// supertypes are not identical - check if one subsumes the other
if (subsumes(existingSupertypeName, supertypeName, typeNameMap)) {
// existing supertype subsumes newly specified supertype -
// reset supertype to the new, more specific type
existingType.setSupertypeName(supertypeName);
// report that a merge occurred
reportMerge(aOutputMergedTypes, type, existingType);
} else if (subsumes(supertypeName, existingSupertypeName, typeNameMap)) {
// newly specified supertype subsumes old type, this is OK and we don't
// need to do anything except report this
reportMerge(aOutputMergedTypes, type, existingType);
} else {
// error
throw new ResourceInitializationException(
ResourceInitializationException.INCOMPATIBLE_SUPERTYPES, new Object[] {
typeName, supertypeName, existingSupertypeName,
type.getSourceUrlString() });
}
}
// merge features or check string allowed values are the same
if (supertypeName.equals("uima.cas.String")) {
AllowedValue[] av1 = getAllowedValues(type);
AllowedValue[] av2 = getAllowedValues(existingType);
if (!isAllowedValuesMatch(av1, av2)) {
throw new ResourceInitializationException(
ResourceInitializationException.ALLOWED_VALUES_NOT_IDENTICAL, new Object[] {
typeName, avAsString(av1), avAsString(av2),
type.getSourceUrlString() });
}
} else {
int prevNumFeatures = existingType.getFeatures().length;
FeatureDescription[] features = type.getFeatures();
if (features != null) {
mergeFeatures(existingType, type.getFeatures());
// if feature-merged occurred, the number of features on the type will have
// changed. Report this by adding to the aOutputMergedTypeNames collection.
if (existingType.getFeatures().length != prevNumFeatures) {
reportMerge(aOutputMergedTypes, type, existingType);
}
}
}
}
}
private static boolean isAllowedValuesMatch(AllowedValue[] av1, AllowedValue[] av2) {
if (av1.length != av2.length) {
return false;
}
Set<String> s1 = new HashSet<String>(av1.length);
Set<String> s2 = new HashSet<String>(av1.length);
for (AllowedValue av : av1) {
s1.add(av.getString());
}
for (AllowedValue av : av2) {
s2.add(av.getString());
}
return s1.equals(s2);
}
private static String avAsString(AllowedValue[] av) {
StringBuilder sb = new StringBuilder("{");
for (int i = 0; i < av.length; i++) {
sb.append(av[i].getString());
if (i < av.length - 1) {
sb.append(", ");
}
}
sb.append('}');
return sb.toString();
}
private static AllowedValue[] getAllowedValues(TypeDescription type) {
AllowedValue[] r = type.getAllowedValues();
if (r == null) {
return EMPTY_ALLOWED_VALUE_ARRAY;
}
return r;
}
/**
* Utility method for populating the aOutputMergedTypes argument in the mergeTypeSystems method.
*
* @param aOutputMergedTypes
* Map to populate
* @param currentType
* TypeDescription currently being processed
* @param existingType
* TypeDescription that already existed for the same name
*/
private static void reportMerge(Map<String, Set<String>> aOutputMergedTypes, TypeDescription currentType,
TypeDescription existingType) {
if (aOutputMergedTypes != null) {
String typeName = currentType.getName();
Set<String> descriptorUrls = aOutputMergedTypes.get(typeName);
if (descriptorUrls == null) {
descriptorUrls = new TreeSet<String>();
descriptorUrls.add(existingType.getSourceUrlString());
descriptorUrls.add(currentType.getSourceUrlString());
aOutputMergedTypes.put(typeName, descriptorUrls);
} else {
descriptorUrls.add(currentType.getSourceUrlString());
}
}
}
/**
* Merges the Type Systems of each component within an aggregate Analysis Engine, producing a
* single combined Type System.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
*
* @return a new TypeSystemDescription that is the result of merging all of the delegate AE type
* systems together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeDelegateAnalysisEngineTypeSystems(
AnalysisEngineDescription aAggregateDescription) throws ResourceInitializationException {
return mergeDelegateAnalysisEngineTypeSystems(aAggregateDescription, UIMAFramework
.newDefaultResourceManager());
}
/**
* Merges the Type Systems of each component within an aggregate Analysis Engine, producing a
* single combined Type System.
* <p>
* This version of this method takes an argument <code>aOutputMergedTypeNames</code>, to which
* this method will add the names of any types whose definitions have been merged from multiple
* non-identical sources. That is, types that are declared more than once, with different (but
* compatible) sets of features in each declaration, or with different (but compatible)
* supertypes.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
* @param aResourceManager
* ResourceManager instance used to resolve imports
*
* @return a new TypeSystemDescription that is the result of merging all of the delegate AE type
* systems together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeDelegateAnalysisEngineTypeSystems(
AnalysisEngineDescription aAggregateDescription, ResourceManager aResourceManager)
throws ResourceInitializationException {
return mergeDelegateAnalysisEngineTypeSystems(aAggregateDescription, aResourceManager, null);
}
/**
* Merges the Type Systems of each component within an aggregate Analysis Engine, producing a
* single combined Type System.
* <p>
* This version of this method takes an argument <code>aOutputMergedTypes</code>, which this
* method will populate with the names and descriptor locations of any types whose definitions
* have been merged from multiple non-identical sources. That is, types that are declared more
* than once, with different (but compatible) sets of features in each declaration, or with
* different (but compatible) supertypes.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
* @param aResourceManager
* ResourceManager instance used to resolve imports
* @param aOutputMergedTypes
* A Map that this method will populate with information about the set of types
* whose definitions were merged from multiple non-identical sources. The keys in
* the Map will be the type names (Strings) and the values will be {link Set}s
* containing Descriptor URLs (Strings) where those types are declared. You may
* pass null if you are not interested in this information. *
* @return a new TypeSystemDescription that is the result of merging all of the delegate AE type
* systems together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static TypeSystemDescription mergeDelegateAnalysisEngineTypeSystems(
AnalysisEngineDescription aAggregateDescription, ResourceManager aResourceManager,
Map<String, Set<String>> aOutputMergedTypes) throws ResourceInitializationException {
// expand the aggregate AE description into the individual delegates
List<AnalysisEngineDescription> l = new ArrayList<AnalysisEngineDescription>();
l.add(aAggregateDescription);
List<ProcessingResourceMetaData> mdList = getMetaDataList(l, aResourceManager);
// extract type systems and merge
List<TypeSystemDescription> typeSystems = new ArrayList<TypeSystemDescription>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypeSystem() != null)
typeSystems.add(md.getTypeSystem());
}
return mergeTypeSystems(typeSystems, aResourceManager, aOutputMergedTypes);
}
/**
* Merges a List of FsIndexCollections into a single FsIndexCollection object.
*
* @param aFsIndexCollections
* list of FsIndexCollection objects
* @param aResourceManager
* ResourceManager instance to use to resolve imports
*
* @return a merged FsIndexCollection object
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static FsIndexCollection mergeFsIndexes(List<? extends FsIndexCollection> aFsIndexCollections,
ResourceManager aResourceManager) throws ResourceInitializationException {
Map<String, FsIndexDescription> aggIndexes = new HashMap<String, FsIndexDescription>();
Iterator<? extends FsIndexCollection> it = aFsIndexCollections.iterator();
while (it.hasNext()) {
FsIndexCollection indexColl = it.next();
if (indexColl != null) {
try {
indexColl.resolveImports(aResourceManager);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
FsIndexDescription[] indexes = indexColl.getFsIndexes();
for (int i = 0; i < indexes.length; i++) {
// does an index with this label already exist?
FsIndexDescription duplicateIndex = aggIndexes.get(indexes[i]
.getLabel());
if (duplicateIndex == null) {
// no, so add it
aggIndexes.put(indexes[i].getLabel(), indexes[i]);
} else if (!duplicateIndex.equals(indexes[i])) {
// index with same label exists, they better be equal!
throw new ResourceInitializationException(
ResourceInitializationException.DUPLICATE_INDEX_NAME, new Object[] {
duplicateIndex.getLabel(), duplicateIndex.getSourceUrlString(),
indexes[i].getSourceUrlString() });
}
}
}
}
// convert index map to FsIndexCollection
FsIndexCollection aggIndexColl = UIMAFramework.getResourceSpecifierFactory()
.createFsIndexCollection();
Collection<FsIndexDescription> indexes = aggIndexes.values();
FsIndexDescription[] indexArray = new FsIndexDescription[indexes.size()];
indexes.toArray(indexArray);
aggIndexColl.setFsIndexes(indexArray);
return aggIndexColl;
}
/**
* Merges the FS Index Collections of each component within an aggregate Analysis Engine,
* producing a single combined FS Index Collection.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
*
* @return a new FsIndexCollection that is the result of merging all of the delegate AE
* FsIndexCollections together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static FsIndexCollection mergeDelegateAnalysisEngineFsIndexCollections(
AnalysisEngineDescription aAggregateDescription) throws ResourceInitializationException {
return mergeDelegateAnalysisEngineFsIndexCollections(aAggregateDescription, UIMAFramework
.newDefaultResourceManager());
}
/**
* Merges the FS Index Collections of each component within an aggregate Analysis Engine,
* producing a single combined FS Index Collection.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
* @param aResourceManager
* ResourceManager instance used to resolve imports
*
* @return a new FsIndexCollection that is the result of merging all of the delegate AE
* FsIndexCollections together
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static FsIndexCollection mergeDelegateAnalysisEngineFsIndexCollections(
AnalysisEngineDescription aAggregateDescription, ResourceManager aResourceManager)
throws ResourceInitializationException {
// expand the aggregate AE description into the individual delegates
List<AnalysisEngineDescription> l = new ArrayList<AnalysisEngineDescription>();
l.add(aAggregateDescription);
List<ProcessingResourceMetaData> mdList = getMetaDataList(l, aResourceManager);
// extract FsIndexCollections and merge
List<FsIndexCollection> fsIndexes = new ArrayList<FsIndexCollection>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getFsIndexCollection() != null)
fsIndexes.add(md.getFsIndexCollection());
}
return mergeFsIndexes(fsIndexes, aResourceManager);
}
/**
* Merges a List of TypePriorities into a single TypePriorities object.
*
* @param aTypePriorities
* list of TypePriorities objects
* @param aResourceManager
* ResourceManager instance to use to resolve imports
*
* @return a merged TypePriorities object
* @throws ResourceInitializationException
* if an import could not be resolved
*/
public static TypePriorities mergeTypePriorities(List<? extends TypePriorities> aTypePriorities,
ResourceManager aResourceManager) throws ResourceInitializationException {
TypePriorities aggTypePriorities = UIMAFramework.getResourceSpecifierFactory()
.createTypePriorities();
Iterator<? extends TypePriorities> it = aTypePriorities.iterator();
while (it.hasNext()) {
TypePriorities tp = it.next();
try {
tp.resolveImports(aResourceManager);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
TypePriorityList[] pls = tp.getPriorityLists();
if (pls != null) {
for (int i = 0; i < pls.length; i++) {
aggTypePriorities.addPriorityList(pls[i]);
}
}
}
return aggTypePriorities;
}
/**
* Merges the Type Priorities of each component within an aggregate Analysis Engine, producing a
* single combined TypePriorities object.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
*
* @return a new TypePriorities object that is the result of merging all of the delegate AE
* TypePriorities together
*
* @throws ResourceInitializationException
* if an incompatibility exists
*/
public static TypePriorities mergeDelegateAnalysisEngineTypePriorities(
AnalysisEngineDescription aAggregateDescription) throws ResourceInitializationException {
return mergeDelegateAnalysisEngineTypePriorities(aAggregateDescription, UIMAFramework
.newDefaultResourceManager());
}
/**
* Merges the Type Priorities of each component within an aggregate Analysis Engine, producing a
* single combined TypePriorities object.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
* @param aResourceManager
* ResourceManager instance used to resolve imports
*
* @return a new TypePriorities object that is the result of merging all of the delegate AE
* TypePriorities together
*
* @throws ResourceInitializationException
* if an incompatibility exists
*/
public static TypePriorities mergeDelegateAnalysisEngineTypePriorities(
AnalysisEngineDescription aAggregateDescription, ResourceManager aResourceManager)
throws ResourceInitializationException {
// expand the aggregate AE description into the individual delegates
ArrayList<AnalysisEngineDescription> l = new ArrayList<AnalysisEngineDescription>();
l.add(aAggregateDescription);
List<ProcessingResourceMetaData> mdList = getMetaDataList(l, aResourceManager);
// extract TypePriorities and merge
List<TypePriorities> typePriorities = new ArrayList<TypePriorities>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypePriorities() != null)
typePriorities.add(md.getTypePriorities());
}
return mergeTypePriorities(typePriorities, aResourceManager);
}
/**
* Merges the Type Systems, Type Priorities, and FS Indexes of each component within an aggregate
* Analysis Engine.
* <p>
* This version of this method takes an argument <code>aOutputMergedTypes</code>, which this
* method will populate with the names and descriptor locations of any types whose definitions
* have been merged from multiple non-identical sources. That is, types that are declared more
* than once, with different (but compatible) sets of features in each declaration, or with
* different (but compatible) supertypes.
*
* @param aAggregateDescription
* an aggregate Analysis Engine description
* @param aResourceManager
* ResourceManager instance used to resolve imports
* @param aOutputMergedTypes
* A Map that this method will populate with information about the set of types
* whose definitions were merged from multiple non-identical sources. That is,
* types that are declared more than once, with different (but compatible) sets of
* features in each declaration, or with different (but compatible) supertypes. The
* keys in the Map will be the type names (Strings) and the values will be {link
* Set}s containing Descriptor URLs (Strings) where those types are declared. You
* may pass null if you are not interested in this information.
* @param aOutputFailedRemotes
* If this parameter is non-null, and if a remote AE could not be contacted, then an
* entry will be added to this map. The key will be the context name (e.g.,
* /myDelegate1/nestedRemoteDelegate) of the failed remote, and the value will be
* the Exception that occurred. If this parameter is null, an exception will be
* thrown if a remote AE could not be contacted.
*
* @return an object containing the merged TypeSystem, TypePriorities, and FS Index definitions.
*
* @throws ResourceInitializationException
* if an incompatibility exists or if an import could not be resolved
*/
public static ProcessingResourceMetaData mergeDelegateAnalysisEngineMetaData(
AnalysisEngineDescription aAggregateDescription, ResourceManager aResourceManager,
Map<String, Set<String>> aOutputMergedTypes, Map<String, ? super Exception> aOutputFailedRemotes) throws ResourceInitializationException {
// expand the aggregate AE description into the individual delegates
ArrayList<AnalysisEngineDescription> l = new ArrayList<AnalysisEngineDescription>();
l.add(aAggregateDescription);
List<ProcessingResourceMetaData> mdList = getMetaDataList(l, aResourceManager, aOutputFailedRemotes);
ProcessingResourceMetaData result = UIMAFramework.getResourceSpecifierFactory()
.createProcessingResourceMetaData();
// extract type systems and merge
List<TypeSystemDescription> typeSystems = new ArrayList<TypeSystemDescription>();
Iterator<ProcessingResourceMetaData> it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypeSystem() != null)
typeSystems.add(md.getTypeSystem());
}
result.setTypeSystem(mergeTypeSystems(typeSystems, aResourceManager, aOutputMergedTypes));
// extract TypePriorities and merge
List<TypePriorities> typePriorities = new ArrayList<TypePriorities>();
it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getTypePriorities() != null)
typePriorities.add(md.getTypePriorities());
}
result.setTypePriorities(mergeTypePriorities(typePriorities, aResourceManager));
// extract FsIndexCollections and merge
List<FsIndexCollection> fsIndexes = new ArrayList<FsIndexCollection>();
it = mdList.iterator();
while (it.hasNext()) {
ProcessingResourceMetaData md = it.next();
if (md.getFsIndexCollection() != null)
fsIndexes.add(md.getFsIndexCollection());
}
result.setFsIndexCollection(mergeFsIndexes(fsIndexes, aResourceManager));
return result;
}
/**
* Determines whether one type subsumes another.
*
* @param aType1Name
* name of first type
* @param aType2Name
* name of second type
* @param aNameMap
* Map from type names to TypeDescriptions
*
* @return true if and only if the type named <code>aType1Name</code> subsumes the type named
* <code>aType2Name</code> according to the information given in the
* <code>aNameMap</code>.
*/
protected static boolean subsumes(String aType1Name, String aType2Name, Map<String, ? extends TypeDescription> aNameMap) {
// Top type subsumes everything
if (CAS.TYPE_NAME_TOP.equals(aType1Name)) {
return true;
}
// "walk up the tree" from aType2Name until we reach aType1Name or null
String current = aType2Name;
while (current != null && !current.equals(aType1Name)) {
TypeDescription curType = aNameMap.get(current);
if (curType == null)
current = null;
else
current = curType.getSupertypeName();
}
return (current != null);
}
/**
* Merges features into a TypeDescription.
*
* @param aType
* TypeDescription into which to merge the features
* @param aFeatures
* array of features to merge
*
* @throws ResourceInitializationException
* if an incompatibility exists
*/
protected static void mergeFeatures(TypeDescription aType, FeatureDescription[] aFeatures)
throws ResourceInitializationException {
FeatureDescription[] existingFeatures = aType.getFeatures();
if (existingFeatures == null) {
existingFeatures = EMPTY_FEAT_DESC_ARRAY;
}
for (int i = 0; i < aFeatures.length; i++) {
String featName = aFeatures[i].getName();
String rangeTypeName = aFeatures[i].getRangeTypeName();
String elementTypeName = aFeatures[i].getElementType();
Boolean multiRefsAllowed = aFeatures[i].getMultipleReferencesAllowed();
// see if a feature already exists with this name
FeatureDescription feat = null;
for (int j = 0; j < existingFeatures.length; j++) {
if (existingFeatures[j].getName().equals(featName)) {
feat = existingFeatures[j];
break;
}
}
if (feat == null) {
// doesn't exist; add it
FeatureDescription featDesc = aType.addFeature(featName, aFeatures[i].getDescription(),
rangeTypeName, elementTypeName, multiRefsAllowed);
featDesc.setSourceUrl(aFeatures[i].getSourceUrl());
} else {// feature does exist
// check that the range types match
if (!feat.getRangeTypeName().equals(rangeTypeName)) {
throw new ResourceInitializationException(
ResourceInitializationException.INCOMPATIBLE_RANGE_TYPES, new Object[] {
aType.getName() + ":" + feat.getName(), rangeTypeName, feat.getRangeTypeName(),
aType.getSourceUrlString() });
}
Boolean mra1 = feat.getMultipleReferencesAllowed();
Boolean mra2 = multiRefsAllowed;
// the logic here:
// OK if both null
// OK if both not-null, and are equals()
// OK if one is null, the other has boolean-value of false (false is the default)
// not ok otherwise
if (!(((mra1 == null) && (mra2 == null)) || ((mra1 != null) && mra1.equals(mra2))
|| ((mra1 == null) && !mra2.booleanValue()) || ((mra2 == null) && !mra1.booleanValue()))) {
throw new ResourceInitializationException(
ResourceInitializationException.INCOMPATIBLE_MULTI_REFS, new Object[] {
aType.getName() + ":" + feat.getName(), aType.getSourceUrlString() });
}
if (!elementTypesCompatible(feat.getElementType(), elementTypeName)) {
throw new ResourceInitializationException(
ResourceInitializationException.INCOMPATIBLE_ELEMENT_RANGE_TYPES, new Object[] {
aType.getName() + TypeSystem.FEATURE_SEPARATOR + feat.getName(), elementTypeName,
feat.getElementType(), aType.getSourceUrlString() });
}
}
}
}
/**
* Compare element type names for array-like features
* @param o1 name of first element type
* @param o2 name of second element type
* @return true if elements are compatible for merging features
*/
private static boolean elementTypesCompatible(String o1, String o2) {
return ((null == o1) && (null == o2)) || ((null != o1) && o1.equals(o2)) ||
// allow missing types to be equal to TOP
(o1 != null && o1.equals(CAS.TYPE_NAME_TOP) && o2 == null) ||
(o2 != null && o2.equals(CAS.TYPE_NAME_TOP) && o1 == null)
;
}
/*************************************************************************************************
* 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) {
if (null == obj) {
return false;
}
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 this.getClass().getSimpleName() + " [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.cancel(); // probably not needed, but for safety ...
cleanupTimer = null;
}
if (cacheDebug) {
System.err.format("GetMetaDataCache: cleanup task finished a cycle%n");
}
}
}
};
cleanupTimer.schedule(metaDataCacheCleanupTask, HOLD_TIME, HOLD_TIME);
}
}
}
/**
* Gets a list of ProcessingResourceMetadata objects from a list containing either
* ResourceSpecifiers, ProcessingResourceMetadata objects, or subparts of
* ProcessingResourceMetadata objects (type systems, indexes, or type priorities). Subparts will
* be wrapped inside a ProcessingResourceMetadata object. All objects will be cloned, so that
* further processing (such as import resolution) does not affect the caller.
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* 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
* {@link TypePriorities} objects.
* @param aResourceManager
* used to resolve delegate analysis engine imports
* @param aOutputFailedRemotes
* If this parameter is non-null, and if a remote AE could not be contacted, then
* the context name (e.g. /myDelegate1/nestedRemoteDelegate) of the failed remote
* will be added to this collection. If this parameter is null, an exception will
* be thrown if a remote AE could not be contacted.
*
* @return a List containing the ProcessingResourceMetaData objects containing all of the
* information in all of the objects in <code>aComponentDescriptionOrMetaData</code>
* (including all components of aggregate AnalysisEngines)
*
* @throws ResourceInitializationException
* if a failure occurs because an import could not be resolved
*/
public static List<ProcessingResourceMetaData> getMetaDataList(Collection<? extends MetaDataObject> aComponentDescriptionOrMetaData,
ResourceManager aResourceManager, Map<String, ? super Exception> aOutputFailedRemotes)
throws ResourceInitializationException {
return getMetaDataList(aComponentDescriptionOrMetaData, aResourceManager, aOutputFailedRemotes,
"");
}
private static List<ProcessingResourceMetaData> getMetaDataList(Collection<? extends MetaDataObject> aComponentDescriptionOrMetaData,
ResourceManager aResourceManager, Map<String, ? super Exception> aOutputFailedRemotes, String aContextName)
throws ResourceInitializationException {
List<ProcessingResourceMetaData> mdList = new ArrayList<ProcessingResourceMetaData>();
if (null == aComponentDescriptionOrMetaData) {
return mdList;
}
Iterator<? extends MetaDataObject> iter = aComponentDescriptionOrMetaData.iterator();
while (iter.hasNext()) {
Object current = iter.next();
if (current instanceof ProcessingResourceMetaData) {
mdList.add((ProcessingResourceMetaData) ((ProcessingResourceMetaData) current).clone());
} else if (current instanceof AnalysisEngineDescription) {
AnalysisEngineDescription aeDesc = (AnalysisEngineDescription) current;
mdList.add((ProcessingResourceMetaData) aeDesc.getAnalysisEngineMetaData().clone());
// expand aggregate
if (!aeDesc.isPrimitive()) {
Map<String, ResourceSpecifier> delegateMap;
try {
delegateMap = aeDesc.getAllComponentSpecifiers(aResourceManager);
} catch (InvalidXMLException e) {
throw new ResourceInitializationException(e);
}
Iterator<Map.Entry<String, ResourceSpecifier>> delIter = delegateMap.entrySet().iterator();
while (delIter.hasNext()) {
Map.Entry<String, ResourceSpecifier> delEntry = delIter.next();
List<ResourceSpecifier> tempList = new ArrayList<ResourceSpecifier>();
tempList.add(delEntry.getValue());
mdList.addAll(getMetaDataList(tempList, aResourceManager, aOutputFailedRemotes,
aContextName + "/" + delEntry.getKey()));
}
}
} else if (current instanceof CollectionReaderDescription) {
mdList.add((ProcessingResourceMetaData) ((CollectionReaderDescription) current).getCollectionReaderMetaData().clone());
} else if (current instanceof CasInitializerDescription) {
mdList.add((ProcessingResourceMetaData) ((CasInitializerDescription) current).getCasInitializerMetaData().clone());
} else if (current instanceof CasConsumerDescription) {
mdList.add((ProcessingResourceMetaData) ((CasConsumerDescription) current).getCasConsumerMetaData().clone());
} else if (current instanceof FlowControllerDescription) {
mdList.add((ProcessingResourceMetaData) ((FlowControllerDescription) current).getFlowControllerMetaData().clone());
} else if (current instanceof TypeSystemDescription) {
ProcessingResourceMetaData md = new ProcessingResourceMetaData_impl();
md.setTypeSystem((TypeSystemDescription) current);
mdList.add(md);
} else if (current instanceof FsIndexCollection) {
ProcessingResourceMetaData md = new ProcessingResourceMetaData_impl();
md.setFsIndexCollection((FsIndexCollection) current);
mdList.add(md);
} else if (current instanceof TypePriorities) {
ProcessingResourceMetaData md = new ProcessingResourceMetaData_impl();
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) {
prParams.put(Resource.PARAM_RESOURCE_MANAGER, aResourceManager);
}
prParams.put(AnalysisEngineImplBase.PARAM_VERIFICATION_MODE, Boolean.TRUE);
try {
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) {
aOutputFailedRemotes.put(aContextName, e);
} else {
if (e instanceof ResourceInitializationException)
throw (ResourceInitializationException) e;
else if (e instanceof RuntimeException)
throw (RuntimeException) e;
else
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) {
if (metadata instanceof ProcessingResourceMetaData) {
mdList.add((ProcessingResourceMetaData) metadata);
}
resource.destroy();
}
} else {
throw new ResourceInitializationException(
ResourceInitializationException.UNSUPPORTED_OBJECT_TYPE_IN_CREATE_CAS,
new Object[] { current.getClass().getName() });
}
}
return mdList;
}
/**
* Gets a list of ProcessingResourceMetadata objects from a list containing either
* ResourceSpecifiers, ProcessingResourceMetadata objects, or subparts of
* ProcessingResourceMetadata objects (type systems, indexes, or type priorities). Subparts will
* be wrapped inside a ProcessingResourceMetadata object. All objects will be cloned, so that
* further processing (such as import resolution) does not affect the caller.
* <p>
* If you pass this method objects of type {@link AnalysisEngineDescription},
* {@link CollectionReaderDescription}, {@link CasInitializerDescription}, or
* {@link CasConsumerDescription}, it will not instantiate the components. It will just extract
* the type system information from the descriptor. For any other kind of
* {@link ResourceSpecifier}, it will call
* {@link UIMAFramework#produceResource(org.apache.uima.resource.ResourceSpecifier, Map)}. For
* example, if a {@link URISpecifier} is passed, a remote connection will be established and the
* service will be queries for its metadata. An exception will be thrown if the connection can not
* be opened.
*
* @param aComponentDescriptionOrMetaData
* a collection of {@link ResourceSpecifier}, {@link ProcessingResourceMetaData},
* {@link TypeSystemDescription}, {@link FsIndexCollection}, or
* {@link TypePriorities} objects.
* @param aResourceManager
* used to resolve delegate analysis engine imports
*
* @return a List containing the ProcessingResourceMetaData objects containing all of the
* information in all of the objects in <code>aComponentDescriptionOrMetaData</code>
* (including all components of aggregate AnalysisEngines)
*
* @throws ResourceInitializationException
* if a failure occurs because an import could not be resolved
*/
public static List<ProcessingResourceMetaData> getMetaDataList(Collection<? extends MetaDataObject> aComponentDescriptionOrMetaData,
ResourceManager aResourceManager) throws ResourceInitializationException {
return getMetaDataList(aComponentDescriptionOrMetaData, aResourceManager, null);
}
}