Error Streams Sample

This sample consists of a Studio project that contains three EventFlow module files (dividemodule.sbapp, dividetoplevel.sbapp, and outoforder.sbapp), a customized StreamBase Server configuration file (engine.conf), and a small amount of Java code used by the applications as a custom function.

The goal of this sample is to demonstrate various use cases for error streams in StreamBase applications.

By default, an error encountered at runtime interrupts the processing of data for the current tuple that caused the error. StreamBase Server reacts to the error according to the type of problem encountered. By default, StreamBase Server does not shut down on receipt of an error, but instead logs all errors and allows processing to continue. You can optionally force the Server to shut down for certain errors if your application design requires that behavior.

Error ports and error streams allow application authors to handle errors as data. You can apply local logic to error processing using StreamBase EventFlow just as when processing normal data.

Importing This Sample into StreamBase Studio

In StreamBase Studio, import this sample with the following steps:

  • From the top-level menu, select File>Import Samples and Community Content.

  • Enter err to narrow the list of options.

  • Select Using user-defined Error Streams from the Applications category.

  • Click Import Now.

StreamBase Studio creates a project for the sample.

Error Handling Sample Setup

  1. Make sure you are in the SB Authoring perspective.

  2. In the Project Explorer view, locate the sample project named sample_errorstreams.

  3. Double-click to open the dividemodule.sbapp application.

Dividemodule Application

The dividemodule application uses both error ports and error streams features.

DoDivision: Notice that the bottom output port of this Map operator is red. This indicates that this operator has its error output port enabled. This port outputs a tuple for every error that occurs during processing inside this operator.

UnhandledErrors: This component is this application's error input stream, and is used to catch unexpected errors that occur anywhere in this module.

ErrorOutputStream: This component is a special output stream that works much like a throw mechanism for this application. It allows the application to pass errors up the chain to be handled elsewhere. Errors are passed to a parent module or to StreamBase Server for handling.

What is Demonstrated

This sample walks you through an interactive session demonstrating:

  • How errors are handled by default.

  • How errors can be caught, even when coming from user-defined Java code.

  • How caught errors are processed as data or continue on up the chain of modules as an error with custom actions.

  • How errors can be caught in a single location when no path-specific error handling is needed.

  • How module boundaries can be used to collect uncaught errors.

Error Output Ports

During processing of tuple data, operators may occasionally trigger an error. Errors occurring in an operator might be due to data (such as a division by zero), or due to external resources (such as attempting to open an unavailable file).

To provide custom handling for errors that occur within an operator, enable that operator's error output port. Studio draws a red output port, always in the last port position, that allows you to connect other components to that output stream, just as you already connect data streams together.

DoDivision

DoDivision is an operator in the dividemodule application that has its error output port enabled.

Select this operator and observe the General tab in the Properties view. Notice that the Enable Error Output Port option is selected.

Notice in the Output Settings tab that this Map operator divides two incoming data. This expression has a potential for generating an error if the divisor field is ever zero.

Error Input Streams

Error Input Streams handle any otherwise unhandled error in the current module. Handled errors are those caught at the operator level and sent out that operator's error output port. All other errors in the module are unhandled and are caught by the Error Input Stream.

For each unhandled error, a tuple appears on the Error Input Stream, and flows through the application as data that you can manipulate. The schema for error tuples is standardized.

There can be only one Error Input Stream per module. Renaming the Error Input Stream is allowed.

If you do not place an Error Input Stream in a module, then any errors in that module are automatically passed up to the parent module's Error Input Stream (if any). This process repeats until reaching StreamBase Server's error handler.

Unhandled Errors

UncheckedDivision is a Map operator that has the same expression settings as DoDivision, but it does not have its error output port enabled. This means a divide-by-zero error in UncheckedDivision will stop processing of the tuple that triggers the error.

UnhandledErrors is this module's Error Input Stream. Errors from UncheckedDivision cause a tuple to appear here.

Error Output Streams

Error Output Streams provide a throw mechanism for the application or module. You can use them to ensure that errors you handle locally continue up to the next level of error handling.

There can be many Error Output Streams in a module, but the schema is fixed for each Error Output Stream. This schema is the same as that of an Error Input Stream and also that of any error output port, as these are typically on the same path.

ErrorOutputStream Component

ErrorOutputStream is the name of the only Error Output Stream in the dividemodule application. It is there to ensure that all the paths that provide error handling are propagated for handling to a parent module or to StreamBase Server. (Paths that handle errors are the one that begins with UnhandledErrors, and the one that begins with DoDivision's error output port.)

What each error processing path does to error tuples can dramatically change the behavior of the application. We will learn more about this in the next steps.

Demo: Default Error Handling

  1. In the Project Explorer view, open the sample you just loaded.

    If you see red marks, wait a moment for the project in Studio to load its features.

    If the red marks do not resolve themselves after a minute, select the project, right-click, and select Maven>Update Project from the context menu.

  2. Open the src/main/eventflow/packageName folder.

  3. Open the dividemodule.sbapp application file and click the Run button. This opens the SB Test/Debug perspective and starts the module.

  4. Use the Manual Input view to send data manually, and look for the results in the Output Streams view.

  5. Send data through the path that contains no special error handling. This demonstrates default error handling with an explicit server shutdown action.

  6. Select the UnsafeDivisionInput stream from the Input Stream drop-down list in the Manual Input view, and send a pair of non-zero integers (such as 200 and 8) in the dividend and divisor fields. Look at the DivisionResult output stream to confirm that output contains the result of the division.

  7. Send any integer value for the dividend, but send zero for the divisor. Immediately after clicking the Send Data button, Studio shows an error message from the Server announcing that it is shutting down.

  8. When done press F9 or click the Terminate EventFlow Fragment button.

Explanation

This is what happened in the last demonstration, when the tuple containing zero was sent:

  1. The division by zero occurred in the UncheckedDivision component, which does not have any special error handling.

  2. The error was passed out into the module, to see if an Error Input Stream exists to handle the error.

  3. StreamBase found the Error Input Stream named UnhandledErrors in this module, and a tuple appeared there containing details about the error.

  4. The Map operator named ForceShutdown does two things:

    1. The error tuple's description field was tagged with the prefix (unhandled).

    2. The error tuple's action field was changed from its default value of continue to shutdown.

  5. The error tuple eventually found its way to ErrorOutputStream, and from there, it passed to the running Server.

  6. The Server received the error tuple containing the shutdown action, and shut itself down in response.

Demo: Caught Error Handling

Run the dividemodule.sbapp application again. This time, we will cause the division-by-zero error to occur in the DoDivision Map operator, which has an error output port.

  1. Open the Console View, if it is not already open. Use Window>Show View>Other>General>Console.

  2. Select the SafeDivisionInput stream from the Input stream drop-down list. As before, send two valid non-zero integers such as 200 and 8, and look at the DivisionResult output stream to confirm that output contains the result of the division.

  3. Next, send a tuple whose divisor is zero on the SafeDivisionInput input stream. This time, output is received on the CustomizedErrorOutput port. This demonstrates how the application was able to emit a user-defined tuple as a result of an error, and does not shut down the server.

Explanation

Notice the following features of the last demonstration:

  1. The division by zero occurred in the DoDivision Map operator. In all cases, this caused an error.

  2. Error tuples found their way first to the CustomizedErrorOutput stream as a custom tuple with a human-readable error message.

  3. Error tuples were also sent to ErrorOutputStream. It is important to do this in order to continue normal error processing such as logging.

  4. If you have the StreamBase logging level set to at a high enough level, you see an error message from StreamBase Server in the Console view.

Module Error Handling

Stop the application from the previous demonstration. Find and open the dividetoplevel.sbapp application file. This simple application contains a module reference to the first application, dividemodule.sbapp.

Notice that the module reference has a red port. This is its error output port for the inner module, enabled the same way for module references as for any operator. This error port emits tuples for any error that was generated inside the inner module during processing.

Demo: Module Error Handling

Run the dividetoplevel.sbapp application. Try sending a zero divisor to each input stream in the following sequence, restarting the module as necessary:

  • Send a zero divisor to SafeDivisionInput, which causes the dividemodule.sbapp application to handle the error and output a tuple on CustomizedErrorOutput.

  • Send a zero divisor to DoCustomCodeDivision, which sends data through a custom Java function to perform the division. In general, StreamBase catches any exception thrown by embedded Java code, whether from an operator, adapter, or function. In this example, the division by zero causes an ArithmeticException to be thrown by the function, which is caught by the UnhandledErrors stream, which causes the server to halt. (See CustomFunctions.java for the source of this function.)

  • Send a zero divisor to UnsafeDivisionInput, which demonstrates how the uncaught errors inside dividemodule.sbapp make it to the module reference's error output port. Once again, the error is caught by the UnhandledErrors stream, which causes the server to halt.

Note

In the cases where errors were handled without a shutdown, ModuleErrorStreamOutput displays the raw error tuple that StreamBase generates for any error. The original tuple that caused the error is enclosed as a blob in the originalTuple field.

Advanced Exercise: Try switching Studio's Preferences to display blobs as ASCII to see what the orginalTuple blob looks like.

Additional Demonstration

As an exercise for further learning, open the outoforder.sbapp module. Run the module and send data through it manually to learn about using an Error Output Port with Aggregates.

It may be instructive to run these samples with intermediate stream dequeuing enabled so that you can use the Output Streams view to see the tuples that exit the error streams. See Intermediate Stream Dequeuing in the StreamBase Help system for instructions.

Conclusion

Error handling is a required portion of the development effort when developing mission critical, real-time production applications. Error ports and error streams provide a level of error-handling richness in EventFlow applications equivalent to that available in modern programming languages.

Running the Error Streams Unit Test

This sample includes a StreamBase JUnit test, DivideTest.java. To run it, locate DivideTest.java in the src/main/java folder, under the com.streambase.sample.test package. Right-click DivideTest.java, and select Runs As>StreamBase Unit test from the context menu.

The test will cause a division by zero, which prints a red warning message in the Console window. Since the error is caught by the error stream, the application continues to run and the test will pass. Click the JUnit view's tab to verify that both test cases have passed.

Sample Location

When you load the sample into StreamBase Studio, Studio copies the sample project's files to your Studio workspace, which is normally part of your home directory, with full access rights.

Important

Load this sample in StreamBase Studio, and thereafter use the Studio workspace copy of the sample to run and test it, even when running from the command prompt.

Using the workspace copy of the sample avoids permission problems. The default workspace location for this sample is:

studio-workspace/sample_errorstreams

See Default Installation Directories for the default location of studio-workspace on your system.