1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
|
---
title: Using Declarative Services
order: 74
layout: page
---
[[using-declarative-services]]
Using declarative services
--------------------------
Declarative Services (DS) are very common to define OSGi services. The
DS bundle scans all bundles (extender pattern), parses the component
definition xml-file and provides services based on that information. DS
may also be used to define references to other services. For instance
service A may require 0:n services of type B. The DS runtime will
ensure, that they are properly injected into the service A. References
to other services also influence the lifecycle of a service. For
instance service A requires 1:1 of service C. Thus service A will not be
activated before service C is injected properly. This short overview of
OSGi-DS is enough to understand the example defined below.
[[setup-example]]
Setup example
~~~~~~~~~~~~~
To follow my explanation
* clone repository from
https://github.com/lunifera/lunifera-Vaadin-examples.git
* use Import -> "Existing Maven Projects" in Eclipse IDE (Make sure that
m2e is installed)
* expand `org.lunifera.example.Vaadin.osgi.bootstrap.ds/setup` and set
`targetDS.target`
* `open targetDS.target`
** wait until resolved
** if error, then select all repository in target and press update
button on the right side
** wait until resolved
** press "set as target platform"
* Now there should be no problems.
* To build the project use `mvn clean verify`
You will recognize that the bundle does not contain an `Activator`. Thats
not necessary since we use OSGi services managed by OSGi-DS. The
component runtime of DS manages the lifecycle of the services. Instead
of an activator we are using the class `ServiceComponent`. It contains all
logic to wire things together properly.
[[servicecomponent]]
ServiceComponent
~~~~~~~~~~~~~~~~
The service component will become instantiated by OSGi DS and DS
controls its lifecycle. If the bundle containing the class is stopped,
the service will be deactivated by invoking `deactivate()`. If mandatory
references can be resolved, the service will be activated automatically. The `bindService` and `unbindService` are invoked by DS,
if a http service becomes available or unavailable. We do not need to
use a `ServiceTracker` anymore to get notified about the
`HttpService`-lifecycle. All that stuff is handled by OSGi-DS.
[source,java]
....
package org.lunifera.example.Vaadin.osgi.bootstrap.ds;
import javax.servlet.ServletException;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.http.HttpService;
import org.osgi.service.http.NamespaceException;
/**
* The service will look for the HttpService and registers the Vaadin servlet at
* it.
*/
public class ServiceComponent implements BundleListener {
private HttpService httpService;
private ResourceProvider resourceProvider;
/**
* Called by OSGi DS if the component is activated.
*
* @param context
*/
protected void activate(ComponentContext context) {
handleStartedBundles(context);
context.getBundleContext().addBundleListener(this);
}
/**
* Called by OSGi DS if the component is deactivated.
*
* @param context
*/
protected void deactivate(ComponentContext context) {
context.getBundleContext().removeBundleListener(this);
resourceProvider = null;
}
/**
* Binds the http service to this component. Called by OSGi-DS.
*
* @param service
*/
protected void bindHttpService(HttpService service) {
httpService = service;
try {
// register the servlet at the http service
httpService.registerServlet("/", new SimpleVaadinServlet(), null,
getResourceProvider());
} catch (ServletException e) {
e.printStackTrace();
} catch (NamespaceException e) {
e.printStackTrace();
}
}
/**
* Unbinds the http service from this component. Called by OSGi-DS.
*
* @param service
*/
protected void unbindHttpService(HttpService service) {
// unregister the servlet from the http service
httpService.unregister("/");
}
....
If a http service is available, it becomes injected and will be used to
register the Vaadin servlet at it. If it becomes unbound (bundle
containing the http service stopped), the servlet will be unregistered.
[[usecase-study]]
Usecase study
~~~~~~~~~~~~~
Imagine the following usecase. There are 2 bundle providing http
services.
* `org.abc.http.jetty`
* `org.abc.http.tomcat` (can be achieved using virgo for instance)
[[what-you-may-do...]]
What you may do...
^^^^^^^^^^^^^^^^^^
* Start the jetty bundle → then jetty-httpService will be bound to our
service component and Vaadin is running on a jetty
* Start the tomcat bundle → nothing will happen so far (service
component requires 0:1 http services - see below)
* Stop the jetty bundle → The jetty-httpService will become unbound and
Vaadin stops
* Some milliseconds later the tomcat-httpService will be bound
automatically → Vaadin will become installed to the tomcat
* Update the jetty bundle in the running OSGi environment (new bundle
with latest version is installed and old uninstalled)
* Start the jetty bundle (with the new version) again
* Stop tomcat bundle → The tomcat-httpService will become unbound and
Vaadin stops
* Some milliseconds later the jetty-httpService will be bound
automatically → Vaadin will become available at jetty
That’s real modularity... Give it a try and play around. Indeed, you
won't write your own http services. But there are a lot of other use
cases too. I will blog about them later when I am talking about "Content
Provider by OSGi DS".
[[servicecomponent-definition]]
ServiceComponent-Definition
~~~~~~~~~~~~~~~~~~~~~~~~~~~
The service component definition is the description about the service.
It defines the implementation class, the provided services and the
referenced (required) services. Eclipse PDE comes with an editor to
define them. Expand the `OSGI-INF` folder in the bundle and double click
`VaadinComponent.xml`. Now you see the definition of the service
component.
[source,xml]
....
<?xml version="1.0" encoding="UTF-8"?>
<scr:component xmlns:scr="http://www.osgi.org/xmlns/scr/v1.1.0" name="org.lunifera.example.Vaadin.osgi.bootstrap.ds">
<implementation class="org.lunifera.example.Vaadin.osgi.bootstrap.ds.ServiceComponent"/>
<reference bind="bindHttpService" cardinality="0..1" interface="org.osgi.service.http.HttpService"
name="HttpService" policy="dynamic" unbind="unbindHttpService"/>
</scr:component>
....
* Line 2 defines the name of the service. Feel free to insert a unique
name
* Line 3 defines the class name of the service class that needs to
become instantiated
* Line 4 defines a reference to a required service - the HttpService
* *bind* means the method that is called to bind the HttpService
instance to the service instance
* *unbind* means the method that is called to unbind the HttpService
instance from the service instance
* *cardinality* defines how many services may / must be bound - 0..1,
1..1, 0..n, 1..n
* *interface* is the name of the service that should be bound
A *very important* issue is an entry in the `MANIFEST.mf`. Using the
manifest header `Service-Component: OSGI-INF/*.xml` all xml files from
OSGI-INF are registered as component definitions to the DS runtime. If
you miss to add this statement, DS will never resolve your service!
[[run-example]]
Run example
~~~~~~~~~~~
To run the example, we need to prepare an OSGi-launch-configuration. The
following bundles are required to run the example properly. In
difference to part 1, the `org.eclipse.equinox.ds` and
`org.eclipse.equinox.util` bundles are required. Otherwise OSGi-DS will
not become started.
[cols=",,",options="header",]
|============================================================
|bundle |start level |autostart
|org.lunifera.example.Vaadin.osgi.bootstrap.ds |default |true
|com.Vaadin.client-compiled |default |false
|com.Vaadin.server |default |false
|com.Vaadin.shared |default |false
|com.Vaadin.shared.deps |default |false
|com.Vaadin.themes |default |false
|javax.annotation |default |false
|javax.servlet |default |false
|org.apache.felix.gogo.command |default |false
|org.apache.felix.gogo.runtime |default |false
|org.apache.felix.gogo.shell |default |false
|org.eclipse.equinox.console |default |false
|org.eclipse.equinox.ds |1 |false
|org.eclipse.equinox.http.jetty |default |false
|org.eclipse.equinox.http.servlet |default |false
|org.eclipse.equinox.util |default |false
|org.eclipse.jetty.continuation |default |false
|org.eclipse.jetty.http |default |false
|org.eclipse.jetty.io |default |false
|org.eclipse.jetty.security |default |false
|org.eclipse.jetty.server |default |false
|org.eclipse.jetty.servlet |default |false
|org.eclipse.jetty.util |default |false
|org.eclipse.osgi |default |false
|org.eclipse.osgi.services |default |false
|org.json |default |false
|org.jsoup |default |false
|============================================================
To start a jetty server on a proper port, use the VM argument:
`-Dorg.osgi.service.http.port=8082` in your launch configuration. Now
you can access the Vaadin page under http://localhost:8082. Have fun!
By http://de.gravatar.com/florianpi[Florian Pirchner] - based on
lunifera.org - OSGi components for business applications
|