Go to new doc!

+49 228 5552576-0


info@predic8.com

Message Interceptors

The architecture of Membrane Service Proxy is open for extension. To add functionality or to change the behavior, an interceptor can be plugged into the flow of messages. When a message flows through Membrane, each interceptor gets a chance to ...

During normal processing, an interceptor gets called twice for every exchange: The router invokes the handleRequest method of the interceptor first and passes a reference to an exchange object to the interceptor. At this time, the exchange object contains a request message only. Now the interceptor can access, read and modify the request. The handleRequest method can return

On the way back, the Exchange takes either the router through an interceptor's handleResponse or its handleAbort, depending on whether an exception has been caught from further down the chain or not.

If handleResponse returns Outcome.ABORT, an exception is immediately thrown.

http-server-handler-ok.png

Figure1: The standard flow of an Exchange through Membrane Service Proxy

In the standard flow, the HTTPServerHandler accepts an incoming HTTP request and creates an Exchange object for the request. The Exchange object passes through interceptors 1, 2 and 3. Each of these interceptors might read and/or modify the HTTP request. They all return Outcome.CONTINUE and processing continues down the chain. Within the HttpClientInterceptor's handleRequest method, the request is then forwarded to its destination (probably some other HTTP server) and the HTTP response is associated with the Exchange. The HttpClientInterceptor's handleRequest method then returns Outcome.RETURN, which reverses the direction of flow.

The exchange object is then passed back through the interceptor 3, 2 and 1's handleResponse methods, which might read and/or modify the HTTP response. Finally, the HTTPServerHandler sends the HTTP response back to the caller.

http-server-handler-abort.png

Figure2: The Exchange flow in case of an error

In case one of the handleRequest or handleResponse methods returns Outcome.ABORT, an exception is immediately thrown. This (or any other) exception is catched at the next upper level. In this case, processing continues back up by using the handleAbort methods.

This allows for special error handling to take place. Note that the Exchange object might not have a response object associated with it when handleAbort is called. (For example when a ConnectException was caught within the HttpClientInterceptor, because the destination HTTP server could not be reached.)

Lifecycle

An interceptor is usually instanciated once per chain. This means that the handle* methods have to be thread-safe with respect to the interceptor's instance. They do not have to be thread-safe with respect to the Exchange, as the Exchange is handled by one thread only (and as long as it has not yet entered an ExchangeStore).

The instanciation can be configured in Membrane's proxies.xml Spring configuration file or can be done by the Java API. A per-exchange lifecycle can be achieved by attaching properties to the Exchange object:

public Outcome handleRequest(Exchange exc) throws Exception {
	exchange.setProperty("myobject", new MyObject());
	return Outcome.CONTINUE;
}

public Outcome handleResponse(Exchange exc) throws Exception {
	MyObject o = (MyObject) exchange.getProperty("myobject");
	return Outcome.CONTINUE;
}

In the handleResponse method you get back the object that was put there for the corresponding request.

Interceptor Interface

Listing 1 shows the interceptor Interface.

public interface Interceptor extends XMLElement {

	public Outcome handleRequest(Exchange exc) throws Exception;
	public Outcome handleResponse(Exchange exc) throws Exception;
	public void handleAbort(Exchange exchange);

	public String getDisplayName();
	public void setDisplayName(String name);

	public String getId();
	public void setId(String id);

    public void setFlow(EnumSet<Flow> flow);
    public EnumSet<Flow> getFlow();
}
Listing 1: Interceptor Interface (shortened)
The methods handleRequest and handleResponse are called during the normal processing of an exchange. The property displayName is a human-readable desciption that is displayed in the GUI of the Membrane Monitor project that is based on the Membrane Service Proxy.

Invocation of Interceptor Methods

Each request

1. acquires, as handleRequest() calls of interceptors return Outcome.CONTINUE, a stack of named interceptors.

2. When the request reaches the end of the chain (in most cases this is the HttpClientInterceptor, which forwarded the request to some other HTTP server), each interceptor from the stack gets popped and handleResponse() is called.

The "end of the chain" is simply the first interceptor whose handleRequest() returned Outcome.RETURN instead of Outcome.CONTINUE. This last interceptor is not pushed onto the stack.

(In case interceptor.getFlow() returns RESPONSE (in which case handleRequest() should not be called for this interceptor), the call to handleRequest() is not executed during step 1, but treated as returning Outcome.CONTINUE.)

If an interceptor's handleRequest() or handleResponse() returns Outcome.ABORT, an AbortException is thrown.

If an exception is caught (during steps 1 or 2), each interceptor remaining on the stack is popped and its handleAbort() method is called. After any handleAbort()s have been called, the original exception is rethrown. If handleAbort() throws an exception, it is logged and ignored (processing continues with the next interceptor on the stack).

When the exception is then re-caught in the handler (AbstractHttpHandler) and the exchange does not yet have a response set, a generic error response is used. I/O-related exceptions (even when not related to the incoming connection) cause the connection to be closed. Any other exceptions are being logged and processing continues as usual: the response is sent to the client; then the next request on this connection is handled.