You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@uima.apache.org by al...@apache.org on 2007/03/01 20:33:37 UTC

svn commit: r513458 - in /incubator/uima/uimaj/trunk/uimaj-core/src: main/java/org/apache/uima/analysis_engine/ main/java/org/apache/uima/flow/ main/java/org/apache/uima/flow/impl/ main/resources/org/apache/uima/ test/java/org/apache/uima/flow/ test/ja...

Author: alally
Date: Thu Mar  1 11:33:36 2007
New Revision: 513458

URL: http://svn.apache.org/viewvc?view=rev&rev=513458
Log:
Added FlowController APIs addAnalysisEngines and removeAnalysisEngines.
UIMA-327: https://issues.apache.org/jira/browse/UIMA-327

Added:
    incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/
    incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/
    incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/FixedFlowControllerTest.java
Modified:
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineProcessException.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController_ImplBase.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FixedFlowController.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FlowControllerContext_impl.java
    incubator/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineProcessException.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineProcessException.java?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineProcessException.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/analysis_engine/AnalysisEngineProcessException.java Thu Mar  1 11:33:36 2007
@@ -75,6 +75,20 @@
   public static final String INCORRECT_CAS_INTERFACE = "incorrect_cas_interface";
 
   /**
+   * Message key for a standard UIMA exception message: "The FlowController class {0} does not 
+   * support the removeAnalysisEngines method.  Analysis Engines cannot be dynamically removed
+   * from the flow."
+   */
+  public static final String REMOVE_AE_FROM_FLOW_NOT_SUPPORTED = "remove_ae_from_flow_not_supported";
+
+  /**
+   * Message key for a standard UIMA exception message: "The Analysis Engine(s) {0} have
+   * been removed from the flow, and the FlowController has determined the Aggregate 
+   * Analysis Engine's processing can no longer continue."
+   */
+  public static final String FLOW_CANNOT_CONTINUE_AFTER_REMOVE = "flow_cannot_continue_after_remove";
+
+  /**
    * Creates a new exception with a null message.
    */
   public AnalysisEngineProcessException() {

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController.java?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController.java Thu Mar  1 11:33:36 2007
@@ -19,6 +19,8 @@
 
 package org.apache.uima.flow;
 
+import java.util.Collection;
+
 import org.apache.uima.UimaContext;
 import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.cas.AbstractCas;
@@ -152,4 +154,50 @@
    * @return the required CAS interface. This must specify a subtype of {@link AbstractCas}.
    */
   Class getRequiredCasInterface();
+  
+  /**
+   * Notifies this FlowController that new Analysis Engines are now available to route CASes to.
+   * Prior to calling this method the framework will update
+   * {@link FlowControllerContext#getAnalysisEngineMetaDataMap()}
+   * with the metadata for these new Analysis Engines.
+   * <p>
+   * This FlowController is not obligated to do anything in response to this method if it does
+   * not want to consider routing CASes to the new AnalysisEngines.
+   * <p>
+   * The contract for this method is that the framework will not concurrently call any
+   * {@link Flow#next()} methods on any Flow objects produced by this FlowController, during the
+   * time between when the Analysis Engine MetaData map is updated and the time when this method
+   * completes.
+   * 
+   * @param aKeys a Collection of Strings, each of which is the key of an Analysis Engine to which 
+   *   CASes can be routed.  These are the same keys as used in 
+   *   {@link FlowControllerContext#getAnalysisEngineMetaDataMap()}. 
+   */
+  void addAnalysisEngines(Collection aKeys);
+
+  /**
+   * Notifies this FlowController that some Analysis Engines are no longer available to route CASes to.
+   * Prior to calling this method the framework will update
+   * {@link FlowControllerContext#getAnalysisEngineMetaDataMap()}
+   * and will remove the metadata for these new Analysis Engines.
+   * <p>
+   * It is not required for a FlowController implementation to support this method.  It may throw
+   * an exception if this operation is not supported 
+   * (see {@link AnalysisEngineProcessException#REMOVE_AE_FROM_FLOW_NOT_SUPPORTED}.  
+   * Also the FlowController may throw an Exception if it determines that it does not make sense for 
+   * the flow to continue in the absence of the removed Analysis Engines
+   * (see {@link AnalysisEngineProcessException#FLOW_CANNOT_CONTINUE_AFTER_REMOVE}.
+   * <p>
+   * The contract for this method is that the framework will not concurrently call any
+   * {@link Flow#next()} methods on any Flow objects produced by this FlowController, during the
+   * time between when the Analysis Engine MetaData map is updated and the time when this method
+   * completes.
+   * 
+   * @param aKeys a Collection of Strings, each of which is the key of an Analysis Engine to which CASes
+   *   may no longer be routed. 
+   *   
+   * @throws AnalysisEngineProcessException if the FlowController cannot continue with these 
+   *   Analysis Engines removed, or doesn't support removing Analysis Engines at all.
+   */
+  void removeAnalysisEngines(Collection aKeys) throws AnalysisEngineProcessException;
 }

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController_ImplBase.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController_ImplBase.java?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController_ImplBase.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/FlowController_ImplBase.java Thu Mar  1 11:33:36 2007
@@ -19,6 +19,8 @@
 
 package org.apache.uima.flow;
 
+import java.util.Collection;
+
 import org.apache.uima.analysis_engine.AnalysisEngineProcessException;
 import org.apache.uima.resource.ResourceConfigurationException;
 import org.apache.uima.resource.ResourceInitializationException;
@@ -81,6 +83,28 @@
    * @see org.apache.uima.flow.FlowController#destroy()
    */
   public void destroy() {
+  }
+  
+  
+
+  /**
+   * Does nothing by default.  Subclasses may override this to support adding new
+   * AnalysisEngines to the flow.
+   * @see org.apache.uima.flow.FlowController#addAnalysisEngines(java.util.Collection)
+   */
+  public void addAnalysisEngines(Collection aKeys) {
+    //does nothing by default
+  }
+
+  /**
+   * Throws an AnalysisEngineProcessException by default.  Subclasses may override
+   * this to support removing AnalysisEngines from the flow.
+   * @see org.apache.uima.flow.FlowController#removeAnalysisEngines(java.util.Collection)
+   */
+  public void removeAnalysisEngines(Collection aKeys) throws AnalysisEngineProcessException {
+    throw new AnalysisEngineProcessException(
+            AnalysisEngineProcessException.REMOVE_AE_FROM_FLOW_NOT_SUPPORTED,
+            new Object[] { getClass().getName() });
   }
 
   /**

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FixedFlowController.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FixedFlowController.java?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FixedFlowController.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FixedFlowController.java Thu Mar  1 11:33:36 2007
@@ -21,6 +21,9 @@
 
 import java.io.IOException;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
 
 import org.apache.uima.UIMAFramework;
 import org.apache.uima.UIMARuntimeException;
@@ -59,14 +62,15 @@
 
   private static final int ACTION_DROP_IF_NEW_CAS_PRODUCED = 3;
 
-  private String[] mSequence;
+  private ArrayList mSequence;
 
   private int mActionAfterCasMultiplier;
 
   public void initialize(FlowControllerContext aContext) throws ResourceInitializationException {
     super.initialize(aContext);
     FlowConstraints flowConstraints = aContext.getAggregateMetadata().getFlowConstraints();
-    mSequence = ((FixedFlow) flowConstraints).getFixedFlow();
+    mSequence = new ArrayList();
+    mSequence.addAll(Arrays.asList(((FixedFlow) flowConstraints).getFixedFlow()));
 
     String actionAfterCasMultiplier = (String) aContext
             .getConfigParameterValue(PARAM_ACTION_AFTER_CAS_MULTIPLIER);
@@ -93,6 +97,22 @@
   public Flow computeFlow(CAS aCAS) throws AnalysisEngineProcessException {
     return new FixedFlowObject(0);
   }
+  
+  /* (non-Javadoc)
+   * @see org.apache.uima.flow.FlowController_ImplBase#addAnalysisEngines(java.util.Collection)
+   */
+  public void addAnalysisEngines(Collection aKeys) {
+    // Append new keys to end of Sequence
+    mSequence.addAll(aKeys);
+  }
+
+  /* (non-Javadoc)
+   * @see org.apache.uima.flow.FlowController_ImplBase#removeAnalysisEngines(java.util.Collection)
+   */
+  public void removeAnalysisEngines(Collection aKeys) throws AnalysisEngineProcessException {
+    //Remove keys from Sequence
+    mSequence.removeAll(aKeys);
+  }
 
   public static FlowControllerDescription getDescription() {
     URL descUrl = FixedFlowController.class
@@ -170,19 +190,19 @@
         casMultiplierProducedNewCas = false;
       }
 
-      if (currentStep >= mSequence.length) {
+      if (currentStep >= mSequence.size()) {
         return new FinalStep(); // this CAS has finished the sequence
       }
 
       // if next step is a CasMultiplier, set wasPassedToCasMultiplier to true for next time
       // TODO: optimize
       AnalysisEngineMetaData md = (AnalysisEngineMetaData) getContext()
-              .getAnalysisEngineMetaDataMap().get(mSequence[currentStep]);
+              .getAnalysisEngineMetaDataMap().get(mSequence.get(currentStep));
       if (md.getOperationalProperties().getOutputsNewCASes())
         wasPassedToCasMultiplier = true;
 
       // now send the CAS to the next AE in sequence.
-      return new SimpleStep(mSequence[currentStep++]);
+      return new SimpleStep((String)mSequence.get(currentStep++));
     }
 
     /*
@@ -195,7 +215,7 @@
       casMultiplierProducedNewCas = true;
       // start the new output CAS from the next node after the CasMultiplier that produced it
       int i = 0;
-      while (!mSequence[i].equals(producedBy))
+      while (!mSequence.get(i).equals(producedBy))
         i++;
       return new FixedFlowObject(i + 1, true);
     }

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FlowControllerContext_impl.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FlowControllerContext_impl.java?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FlowControllerContext_impl.java (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/java/org/apache/uima/flow/impl/FlowControllerContext_impl.java Thu Mar  1 11:33:36 2007
@@ -19,6 +19,7 @@
 
 package org.apache.uima.flow.impl;
 
+import java.util.Collections;
 import java.util.Map;
 
 import org.apache.uima.UimaContextAdmin;
@@ -46,7 +47,7 @@
           Map aSofaMappings, Map aAnalysisEngineMetaDataMap,
           AnalysisEngineMetaData aAggregateMetadata) {
     super(aParentContext, aContextName, aSofaMappings);
-    mAnalysisEngineMetaDataMap = aAnalysisEngineMetaDataMap;
+    mAnalysisEngineMetaDataMap = Collections.unmodifiableMap(aAnalysisEngineMetaDataMap);
     mAggregateMetadata = aAggregateMetadata;
 
     // add our MBean to the tree

Modified: incubator/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties?view=diff&rev=513458&r1=513457&r2=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties (original)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/main/resources/org/apache/uima/UIMAException_Messages.properties Thu Mar  1 11:33:36 2007
@@ -181,6 +181,13 @@
 
 incorrect_cas_interface = Expected CAS interface {0}, but received interface {1}.
      	   	
+remove_ae_from_flow_not_supported = The FlowController class {0} does not support the removeAnalysisEngines method. \
+ Analysis Engines cannot be dynamically removed from the flow.
+ 
+flow_cannot_continue_after_remove = The Analysis Engine(s) {0} have been removed from the flow, and the FlowController \
+  has determined that the Aggregate Analysis Engine's processing can no longer continue.
+ 
+ 
 #-------------------------------
 #ResourceInitializationException
 #-------------------------------

Added: incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/FixedFlowControllerTest.java
URL: http://svn.apache.org/viewvc/incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/FixedFlowControllerTest.java?view=auto&rev=513458
==============================================================================
--- incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/FixedFlowControllerTest.java (added)
+++ incubator/uima/uimaj/trunk/uimaj-core/src/test/java/org/apache/uima/flow/impl/FixedFlowControllerTest.java Thu Mar  1 11:33:36 2007
@@ -0,0 +1,212 @@
+/*
+ * 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.flow.impl;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import junit.framework.TestCase;
+
+import org.apache.uima.UIMAFramework;
+import org.apache.uima.UimaContextAdmin;
+import org.apache.uima.analysis_engine.metadata.AnalysisEngineMetaData;
+import org.apache.uima.analysis_engine.metadata.FixedFlow;
+import org.apache.uima.analysis_engine.metadata.impl.AnalysisEngineMetaData_impl;
+import org.apache.uima.analysis_engine.metadata.impl.FixedFlow_impl;
+import org.apache.uima.cas.CAS;
+import org.apache.uima.flow.FinalStep;
+import org.apache.uima.flow.Flow;
+import org.apache.uima.flow.FlowControllerContext;
+import org.apache.uima.flow.SimpleStep;
+import org.apache.uima.flow.Step;
+import org.apache.uima.resource.metadata.OperationalProperties;
+import org.apache.uima.resource.metadata.impl.OperationalProperties_impl;
+import org.apache.uima.resource.metadata.impl.TypeSystemDescription_impl;
+import org.apache.uima.util.CasCreationUtils;
+
+/**
+ * 
+ */
+public class FixedFlowControllerTest extends TestCase {
+
+  private Map analysisEngineMetaDataMap;
+  private FixedFlowController fixedFlowController;
+  
+  /* (non-Javadoc)
+   * @see junit.framework.TestCase#setUp()
+   */
+  protected void setUp() throws Exception {
+    super.setUp();
+    analysisEngineMetaDataMap = new HashMap();
+    AnalysisEngineMetaData delegateMd = new AnalysisEngineMetaData_impl();
+    delegateMd.setOperationalProperties(new OperationalProperties_impl());
+    analysisEngineMetaDataMap.put("key1", delegateMd);
+    analysisEngineMetaDataMap.put("key2", delegateMd);
+    analysisEngineMetaDataMap.put("key3", delegateMd);
+    
+    AnalysisEngineMetaData aggregateMd = new AnalysisEngineMetaData_impl();
+    FixedFlow fixedFlow = new FixedFlow_impl();
+    fixedFlow.setFixedFlow(new String[]{"key1", "key2", "key3"});
+    aggregateMd.setFlowConstraints(fixedFlow);
+    OperationalProperties opProps = new OperationalProperties_impl();
+    aggregateMd.setOperationalProperties(opProps);
+    
+    UimaContextAdmin rootContext = UIMAFramework.newUimaContext(
+            UIMAFramework.getLogger(), UIMAFramework.newDefaultResourceManager(),
+            UIMAFramework.newConfigurationManager());
+    FlowControllerContext fcContext = new FlowControllerContext_impl(
+            rootContext, "_FlowController", Collections.EMPTY_MAP,
+            analysisEngineMetaDataMap, aggregateMd);
+    fixedFlowController = new FixedFlowController();
+    fixedFlowController.initialize(fcContext);    
+  }
+
+  public void testComputeFlow() throws Exception {
+    CAS cas1 = CasCreationUtils.createCas(new TypeSystemDescription_impl(), null, null);
+    CAS cas2 = CasCreationUtils.createCas(new TypeSystemDescription_impl(), null, null);
+    Flow flow1 = fixedFlowController.computeFlow(cas1);
+    Flow flow2 = fixedFlowController.computeFlow(cas2);
+    //two steps in flow 1
+    Step step = flow1.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow1.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key2", ((SimpleStep)step).getAnalysisEngineKey());
+    
+    //one step in flow 2
+    step = flow2.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+
+    //third step in flow 1
+    step = flow1.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());
+
+    //one step in flow 2
+    step = flow2.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key2", ((SimpleStep)step).getAnalysisEngineKey());
+
+    //finish flow 1
+    step = flow1.next();
+    assertTrue(step instanceof FinalStep);
+    
+    //finish flow 2
+    step = flow2.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow2.next();
+    assertTrue(step instanceof FinalStep);
+  }
+  
+  public void testAddAnalysisEngines() throws Exception {
+    CAS cas = CasCreationUtils.createCas(new TypeSystemDescription_impl(), null, null);
+    Flow flow = fixedFlowController.computeFlow(cas);
+    //two steps in flow
+    Step step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key2", ((SimpleStep)step).getAnalysisEngineKey());
+    
+    //now add two new AEs
+    //first update AE metadata map
+    AnalysisEngineMetaData delegateMd = new AnalysisEngineMetaData_impl();
+    delegateMd.setOperationalProperties(new OperationalProperties_impl());
+    analysisEngineMetaDataMap.put("key4", delegateMd);    
+    analysisEngineMetaDataMap.put("key5", delegateMd);    
+    //then notify FC
+    List newAeKeys = new ArrayList();
+    newAeKeys.add("key4");
+    newAeKeys.add("key5");
+    fixedFlowController.addAnalysisEngines(newAeKeys);
+    
+    //finish flow
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key4", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key5", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof FinalStep);
+    
+    //test new flow
+    flow = fixedFlowController.computeFlow(cas);
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key2", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key4", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key5", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof FinalStep);
+  }
+  
+  public void testRemoveAnalysisEngines() throws Exception {
+    CAS cas = CasCreationUtils.createCas(new TypeSystemDescription_impl(), null, null);
+    Flow flow = fixedFlowController.computeFlow(cas);
+    //one step in flow
+    Step step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+    
+    //remove "key2"
+    analysisEngineMetaDataMap.remove("key2");
+    List removedKeys = new ArrayList();
+    removedKeys.add("key2");
+    fixedFlowController.removeAnalysisEngines(removedKeys);
+    
+    //finish flow
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());    
+    step = flow.next();
+    assertTrue(step instanceof FinalStep);
+    
+    //test new flow
+    flow = fixedFlowController.computeFlow(cas);
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key1", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof SimpleStep);
+    assertEquals("key3", ((SimpleStep)step).getAnalysisEngineKey());
+    step = flow.next();
+    assertTrue(step instanceof FinalStep);
+  }
+}