As a tool library that can run safely, in order to ensure the security of occupied resources, support for exception handling and final clean-up is indispensable. FunDA's data stream, FDAPipeLine, generally starts by reading database data to form data sources. In order to ensure that every data source can be used safely, FunDA provides a post-processing finalizing program interface to realize the data stream cleaning and error-handling program interface to capture and handle the abnormal situations in the use process. First, the finalizer ensures that the FunDA data stream terminates operations in any case: including element exhaustion, forced interruption and abnormal interruption, finalizer will be invoked. In this discussion, we will test and demonstrate FunDA Exception-handling and Final-cleanup. The following boilerplate code sets a static collection data source viewState and a dynamic data stream streamState:
val db = Database.forConfig("h2db")
implicit def toState(row: StateTable#TableElementType) =
StateModel(row.id,row.name)
val viewLoader = FDAViewLoader(slick.jdbc.H2Profile)(toState _)
val streamLoader = FDAStreamLoader(slick.jdbc.H2Profile)(toState _)
val stateSeq = viewLoader.fda_typedRows(StateQuery.result)(db).toSeq
val viewState = fda_staticSource(stateSeq)(println("***Finally*** the end of viewState!!!"))
val streamState = streamLoader.fda_typedStream(StateQuery.result)(db)(64,64)(println("***Finally*** the end of streamState!!!"))
In the code example above, we can see that fda_staticSource and fad_typedStream are linked to the post-processing program. We simply use println to represent a complete program to verify the call to the post-processing program. So the post-processing program is hooked when building view or stream. Let's first see if they are invoked at normal termination or forced interruption:
viewState.startRun
viewState.take(2).startRun
streamState.startRun
streamState.take(3).startRun
// ***Finally*** the end of viewState!!!
// ***Finally*** the end of viewState!!!
// ***Finally*** the end of streamState!!!
// ***Finally*** the end of streamState!!!
So if there is an exception interrupt, will it be called as well? Let's first design the following two user-defined functions:
def trackRows: FDAUserTask[FDAROW] = row => {
row match {
case m@StateModel(id,name) =>
println(s"State: $id $name")
println( "----------------")
fda_next(m)
case m@_ => fda_next(m)
}
}
def errorRow: FDAUserTask[FDAROW] = row => {
row match {
case StateModel(id,name) =>
val idx = id / (id - 3)
fda_next(StateModel(idx,name))
case m@_ => fda_next(m)
}
}
trackRows traces show the current data row, and errorRow artificially causes an exception in the third row. Let's test it with streamState:
streamState.appendTask(errorRow).appendTask(trackRows).startRun
// State: 0 Alabama
// ----------------
// State: -2 Alaska
// ----------------
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at examples.ExceptionsAndFinalizers$$anonfun$errorRow$1.apply(ExceptionsAndFinalizers.scala:46)
// ...
// at java.lang.Thread.run(Thread.java:745)
// ***Finally*** the end of streamState!!!
Indeed, after showing two rows of data normally, the third line interrupts and calls finalizer directly. This ensures that no matter what happens, when the data source is used, the programmer is given a space to do post-processing, such as releasing resources, interrupting connections, closing files, etc.
We can use onError to hook exception handlers, as follows:
val s = streamState.appendTask(errorRow).appendTask(trackRows)
val s1 = s.onError {case e: Exception => println(s"Caught Error in streamState!!![${e.getMessage}]"); fda_appendRow(FDANullRow)}
Note: onError must be attached to the end of the stream to ensure that any abnormal situation in all links can be correctly handled. Look at the results of the operation:
State: 0 Alabama
----------------
State: -2 Alaska
----------------
***Finally*** the end of streamState!!!
Caught Error in streamState!!![/ by zero]
The above example captures the exception and calls finalizer after the exception interrupt.
Sometimes we need to customize special situations, and we want to capture them. But at the same time, we hope that these situations will not interrupt the operation. First, we can customize an exception row type:
case class DivideZeroError(msg: String, e: Exception) extends FDAROW
Note: Never forget extends FDAROW. We modify the errorRow function above to a function that captures exceptions by itself:
def catchError: FDAUserTask[FDAROW] = row => {
row match {
case StateModel(id,name) =>
try {
val idx = id / (id - 3)
fda_next(StateModel(idx, name))
} catch {
case e: Exception => //pass an error row
fda_next(DivideZeroError(s"Divide by zero excption at ${id}",e))
}
case m@_ => fda_next(m)
}
}
trackRows must be modified to distinguish DivideZeroError rows:
def trackRows: FDAUserTask[FDAROW] = row => {
row match {
case m@StateModel(id,name) =>
println(s"State: $id $name")
println( "----------------")
fda_next(m)
case DivideZeroError(msg, e) => //error row
println(s"***Error:$msg***")
fda_skip
case m@_ => fda_next(m)
}
}
Operate the following program:
val s = streamState.take(5).appendTask(catchError).appendTask(trackRows)
val s1 = s.onError {case e: Exception => println(s"Caught Error in streamState!!![${e.getMessage}]"); fda_appendRow(FDANullRow)}
s1.startRun
The following results are produced:
State: 0 Alabama
----------------
State: -2 Alaska
----------------
***Error:Divide by zero excption at 3***
State: 4 Arkansas
----------------
State: 2 California
----------------
***Finally*** the end of streamState!!!
Process finished with exit code 0
There was no exception interrupt, the custom exception was captured and handled, and the post-processing program finalizer was called.
Here is the source code for this demonstration:
import slick.jdbc.H2Profile.api._
import com.bayakala.funda.samples.SlickModels._
import com.bayakala.funda._
import api._
import scala.language.implicitConversions
object ExceptionsAndFinalizers extends App {
val db = Database.forConfig("h2db")
implicit def toState(row: StateTable#TableElementType) =
StateModel(row.id,row.name)
val viewLoader = FDAViewLoader(slick.jdbc.H2Profile)(toState _)
val streamLoader = FDAStreamLoader(slick.jdbc.H2Profile)(toState _)
val stateSeq = viewLoader.fda_typedRows(StateQuery.result)(db).toSeq
val viewState = fda_staticSource(stateSeq)(println("***Finally*** the end of viewState!!!"))
val streamState = streamLoader.fda_typedStream(StateQuery.result)(db)(64,64)(println("***Finally*** the end of streamState!!!"))
/*
viewState.startRun
viewState.take(2).startRun
streamState.startRun
streamState.take(3).startRun
// ***Finally*** the end of viewState!!!
// ***Finally*** the end of viewState!!!
// ***Finally*** the end of streamState!!!
// ***Finally*** the end of streamState!!!
*/
def trackRows: FDAUserTask[FDAROW] = row => {
row match {
case m@StateModel(id,name) =>
println(s"State: $id $name")
println( "----------------")
fda_next(m)
case DivideZeroError(msg, e) => //error row
println(s"***Error:$msg***")
fda_skip
case m@_ => fda_next(m)
}
}
def errorRow: FDAUserTask[FDAROW] = row => {
row match {
case StateModel(id,name) =>
val idx = id / (id - 3)
fda_next(StateModel(idx,name))
case m@_ => fda_next(m)
}
}
case class DivideZeroError(msg: String, e: Exception) extends FDAROW
def catchError: FDAUserTask[FDAROW] = row => {
row match {
case StateModel(id,name) =>
try {
val idx = id / (id - 3)
fda_next(StateModel(idx, name))
} catch {
case e: Exception => //pass an error row
fda_next(DivideZeroError(s"Divide by zero excption at ${id}",e))
}
case m@_ => fda_next(m)
}
}
/*
streamState.appendTask(errorRow).appendTask(trackRows).startRun
// State: 0 Alabama
// ----------------
// State: -2 Alaska
// ----------------
// Exception in thread "main" java.lang.ArithmeticException: / by zero
// at examples.ExceptionsAndFinalizers$$anonfun$errorRow$1.apply(ExceptionsAndFinalizers.scala:46)
// ...
// at java.lang.Thread.run(Thread.java:745)
// ***Finally*** the end of streamState!!!
*/
/*
val v = viewState.appendTask(errorRow).appendTask(trackRows)
val v1 = v.onError {case e: Exception => println(s"Caught Error in viewState!!![${e.getMessage}]"); fda_appendRow(FDANullRow)}
v1.startRun
val s = streamState.appendTask(errorRow).appendTask(trackRows)
val s1 = s.onError {case e: Exception => println(s"Caught Error in streamState!!![${e.getMessage}]"); fda_appendRow(FDANullRow)}
s1.startRun
*/
val s = streamState.take(5).appendTask(catchError).appendTask(trackRows)
val s1 = s.onError {case e: Exception => println(s"Caught Error in streamState!!![${e.getMessage}]"); fda_appendRow(FDANullRow)}
s1.startRun
}