Skip to content

Commit

Permalink
Manual: Filter. #9
Browse files Browse the repository at this point in the history
  • Loading branch information
veblush committed Jun 10, 2016
1 parent c5ce41d commit 9ebbe40
Showing 1 changed file with 104 additions and 30 deletions.
134 changes: 104 additions & 30 deletions docs/Filter.md
Original file line number Diff line number Diff line change
@@ -1,44 +1,118 @@
** WRITING IN PROCESS **

## Filter

### Introduction
Filter are instances that run code before or after handling request, message and
notification. It allows you to log or manipulate specific messages.

<p align="center">
<img src="./Figure_Filter.png"/><br/>
*Figure: Filter run code before or after main handler.*
</p>

For example, folloing `MyActor` uses two filter.
One is `Log` filter to capture all requests, message and notifications.
The other is `Authroized` filter to restrict access to authorized users.

```csharp
[Log]
class MyActor : InterfacedActor, IGreeterSync, IAuthorizable
{
string IGreeterSync.Greet(string name) { ... }
[Authorized] int IGreeterSync.GetCount() { ... }
}
```

#### Factory

Before an actor starts, it initializes a filter chain of its handlers.
A filter factory creates filters for constructing a filter chain.

For example, previous `Log` filter is created by factory `LogAttribute`.
This factory will create a `Log` filter instance for every method of class and
you can keep `actorType` and `method` state and make use of them.

```csharp
class LogAttribute : Attribute, IFilterPerClassMethodFactory
{
public void Setup(Type actorType, MethodInfo method) { ... }
public IFilter CreateInstance() => new LogFilter(...);
}
```

There are 5 factory interfaces for controlling how a filter instance will be
created.

- `IFilterPerClassFactory`: Create a filter per a class
- `IFilterPerClassMethodFactory`: Create a filter per a method of class
- `IFilterPerInstanceFactory`: Create a filter per an instance
- `IFilterPerInstanceMethodFactory`: Create a filter per a method of an instance
- `IFilterPerInvokeFactory`: Create a filter for every invocation.

`IFilterPerClassFactory` and `IFilterPerClassMethodFactory` create a filter which
is shared by multiple instances and can be executed simultaneously.
Therefore, you may prepare race condition.

#### What and when

Filters can run code on request, notification, and message and can be executed
before or after those messages.
For example, a filter which implements `IPreRequestFilter` will be executed
before handling a request and a filter which implements `IPostNotificationFilter`
will be executed after handling a notification.

Filters can have multiple handlers. For example, `LogFilter` has 4 handlers to
log all possible messages.

- Filter can monitor and manipulate request and response.
- For example, when you need to log all requests and responses, filter can help you.
```csharp
class LogFilters : IPreRequestFilter, IPostRequestFilter, IPreMessageFilter, IPreNotificationFilter
{ ... }
```

### Factory
All filters support both synchronous and asynchronous implementations.
For example, `IPreRequestAsyncFilter` is an asynchronous one of `IPreRequestFilter`.

- Scope
- IFilterPerClassFactory: Create a filter per a class
- IFilterPerClassMethodFactory: Create a filter per a method of class
- IFilterPerInstanceFactory: Create a filter per an instance
- IFilterPerInstanceMethodFactory: Create a filter per a method of an instance
- IFilterPerInvokeFactory: Create a filter for every invocation.
#### Ordering

### Filter
Multiple filters can be executed for one message and execution order is based on
`Order` property of filter.

Events
- Pre or Post
- Request or Notification or Message
- For pre-filters, a filter that has the lowest order runs first.
- For post-filters, a filter that has the highest order rusn first.

Chain:
- Filter order
- F(1).Pre -> F(2).Pre -> ... -> Handler -> ... -> F(2).Post -> F(1).Post
- When one of pre-filters makes result, handler won't handle a request
- But other filters will handle request so it is required to check `Handled` property of request context.
For examples, if there are A filter whose order is 1 and B filter whose order is 2,
they run in order like:

#### Request Filter
```
A.Pre -> B.Pre -> Handler -> B.Post -> A.Post
```

- IPreRequestFilter, IPreRequestAsyncFilter
- IPostRequestFilter, IPostRequestAsyncFilter
#### Blocking handler

#### Notification Filter
When a pre-filter makes context `handled`, main handler is not executed.
For example, following `AuthorizedFilter` is a pre-request filter that runs
`OnPreRequest` method before main handler.
It checks whether request is from an authroized actor and stops handling if not.

- IPreNotificationFilter, IPreNotificationAsyncFilter
- IPostNotificationFilter, IPostNotificationAsyncFilter
```csharp
class AuthorizedFilter : IPreRequestFilter
{
void IPreRequestFilter.OnPreRequest(PreRequestFilterContext context)
{
if (context.Handled) return;

#### Message Filter
var actor = (IAuthorizable)context.Actor;
if (actor == null || actor.Authorized == false)
{
context.Response = new ResponseMessage
{
RequestId = context.Request.RequestId,
Exception = new InvalidOperationException("Not enough permission.")
};
return;
}
}
}
```

- IPreMessageFilter, IPreMessageAsyncFilter
- IPostMessageFilter, IPostMessageAsyncFilter
A thing you should know is that filters will execute code even if one of previous filters
set context `handled` unlike main handler.
Because of this, you may need to check `context.Handled` at filter handler.

0 comments on commit 9ebbe40

Please sign in to comment.