blob: a276750534564ac2d3d8c46132836df30f409b69 [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.analysis_engine.impl;
import java.util.Collections;
import java.util.Map;
import org.apache.uima.Constants;
import org.apache.uima.UIMAFramework;
import org.apache.uima.UIMA_IllegalStateException;
import org.apache.uima.UimaContextAdmin;
import org.apache.uima.analysis_component.AnalysisComponent;
import org.apache.uima.analysis_engine.AnalysisEngine;
import org.apache.uima.analysis_engine.AnalysisEngineDescription;
import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
import org.apache.uima.analysis_engine.CasIterator;
import org.apache.uima.analysis_engine.ResultNotSupportedException;
import org.apache.uima.analysis_engine.ResultSpecification;
import org.apache.uima.analysis_engine.TypeOrFeature;
import org.apache.uima.analysis_engine.impl.compatibility.AnalysisComponentAdapterFactory;
import org.apache.uima.cas.AbstractCas;
import org.apache.uima.cas.CAS;
import org.apache.uima.cas.TypeSystem;
import org.apache.uima.cas.impl.CASImpl;
import org.apache.uima.impl.UimaContext_ImplBase;
import org.apache.uima.internal.util.UUIDGenerator;
import org.apache.uima.jcas.JCas;
import org.apache.uima.resource.ResourceConfigurationException;
import org.apache.uima.resource.ResourceCreationSpecifier;
import org.apache.uima.resource.ResourceInitializationException;
import org.apache.uima.resource.ResourceSpecifier;
import org.apache.uima.resource.metadata.Capability;
import org.apache.uima.resource.metadata.ProcessingResourceMetaData;
import org.apache.uima.resource.metadata.ResourceMetaData;
import org.apache.uima.util.Level;
import org.apache.uima.util.Logger;
/**
* Reference implementation of {@link AnalysisEngine}.
*
*
*/
public class PrimitiveAnalysisEngine_impl extends AnalysisEngineImplBase implements AnalysisEngine {
/**
* current class
*/
private static final Class CLASS_NAME = PrimitiveAnalysisEngine_impl.class;
private ResultSpecification mCurrentResultSpecification;
private boolean mResultSpecChanged;
private TypeSystem mLastTypeSystem;
/**
* The ResourceCreationSpecifier parsed from this component's descriptor.
*/
private ResourceCreationSpecifier mDescription;
/**
* The AnalysisComponent that holds the user-developed analysis logic.
*/
private AnalysisComponent mAnalysisComponent;
/**
* If this is set it indicates that the AnalysisEngine is being constructed only to verify the
* validity of the descriptor. The Annotator classes should not be instantiated in this case.
*/
private boolean mVerificationMode = false;
private boolean mSofaAware;
/**
* @see org.apache.uima.resource.Resource#initialize(ResourceSpecifier, Map)
*/
public boolean initialize(ResourceSpecifier aSpecifier, Map aAdditionalParams)
throws ResourceInitializationException {
try {
// Primitive AnalysisEngine can be build from any ResourceCreationSpecifier-
// this includes CollectionReader, and CasConsumer descriptors
// as well as AnalysisEngine descriptors.
if (!(aSpecifier instanceof ResourceCreationSpecifier)) {
return false;
}
// BUT, for AnalysisEngineDescriptions, must not be an aggregate
if (aSpecifier instanceof AnalysisEngineDescription
&& !((AnalysisEngineDescription) aSpecifier).isPrimitive()) {
return false;
}
mDescription = (ResourceCreationSpecifier) aSpecifier;
// also framework implementation must start with org.apache.uima.java
final String fwImpl = mDescription.getFrameworkImplementation();
if (!fwImpl.startsWith(Constants.JAVA_FRAMEWORK_NAME)) {
return false;
}
super.initialize(aSpecifier, aAdditionalParams);
ProcessingResourceMetaData md = (ProcessingResourceMetaData) mDescription.getMetaData();
getLogger().logrb(Level.CONFIG, CLASS_NAME.getName(), "initialize", LOG_RESOURCE_BUNDLE,
"UIMA_analysis_engine_init_begin__CONFIG", md.getName());
// Normalize language codes. Need to do this since a wide variety of
// spellings are acceptable according to ISO.
normalizeIsoLangCodes(md);
// clone this metadata and assign a UUID if not already present
ResourceMetaData mdCopy = (ResourceMetaData) md.clone();
if (mdCopy.getUUID() == null) {
mdCopy.setUUID(UUIDGenerator.generate());
}
setMetaData(mdCopy);
// validate the AnalysisEngineDescription and throw a
// ResourceInitializationException if there is a problem
mDescription.validate(getResourceManager());
// Read parameters from the aAdditionalParams map.
if (aAdditionalParams == null) {
aAdditionalParams = Collections.EMPTY_MAP;
}
// determine if verification mode is on
mVerificationMode = aAdditionalParams.containsKey(PARAM_VERIFICATION_MODE);
// determine if this component is Sofa-aware (based on whether it
// declares any input or output sofas in its capabilities)
mSofaAware = getAnalysisEngineMetaData().isSofaAware();
initializeAnalysisComponent(aAdditionalParams);
// Initialize ResultSpec based on output capabilities
// TODO: should only do this for outermost AE
resetResultSpecificationToDefault();
getLogger().logrb(Level.CONFIG, CLASS_NAME.getName(), "initialize", LOG_RESOURCE_BUNDLE,
"UIMA_analysis_engine_init_successful__CONFIG", md.getName());
return true;
} catch (ResourceConfigurationException e) {
throw new ResourceInitializationException(
ResourceInitializationException.ERROR_INITIALIZING_FROM_DESCRIPTOR, new Object[] {
getMetaData().getName(), aSpecifier.getSourceUrlString() });
}
}
/**
* Loads, instantiates, and initializes the AnalysisComponent contained in this AE.
*
* @param aAdditionalParams
* parameters passed to this AE's initialize method
*
* @throws ResourceInitializationException
* if an initialization failure occurs
*/
protected void initializeAnalysisComponent(Map aAdditionalParams)
throws ResourceInitializationException {
// instantiate Annotator class
String annotatorClassName;
annotatorClassName = mDescription.getImplementationName();
if (annotatorClassName == null || annotatorClassName.length() == 0) {
throw new ResourceInitializationException(
ResourceInitializationException.MISSING_ANNOTATOR_CLASS_NAME,
new Object[] { mDescription.getSourceUrlString() });
}
// load annotator class
Class annotatorClass = null;
try {
// get UIMA extension ClassLoader if available
ClassLoader cl = getUimaContextAdmin().getResourceManager().getExtensionClassLoader();
if (cl != null) {
// use UIMA extension ClassLoader to load the class
annotatorClass = cl.loadClass(annotatorClassName);
} else {
// use application ClassLoader to load the class
annotatorClass = Class.forName(annotatorClassName);
}
} catch (ClassNotFoundException e) {
throw new ResourceInitializationException(
ResourceInitializationException.ANNOTATOR_CLASS_NOT_FOUND, new Object[] {
annotatorClassName, mDescription.getSourceUrlString() }, e);
}
// Make sure the specified class can be adapter to an AnalysisComponent.
if (!(AnalysisComponent.class.isAssignableFrom(annotatorClass))
&& !AnalysisComponentAdapterFactory.isAdaptable(annotatorClass)) {
throw new ResourceInitializationException(
ResourceInitializationException.NOT_AN_ANALYSIS_COMPONENT, new Object[] {
annotatorClass.getName(), mDescription.getSourceUrlString() });
}
// if we're in verification mode, stop here and do not try to instantiate the
// analysis component
if (mVerificationMode) {
return;
}
try {
Object userObject = annotatorClass.newInstance();
if (userObject instanceof AnalysisComponent) {
mAnalysisComponent = (AnalysisComponent) userObject;
} else {
mAnalysisComponent = AnalysisComponentAdapterFactory.createAdapter(userObject,
getAnalysisEngineMetaData(), aAdditionalParams);
}
} catch (ResourceInitializationException e) {
throw e;
} catch (Exception e) {
throw new ResourceInitializationException(
ResourceInitializationException.COULD_NOT_INSTANTIATE_ANNOTATOR, new Object[] {
annotatorClassName, mDescription.getSourceUrlString() }, e);
}
// Set Logger, to enable annotator-specific logging
UimaContextAdmin uimaContext = getUimaContextAdmin();
Logger logger = UIMAFramework.getLogger(annotatorClass);
logger.setResourceManager(this.getResourceManager());
uimaContext.setLogger(logger);
// initialize AnalysisComponent
try {
mAnalysisComponent.initialize(getUimaContext());
} catch (Exception e) {
throw new ResourceInitializationException(
ResourceInitializationException.ANNOTATOR_INITIALIZATION_FAILED, new Object[] {
annotatorClassName, mDescription.getSourceUrlString() }, e);
}
// set up the CAS pool for this AE (this won't do anything if
// mAnalysisComponent.getCasInstancesRequired() == 0)
getUimaContextAdmin().defineCasPool(mAnalysisComponent.getCasInstancesRequired(),
getPerformanceTuningSettings(), mSofaAware);
}
/**
* @see org.apache.uima.resource.Resource#destroy()
*/
public void destroy() {
if (mAnalysisComponent != null) {
mAnalysisComponent.destroy();
getLogger().logrb(Level.CONFIG, CLASS_NAME.getName(), "initialize", LOG_RESOURCE_BUNDLE,
"UIMA_analysis_engine_destroyed__CONFIG", getMetaData().getName());
}
super.destroy();
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.AnalysisEngine#setResultSpecification(org.apache.uima.analysis_engine.ResultSpecification)
*/
public void setResultSpecification(ResultSpecification aResultSpec) {
if (aResultSpec == null) {
resetResultSpecificationToDefault();
} else {
mCurrentResultSpecification = aResultSpec;
mResultSpecChanged = true;
}
}
/**
* @see AnalysisEngine#processAndOutputNewCASes(CAS)
*/
public CasIterator processAndOutputNewCASes(CAS aCAS) throws AnalysisEngineProcessException {
enterProcess();
try {
// make initial call to the AnalysisComponent
callAnalysisComponentProcess(aCAS);
// return a CasIterator that allows caller to step through the outputs
// of this AnalysisComponent (if any)
return new AnalysisComponentCasIterator(mAnalysisComponent, aCAS);
} finally {
exitProcess();
}
}
public void batchProcessComplete() throws AnalysisEngineProcessException {
enterBatchProcessComplete();
try {
getAnalysisComponent().batchProcessComplete();
} finally {
exitBatchProcessComplete();
}
}
public void collectionProcessComplete() throws AnalysisEngineProcessException {
enterCollectionProcessComplete();
try {
getAnalysisComponent().collectionProcessComplete();
} finally {
exitCollectionProcessComplete();
}
}
/**
* Calls the Analysis Component's process method.
*
* @param aCAS
* CAS to be processed by annotator
* @param aResultSpec
* result specification to be passed to annotator
* @param aProcessTrace
* keeps track of time spent in each component
*/
protected void callAnalysisComponentProcess(CAS aCAS) throws AnalysisEngineProcessException {
// logging and instrumentation
String resourceName = getMetaData().getName();
getLogger().logrb(Level.FINE, CLASS_NAME.getName(), "process", LOG_RESOURCE_BUNDLE,
"UIMA_analysis_engine_process_begin__FINE", resourceName);
try {
// call Annotator's process method
try {
// set the current component info of the CAS, so that it knows the sofa
// mappings for the component that's about to process it
aCAS.setCurrentComponentInfo(getUimaContextAdmin().getComponentInfo());
// Get the right view of the CAS. Sofa-aware components get the base CAS.
// Sofa-unaware components get whatever is mapped to the _InitialView.
CAS view = ((CASImpl) aCAS).getBaseCAS();
if (!mSofaAware) {
view = aCAS.getView(CAS.NAME_DEFAULT_SOFA);
}
// now get the right interface(e.g. CAS or JCAS)
Class requiredInterface = mAnalysisComponent.getRequiredCasInterface();
AbstractCas casToPass = getCasManager().getCasInterface(view, requiredInterface);
// check if there was a change in the ResultSpecification or in
// the TypeSystem. If so, recompile the ResultSpecification and
// inform the component
if (mResultSpecChanged || mLastTypeSystem != view.getTypeSystem()) {
mLastTypeSystem = view.getTypeSystem();
mCurrentResultSpecification.compile(mLastTypeSystem);
// the actual ResultSpec we send to the component is formed by
// looking at this primitive AE's declared output types and eliminiating
// any that are not in mCurrentResultSpecification.
ResultSpecification analysisComponentResultSpec = computeAnalysisComponentResultSpec(
mCurrentResultSpecification, getAnalysisEngineMetaData().getCapabilities());
// compile result spec - necessary to get type subsumption to work properly
analysisComponentResultSpec.compile(mLastTypeSystem);
mAnalysisComponent.setResultSpecification(analysisComponentResultSpec);
mResultSpecChanged = false;
}
((CASImpl)aCAS).switchClassLoaderLockCasCL(this.getResourceManager().getExtensionClassLoader());
// call the process method
mAnalysisComponent.process(casToPass);
getMBean().incrementCASesProcessed();
//note we do not clear the CAS's currentComponentInfo at this time. The AnalysisComponents still
//can access the CAS until such time as its hasNext method returns false. Thus is is the
//AnalysisComponentCasIterator that knows when it is time to clear the currentComponentInfo.
} catch (Exception e) {
aCAS.setCurrentComponentInfo(null);
((CASImpl)aCAS).restoreClassLoaderUnlockCas();
if (e instanceof AnalysisEngineProcessException) {
throw e;
} else {
throw new AnalysisEngineProcessException(
AnalysisEngineProcessException.ANNOTATOR_EXCEPTION, null, e);
}
} finally {
}
// log end of event
getLogger().logrb(Level.FINE, CLASS_NAME.getName(), "process", LOG_RESOURCE_BUNDLE,
"UIMA_analysis_engine_process_end__FINE", resourceName);
} catch (Exception e) {
// log and rethrow exception
getLogger().log(Level.SEVERE, "", e);
if (e instanceof AnalysisEngineProcessException)
throw (AnalysisEngineProcessException) e;
else
throw new AnalysisEngineProcessException(e);
}
}
/**
* Creates the ResultSpecification to be passed to the AnalysisComponent. This is derived from the
* ResultSpec that is input to this AE (via its setResultSpecification method) by intersecting
* with the declared outputs of this AE, so that we never ask an AnalysisComponent to produce a
* result type that it does not declare in its outputs.
*
* @param currentResultSpecification
* the result spec passed to this AE's setResultSpecification method
* @param capabilities
* the capabilities of this AE
*
* @return a ResultSpecifciation to pass to the AnalysisComponent
*/
protected ResultSpecification computeAnalysisComponentResultSpec(
ResultSpecification inputResultSpec, Capability[] capabilities) {
ResultSpecification newResultSpec = new ResultSpecification_impl();
for (int i = 0; i < capabilities.length; i++) {
Capability cap = capabilities[i];
TypeOrFeature[] outputs = cap.getOutputs();
String[] languages = cap.getLanguagesSupported();
if (languages.length == 0) {
languages = new String[] { "x-unspecified" };
}
for (int j = 0; j < outputs.length; j++) {
for (int k = 0; k < languages.length; k++) {
if (outputs[j].isType()
&& inputResultSpec.containsType(outputs[j].getName(), languages[k])) {
newResultSpec.addResultType(outputs[j].getName(), outputs[j].isAllAnnotatorFeatures(),
new String[] { languages[k] });
} else if (!outputs[j].isType()
&& inputResultSpec.containsFeature(outputs[j].getName(), languages[k])) {
newResultSpec.addResultFeature(outputs[j].getName(), new String[] { languages[k] });
}
}
}
}
return newResultSpec;
}
/**
* Calls the Analysis Component's next() method.
*
* @return CAS returned by the analysis component
*/
protected CAS callAnalysisComponentNext() throws AnalysisEngineProcessException,
ResultNotSupportedException {
try {
AbstractCas absCas = mAnalysisComponent.next();
getMBean().incrementCASesProcessed();
// notify UimaContext that a CAS was returned -- it uses
// this information to track how many CASes the AnalysisComponent
// is using at any one time.
((UimaContext_ImplBase) getUimaContext()).returnedCAS(absCas);
// convert back to CASImpl and then get the initial View
CAS casToReturn;
if (absCas instanceof JCas) {
casToReturn = ((JCas) absCas).getCas();
} else {
casToReturn = (CAS) absCas;
}
casToReturn = casToReturn.getView(CAS.NAME_DEFAULT_SOFA);
// clear the CAS's component info, since it is no longer
// being processed by this AnalysisComponent
casToReturn.setCurrentComponentInfo(null);
((CASImpl)casToReturn).restoreClassLoaderUnlockCas();
return casToReturn;
} catch (Exception e) {
// log and rethrow exception
getLogger().log(Level.SEVERE, "", e);
if (e instanceof AnalysisEngineProcessException)
throw (AnalysisEngineProcessException) e;
else
throw new AnalysisEngineProcessException(e);
}
}
/**
* @see org.apache.uima.resource.AnalysisEngine#reconfigure()
*/
public void reconfigure() throws ResourceConfigurationException {
// do base resource reconfiguration
super.reconfigure();
// inform the annotator
try {
mAnalysisComponent.reconfigure();
} catch (ResourceInitializationException e) {
throw new ResourceConfigurationException(e);
}
}
protected AnalysisComponent getAnalysisComponent() {
return mAnalysisComponent;
}
/**
* Implements the iterator that steps through all outputs from an AnalysisComponent.
*/
class AnalysisComponentCasIterator implements CasIterator {
private AnalysisComponent mMyAnalysisComponent;
private CAS mInputCas;
AnalysisComponentCasIterator(AnalysisComponent aAnalysisComponent, CAS aInputCas) {
mMyAnalysisComponent = aAnalysisComponent;
mInputCas = aInputCas;
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.core.CasIterator#hasNext()
*/
public boolean hasNext() throws AnalysisEngineProcessException {
enterProcess();
try {
boolean result = mMyAnalysisComponent.hasNext();
if (!result) {
//when hasNext returns false, by contract the AnalysisComponent is done processing its
//input CAS. Now is the time to clear the currentComponentInfo to indicate that the
//CAS is no longer being processed.
mInputCas.setCurrentComponentInfo(null);
((CASImpl)mInputCas).restoreClassLoaderUnlockCas();
}
return result;
} finally {
exitProcess();
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.core.CasIterator#next(java.lang.Class)
*/
public CAS next() throws AnalysisEngineProcessException {
enterProcess();
try {
// Make sure that the AnalysisComponent has a next CAS to return
boolean analysisComponentHasNext = mMyAnalysisComponent.hasNext();
if (!analysisComponentHasNext) {
throw new UIMA_IllegalStateException(UIMA_IllegalStateException.NO_NEXT_CAS,
new Object[0]);
}
// call AnalysisComponent.next method to populate CAS
try {
CAS cas = callAnalysisComponentNext();
// cas.setParentID(mOriginalCas.getID());
return cas;
} catch (Exception e) {
if (e instanceof AnalysisEngineProcessException) {
throw (AnalysisEngineProcessException) e;
}
throw new AnalysisEngineProcessException(e);
}
} finally {
exitProcess();
}
}
/*
* (non-Javadoc)
*
* @see org.apache.uima.analysis_engine.CasIterator#release()
*/
public void release() {
// nothing to do
}
}
}