blob: 3d6e36f8e4c29a008c87c7d012c15dc0b5a0f40b [file] [log] [blame]
//////////////////////
* Copyright (c) 2007-2013, Niclas Hedhman. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied.
* See the License for the specific language governing permissions
* and limitations under the License.
//////////////////////
[[core-api-concern,Concern]]
= Concern =
Concerns are the equivalent of "around advice" in Aspect Oriented Programming. They are chained into an invocation
stack for each Mixin Type method and invoked after the Constraints have been executed. Since they are sitting "around"
the Mixin implementation method, they also have a chance to modify the returned result, and even skip calling the
underlying Mixin method implementation altogether.
To create a concern, you need to create a class that,
* implements the Mixin Type (Typed Concerns) or java.lang.reflect.InvocationHandler (Generic Concerns),
* extend ConcernOf (Typed Concerns) or GenericConcern (Generic Concerns) [1]
You are allowed to modify both the in-arguments as well as the returned value, including throw exceptions if that is
suitable, perhaps for post condition checks.
== Typed Concern ==
As mentioned above, concerns that implements the _Mixin Type_ are called *Typed Mixins*. They are more common in the
business domain, and can be used for many important things in the domain model, such as checking post conditions (i.e.
ensure that the state in the entire composite is valid), coordinating services, handling events and much more.
Typed Concerns doesn't have to implement all the methods in the Mixin Type. By making the class abstract and only
implementing the methods of interest, Zestâ„¢ runtime will subclass the concern (otherwise not valid for the JVM), but the
generated methods will never be invoked.
== Generic Concern ==
In classic AOP, all advice are effectively _generic_. There is no type information in the advice implementation and the
pointcut can be defined anywhere in the code, and the implementation uses proxy InvocationHandlers. Zestâ„¢ supports this
construct as well, and we call it *Generic Concern*.
Generic Concerns will be added to all methods that the AppliesToFilter evaluates to true. By default, that is all methods.
AppliesToFilters is a mechanism to limit, or direct, which methods that the concern should be added to. You have full
control over this selection process, via several mechanisms.
* @AppliesTo annotation can be put on the concern, with either;
* an interface for which the methods should be wrapped, or
* an AppliesToFilter implementation that is consulted during building the invocation stack, or
* an annotation type that must be given on the method.
* Concerns are added only to composites that declares the Concern, either in
* the Composite Type, or
* during assembly in the withConcerns() method.
This means that we can make the following three samples of concerns. First the generic concern that is added to the methods
of the JDBC Connection class;
[snippet,java]
--------------
source=core/api/src/test/java/org/qi4j/api/concern/DocumentationSupport.java
tag=class
--------------
We can also use an AppliesToFilter to define which methods should be wrapped with the concern, like this;
[snippet,java]
--------------
source=core/api/src/test/java/org/qi4j/api/concern/DocumentationSupport.java
tag=filter
--------------
And finally an example of how to use annotations to mark indvidual methods for being wrapped by the concern.
[snippet,java]
--------------
source=core/api/src/test/java/org/qi4j/api/concern/DocumentationSupport.java
tag=annotation
--------------
NOTE: Even if a method fulfills the requirement for the concern, if the concern is not declared for the Composite then the concern will NOT be applied.
== Invocation Order ==
The concerns are invoked AFTER all <<core-api-constraint>> have been checked. The concerns are executed before the
<<core-api-sideeffect>> are executed in the return path.
The order of execution is defined by the declaration order, interface hierarchy, whether the concern is generic or typed
and if they are declared in the interface or declared in the <<core-bootstrap-assembly>>.
From the perspective of incoming call, i.e. after the <core-api-constraint>> have been checked, the following rules
are in place;
* Typed concerns are invoked AFTER Generic concerns.
* Concern declared to the LEFT are executed BEFORE concerns to the RIGHT.
* Concerns in subclasses are executed BEFORE concerns in super-interfaces.
* Concerns in super-interfaces are executed breadth BEFORE depth.
* Concerns in different super-interfaces at the same "level" are executed with the concerns declared in super-interfaces left of other super-interfaces first. (TODO: Strange explanation)
* Concerns declared in interfaces are executed AFTER concerns declared in <<core-bootstrap-assembly>>.