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
Copy file name to clipboardExpand all lines: _appnotes/interceptors.md
+41-33
Original file line number
Diff line number
Diff line change
@@ -10,36 +10,40 @@ A recurring question on OSGi forums is how to use _interceptors_. The [Spring fr
10
10
11
11
Let's say we have a function that performs some work. In the following example we need to do some "pre work", "post work", and additionally handle exceptions in similar way:
12
12
13
-
void doWork() {
14
-
preWork();
15
-
try {
16
-
… do work
17
-
postWork();
18
-
} catch( Throwable t) {
19
-
exceptionWork(t);
20
-
throw t;
21
-
}
13
+
```java
14
+
void doWork() {
15
+
preWork();
16
+
try {
17
+
… do work
18
+
postWork();
19
+
} catch( Throwable t) {
20
+
exceptionWork(t);
21
+
throw t;
22
22
}
23
-
24
-
void doOtherWork() {
25
-
preWork();
26
-
try {
27
-
… do other work
28
-
postWork();
29
-
} catch( Throwable t) {
30
-
exceptionWork(t);
31
-
throw t;
32
-
}
23
+
}
24
+
25
+
void doOtherWork() {
26
+
preWork();
27
+
try {
28
+
… do other work
29
+
postWork();
30
+
} catch( Throwable t) {
31
+
exceptionWork(t);
32
+
throw t;
33
33
}
34
+
}
35
+
```
34
36
35
37
The problem with this approach is that it creates a lot of boiler plate code, and the actual work gets lost. How can we get rid of this noisy and distracting code?
36
38
37
39
One possible solution is to use Interceptors. With this approach, you add an annotation and now the caller is ensuring that your annotation is correctly interpreted.
38
40
39
-
@Work(SOME_PARAMETER)
40
-
void doWork() {
41
-
… do work
42
-
}
41
+
```java
42
+
@Work(SOME_PARAMETER)
43
+
void doWork() {
44
+
… do work
45
+
}
46
+
```
43
47
44
48
Voila, boiler plate gone! Problem solved. Or is it?
45
49
@@ -76,9 +80,11 @@ However, we're many years further today then when that model was introduced, and
76
80
77
81
Since Java 8 we now have lambdas! (About 42 years after Smalltalk.) Lambdas are interceptors turned inside out. In the transaction composition problem the interceptor had to do something before it ran our code, then ran our code, and then handle any exceptions and do some post-processing. With lambdas, we can achieve the same model by the method calling the interceptor and passing the function.
78
82
79
-
void doWork() {
80
-
interceptor.doWork( () -> ... working );
81
-
}
83
+
```java
84
+
void doWork() {
85
+
interceptor.doWork( () ->... working );
86
+
}
87
+
```
82
88
83
89
That is, instead of typing an annotation above the method, you just use a method with that name and pass it parameters, one of the parameters being the function you want to check.
84
90
@@ -90,14 +96,16 @@ An extremely interesting example of this is the work being done on the [Transact
90
96
91
97
The underlying problem with transactions is that when you get called in a service oriented world it is not always clear how to _compose_ the transactions. I.e. join, reject if on is there, or start. This is a classical problem where you need to do something _before_ and _after_ your actual code. Therefore the transaction control service makes it look like:
92
98
93
-
@Reference Store<Person> persons;
94
-
@Reference TransactionController txc;
99
+
```java
100
+
@ReferenceStore<Person> persons;
101
+
@ReferenceTransactionController txc;
95
102
96
-
public Person findPerson( long id) {
97
-
return txc.required( () ->
98
-
persons.find( "select * from Person where id=%s", id )
99
-
);
100
-
}
103
+
publicPerson findPerson( long id) {
104
+
return txc.required( () ->
105
+
persons.find( "select * from Person where id=%s", id )
106
+
);
107
+
}
108
+
```
101
109
102
110
Therefore, with the same amount of code (or less) you do not need the magic interceptors.
Copy file name to clipboardExpand all lines: _qs/410-exercise-service.md
+17-11
Original file line number
Diff line number
Diff line change
@@ -25,9 +25,11 @@ In OSGi enRoute this means the name of the project should end with `.api` and yo
25
25
26
26
In this new project, we rename the template API to reflect our semantics. So rename the `com.acme.prime.api` package to `com.acme.prime.upper.api` and the `Prime.java` file to `Upper.java`. Then change the `Upper` class so that we can use it to change a word to upper case:
27
27
28
-
public interface Upper {
29
-
String upper(String input);
30
-
}
28
+
```java
29
+
publicinterfaceUpper {
30
+
Stringupper(Stringinput);
31
+
}
32
+
```
31
33
32
34
If you have another API in the future, you can then add it to the same project in another package.
33
35
@@ -56,20 +58,24 @@ The first thing we need to do is to make sure the Upper Application can see the
56
58
57
59
Then we change the `UpperApplication` component class. We must add a setter method for the `Upper` service with a `@Reference` annotation, which means we must import `org.osgi.service.component.annotations.Reference`. No we can add the reference to the end of the class (convention is to place references at the end):
58
60
59
-
public class UpperApplication implements REST {
60
-
...
61
+
```java
62
+
publicclassUpperApplicationimplementsREST {
63
+
...
61
64
62
-
@Reference
63
-
Upper upper;
64
-
}
65
+
@Reference
66
+
Upper upper;
67
+
}
68
+
```
65
69
66
70
The `@Reference` annotation creates a dependency on this service; the `UpperApplication` component is not started until the service registry contains an Upper service.
67
71
68
72
The next step is to use the `upper` instance variable that we've just set in the `getUpper` method (so we must import `com.acme.prime.upper.api.Upper` again).
69
73
70
-
public String getUpper(String string) {
71
-
return upper.upper(string);
72
-
}
74
+
```java
75
+
publicString getUpper(String string) {
76
+
return upper.upper(string);
77
+
}
78
+
```
73
79
74
80
If the OSGi framework is still running, you likely get errors since we now have an unresolved requirement in our code; we're referring to the `com.acme.prime.upper.api` package which is now not provided by anybody.
Copy file name to clipboardExpand all lines: _tutorial_base/300-api.md
+19-13
Original file line number
Diff line number
Diff line change
@@ -17,16 +17,18 @@ This section discuss an _API_ project. An API project can be used on the classpa
17
17
18
18
We want to make a component that evaluates expressions. Though we could start with the implementation, let's do it right and first define the contract. The service contract for now could be an interface:
19
19
20
-
package com.acme.prime.eval.api;
20
+
```java
21
+
packagecom.acme.prime.eval.api;
22
+
/**
23
+
* A service that evaluates an expression and returns the result
24
+
*/
25
+
publicinterfaceEval {
21
26
/**
22
-
* A service that evaluates an expression and returns the result
27
+
* Evaluate an expression and return the result.
23
28
*/
24
-
public interface Eval {
25
-
/**
26
-
* Evaluate an expression and return the result.
27
-
*/
28
-
double eval(String expression) throws Exception;
29
-
}
29
+
doubleeval(Stringexpression) throwsException;
30
+
}
31
+
```
30
32
31
33
Ok, it does not get a lot easier than this!
32
34
@@ -78,17 +80,21 @@ The box with rounded corners represents a bundle; the inside black box represent
78
80
79
81
You might not have noticed it but you actually semantically versioned this package as well (don't you love magic?). When you look in the `com.acme.prime.eval.api` package you will find the `package-info.java` file. Double clicking will show the contents. In this file we define the version of the package. If you make modifications to the package, you should always update the version in this file as well.
80
82
81
-
@org.osgi.annotation.versioning.Version("1.0.0")
82
-
package com.acme.prime.eval.api;
83
-
83
+
```java
84
+
@org.osgi.annotation.versioning.Version("1.0.0")
85
+
packagecom.acme.prime.eval.api;
86
+
```
87
+
84
88
### Provider & Consumer Types
85
89
86
90
In this API, any party that will implement the `Eval` interface is considered to be the _provider_. A provider must fully implement a contract that has virtually no backward compatibility unlike _consumers_ of this API. Any change in the version that affects the public API must result in rebuild of the provider's bundle. That is, if our version here goes to 1.1 we want to make sure our providers that implemented 1.0 are no longer compatible.
87
91
88
92
Obviously it is a nightmare to ensure that the proper version ranges are used. We can significantly help the provider by adding an annotation to this interface:
89
93
90
-
@ProviderType
91
-
public interface Eval { ... }
94
+
```java
95
+
@ProviderType
96
+
publicinterfaceEval { ... }
97
+
```
92
98
93
99
The bnd tool will now automatically ensure that any _implementers_ of this interface use semantic versioning to import the package with a minor range, for example `[1.0,1.1)`.
0 commit comments