Component Executor Lifecycle

A component execution class that must implement the Component interface or extend the AbstractComponent class, goes through a specific lifecycle in the course of execution.

The infrastructure will call methods on the class in a specific predetermined order. In general, a component execution class will have the following methods called in this order:

  1. no-argument constructor is called to create an instance of the class
  2. initialize()
  3. configEnvironment()
  4. preExecute()
  5. execute()
  6. postExecute()
  7. destroy()

After the destroy() method is called, the infrastructure removes all references to the class instance. Sometime thereafter, the JVM may clean up the instance. The system guarantees that these “lifecycle” methods are called in sequence, one at a time, and always on the same JVM thread.

See Component Lifecycle Methods for more information in each of the methods in the component lifecycle. Many components will need to implement only the execute() method.

The system may create multiple instances of the component execution class, and the instances may all run at the same time (each on its own thread). In the SIMULIA Execution Engine distributed execution environment, a single station that services execution requests for many users running different models. Isight models that use parallel process components or parallel simulation process flow may also create multiple concurrently running instances of a component execution class.

To allow for multiple simultaneously running instances, the component execution class must not share data between instances. Specifically, the component must not use STATIC class fields or call any native (JNI) code that uses static storage. If static data are used, the component must provide the required thread synchronization to prevent data corruption or unintended data sharing between instances. In general, one execution of a component should be independent of all other executions that happen before, at the same time, or after it. There are some special cases that may require instances to share data (see the following section on “persistent” components). In general, it is recommended that component execution classes have no static data.

If the component cannot be made safe for multiple simultaneous use (for example, it may call native JNI code that uses static storage), the component can limit the number of instances that will be created. If the limit is set to “1”, only a single instance will ever exist at the same time. For more information, see Limiting Component Instances.

Component Lifecycle Methods

Each method in the component lifecycle has specific purposes. Most components will need to implement only a few or even one of them because the AbstractComponent class has default implementations for most methods. More complex components may need to implement (override) all of them.

The component lifecycle methods are:

  • no-argument constructor

  • initialize()

  • Repeat the following execution sequence for each execution:

    • configEnvironment()

    • preExecute()

    • execute()

    • configEnvironment()

    • postExecute()

  • destroy()

Each of these methods is described in the following sections:

Method: No-Argument constructor

This constructor is the first method called by the system when constructing a new instance of the component runtime class. This constructor must exist in the implementation class, be declared public, and must not throw any exceptions. It is recommended that this constructor do nothing and that all one-time component initialization be done in the initialize() method where exception handling is well defined.

The signature for this method is:

public MyClass()

All static blocks and static initialization will be done as part of the construction of the class. It is recommended that component runtime classes perform no static initialization and declare no static class fields. For more information on the use of static data, see Reusable and Persistent Components.

Method: initialize()

The signature for this method is:

public void initialize(StationEnv stationEnv) throws
RtException;

This method will be the first method called by the system after the class instance is constructed. It will be called once for the entire lifetime of the class, even if the class instance is reused for many execution cycles (see Reusable and Persistent Components).

This method gives the component an opportunity to perform any one-time initialization. This method will be called in advance of any request to execute the component. There is no job or workitem associated with this method call; thus, it is not possible to write messages to a job log, obtain the parameter Context object, or do any other job-specific function. The StationEnv argument gives access to the component MetaModel (for loading resources, for example), utility methods to run system commands, and other functions.

If any errors occur in this method or log messages need to be generated, the StationEnv.getStationLog() method returns a Log interface that can be used to write log messages. These messages are written to the SIMULIA Execution Engine station log and will not be part of any job log. (In Isight these messages will appear in the gateway log file, which is accessible from a button at the bottom of the Design Gateway screen.)

If this method throws any exception (including the declared RtException type), the system will discard this component instance and no other methods will be called. The component execution that triggered the creation of the component class will be considered to have failed. The next request to execute this component will create a new instance, and this method will be attempted again.

Method: configEnvironment()

Method signature:

public void configEnvironment(RuntimeEnv runEnv) throws
RtException;

This method is called by the runtime infrastructure as the first step in executing a component. This method gives the component code an opportunity to configure the runtime environment before the plug-ins (if any) are called and before the environment is used to set up the local execution directories and files. In particular, the RuntimeEnv.setLocalDir()method may be called at this time, which is the only time at which that method may be called.

At the time this method is called, there will be no local execution directory, and no files have been prestaged yet. All the component properties and parameters are available through the RuntimeEnv argument, as well as methods for writing the job log, the station log, and other functions. Process-type components may not run any subflows at this time (e.g., may not call the RuntimeEnv.getExecutor().runSubflow() methods).

Only components that need to have explicit control of the local runtime directory will need to implement (or override) this method. This method will be called once each time the component is executed.

Method: preExecute()

Method signature:

public void preExecute(RuntimeEnv runEnv) throws
RtException;

This method is called once each time a component is executed, before the execute() method.

This method is called once each time a component is executed, before the execute() method. At this time any file parameters will have been written (prestaged) in the local execution directory. This method is called before any plug-ins (if any) have their preExecute() methods called.

All the component properties and parameters are available through the RuntimeEnv argument, as well as methods for writing the job log, the station log, and other functions. Process components may not run any subflows at this time (e.g., may not call the RuntimeEnv.getExecutor().runSubflow() methods).

The component can override this method to implement any file manipulation that might be required before the component begins execution using the execute() method.

Method: execute()

Method signature:

public void execute(RuntimeEnv runEnv) throws
RtException;

This method is the main execution method of the component. The bulk of the component work should be done in this method. Many components will need to implement only this single method. This method is called once each time the component is executed.

All the component properties and parameters are available through the RuntimeEnv argument as well as methods for writing the job log, the station log, and other functions. All files have been prestaged in the local execution directory, and any plug-ins associated with the component have been pre-executed (i.e., have had their preExecute() method called).

If this method throws an RtException, it will be handled by the infrastructure by recording the exception in the job log and indicating that the component execution failed.

References to the RuntimeEnv argument should not be saved and used across multiple calls to this method. The RuntimeEnv argument is valid only for the duration of a single call to this method. After this method returns, calls to the RuntimeEnv object have undefined results.

The following is a sample execute() method for an application-type component. It sums all the input parameter values and writes the results to an output parameters named “sum”.

public void execute(RuntimeEnv runEnv) throws RtException {
   try {
      // Walk the parameter list
     double sum = 0.0;
     Iterator it = runEnv.getContext().getIterator();
     while (it.hasNext()) {
        Variable parm = (Variable)it.next();

        // Process input scalar values
        if ((parm.getStructure() == Variable.STRUCT_SCALAR) &&
           (parm.getMode() == Variable.MODE_INPUT)) {

              // Add them up
              sum = sum + 
              ((ScalarVariable)parm).getValueObj().getAsReal();
           }
         }

         // Assign the sum to the output parameter
         ScalarVariable sumParm = 
         runEnv.getContext().getScalar("sum");
         sumParm.getValueObj().setValue(sum);
     }
     catch (Throwable t) {
        throw new RtException(t, "My component failed to execute.");
     }
   }

This simple example does little in the way of error checking and assumes that all the input scalar parameters are numeric variables (not a good assumption in most components).

It is good practice to catch all types of Throwable errors (checked and uncheck exceptions) in the execute() method and rethrow them as RtException type. The original error is included in the constructor of the RtException so that all the failure details will be displayed in the job log.

Process Component Subflow Execution

This method is the only one in which process-type components may run subflows by calling the RuntimeEnv.getExecutor().runSubflow() method. This process is typically done by first creating a Context that contains the parameter values to be used by the subflow. The subflow Context is initially populated with a copy of the process component’s parameters. Typically, the component will alter some values in the subflow Context and then submit it for execution.

For example, the following execute() method for a process-type will run one subflow with all the scalar parameters that begin with “a” set to the value of zero:

public void execute(RuntimeEnv runEnv) throws RtException {
   try {
      Context subflowCtx = 
      runEnv.getExecutor().createSubflowContext();
      // Walk the parameter list
      Iterator it = subflowCtx.getIterator();
      while (it.hasNext()) {
         Variable parm = (Variable)it.next();
         // Scalar values that start with "a"
         if ((parm.getStructure() == Variable.STRUCT_SCALAR) &&
            (parm.getName().startsWith("a"))) {
               // Set value to zero
               ((ScalarVariable)parm).getValueObj().setValue(0);
            }
         }
     // Submit subflow for execution with updated parameters
         String subflowID = 
         runEnv.getExecutor().runSubflow(subflowCtx);
         // Wait for it to finish
         SubflowResults results = 
         runEnv.getExecutor().waitForSubflow(subflowID);

         // If subflow failed, we should fail too.
         switch (result.getCc()) {
           case PSEUtils.WORK_CC_OK:
           case PSEUtils.WORK_CC_NOTRUN:
              break;    // Continue
           case PSEUtils.WORK_CC_CANCELLED:
              // If subflow cancelled, cancel this component too.
              throw new RtCancelledException(new IString(CLASS, 71160, 
              "Subflow {0} cancelled.", result.getSubflowId()));
              default:
              // If subflow failed, fail this component too.
              throw new RtSubflowFailedException(new IString(CLASS, 
              68677, "Subflow {0} failed.", result.getSubflowId()));
         }

         // Now that the subflow has completed successfully, copy 
         outputs 
         // from the subflow results to my context.
         if (result.getCc() == PSEUtils.WORK_CC_OK) {
            RtUtils.mapContext(runEnv.getContext(), 
            result.getOutputContext());
         }

         //Perform some cleanup to free memory that is holding 
         results
         result.discardResultContexts();
         runEnv.getExecutor().discardSubflowResults(id);
      } 
      catch (RtException rte) {
      throw rte; // No need to wrap RtExceptions
    }
    catch (Throwable t) {
       throw new RtException(t, "My component failed to execute.");
    }
  }

This example does minimal error checking but shows the basic methods used by a process component to submit a subflow for execution, to wait for it to complete, and to get the results of the subflow execution.

Method: postExecute()

Method signature:

public void postExecute(RuntimeEnv runEnv) throws RtException;

This method is called after the component has returned from or thrown an exception from, the execute() method and after output file parameters have been processed from the local execution directory. This method is called after any plug-ins (if any) have their postExecute() methods called.

In this method the component can perform any final per-execution cleanup that may be required. Any resources allocated in the preExecute() method may be released.

This method will be called even if the execute() method throws an exception. However, this method will not be called if the execute() method never returns (e.g., does not respond). In this case the component will eventually either be cancelled or timed out. For more information on what happens under these conditions, see Component Executor Lifecycle.

Method: destroy()

Method signature:

public void destroy(StationEnv stationEnv) throws
RtException;

This method will be called once for the entire lifetime of the class, even if the class instance is reused for many execution cycles (see Reusable and Persistent Components). It will be the last method called by the system. After this method is called, the system will remove all references to the class instance. Some time thereafter, the JVM may (or may not) garbage collect the class instance.

If the component has a reusePolicy of any, this method may not be called until the SIMULIA Execution Engine station is shut down (SIMULIA Execution Engine environment) or until the Isight main window is closed (Isight environment).

The component should release any resources that were allocated in the initialize() method, such as stopping any external process that was started.