You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Currently, there is no way expect for using a Java debugger to find out
how data is processed by a Metamorph script. This makes introspection of
Metamorph scripts rather difficult as in-depth knowledge of the Java
implementation of Metamorph is required. Additionally, no simple method
exists for tools building on Metamorph (such as script editors) to
interact with Metamorph scripts during their execution.
This commit solves these problems by modifying the Metamorph
implementation so that the user can add user-defined interceptor objects
to the processing pipeline constructed from a Metamorph script. The
interceptor objects are added before and after each processing step in
the script. Additionally, the user can introspect the `flushWith`-
mechanism. It is up to the implementor of the interceptor objects
whether invokations are simply watched (e.g. for logging) or actively
interfered with (e.g. for setting break points to interrupt data
processing at certain points).
The implementation of the interception system is based on factory
classes which create the interceptor objects. The interface for these
classes is defined in `InterceptorFactory`. Users can implement this
interface to create custom interceptor objects. Instances of an
interceptor factory implementation can be passed to the `Metamorph`
object during its construction. The factory is internally passed to the
`MorphBuilder` which is modified to add an interceptor object into each
link between data-elements, functions and collectors. It also wraps
every flush listener in an interceptor object.
The interceptor factory can return `null` when asked for creating an
interceptor. The `NullInterceptorFactory` implements this behaviour and
serves as a default implementation of `InterceptorFactory` which
creates a Metamorph pipeline without any interceptors.
Writing an interceptor factory is quite simple. First, we need to
implement the actual interceptor objects; then we only need to create
and return instances of these interceptors in the factory class. The
following example demonstrates how to implement interceptors which log
all data processed by Metamorph to standard out:
- The pipeline interceptors need to implement `NamedValuePipe`.
However, we do not implement this interface directly but use
`AbstractNamedValuePipe` as a base class. This class implements the
method required for connecting the named value pipe with the
pipeline so that we only have to implement the `receive` method
which is called whenever data is passed through the pipeline. In
this method we output the received data to standard out and pass the
data to the next pipeline element (if we did not do this we would
interrupt the pipeline):
```
class NamedValueLogger extends AbstractNamedValuePipe {
@OverRide
public void receive(final String name, final String value,
final NamedValueSource source, final int recordCount,
final int entityCount) {
System.out.println(name + ": " + value);
getNamedValueReceiver().receive(name, value, this,
recordCount, entityCount);
}
}
```
- The `flushWith`-interceptors need to implement `FlushListener`.
Interceptors for flush listeners wrap the actual flush listener.
We pass the original flush listener as a constructor argument to
our interceptor:
```
class FlushLogger implements FlushListener {
private static FlushListener wrappedListener;
public FlushLogger(final FlushListener wrappedListener) {
this.wrappedListener = wrappedListener;
}
@OverRide
public void flush(final int recordCount,
final int entityCount) {
System.out.println("Flushing: " +
wrappedListener.toString());
wrappedListener.flush(recordCount, entityCount);
}
}
```
- Now we need to write an interceptor factory for creating our
interceptor objects:
```
class LoggingInterceptorFactory implements InterceptorFactory {
@OverRide
public NamedValuePipe createNamedValueInterceptor() {
return new NamedValueLogger();
}
@OverRide
public FlushListener createFlushInterceptor(
final FlushListener listener) {
return new FlushLogger(listener);
}
}
```
- To use our interceptors we need to pass an instance of our factory
class to `Metamorph`:
```
final InterceptorFactory factory = new LoggingInterceptorFactory();
final Metamorph metamorph
= new Metamorph("morph-script.xml", factory);
```
0 commit comments