OSGi Community Event 2016 Presentation by Magnus Jungsbluth & Domagoj Cosic (Bundesdruckerei GmbH)
If you had to name a single great thing about OSGi, it would probably be its dynamics. Services come and go; other services react to those events, configuration can change and so on. Even the startup is dynamic: start levels are increased synchronously; however, configuration, Declarative Services, and Blueprint are started asynchronously after bundles turn active. We love that but sometimes you want to exercise control over when your application is actually fully started or more importantly when it is not. You certainly do not want your system to be accessible with a security module that threw an exception during startup. Unlike monolithic applications, an OSGi application behaves more like a distributed system that converges to a final state eventually.
We will show you a way to monitor startup of your application by creatively using some common OSGi mechanisms and demonstrate failure scenarios for common subsystems like configuration and Blueprint. We will also demonstrate the concept of start phases which are a higher-level concept on top of OSGi start levels. A phased start enables a higher level of security in the face of failures during startup.
The source code for the APIs and the reference implementation are available under Apache 2.0 license
3. 325.10.2016
Taming Startup Dynamics
BUT sometimes you need something predictable
System startup
Partial restart
Re-configurations that must succeed
Integration tests expect system in a stable state
4. 425.10.2016
Taming Startup Dynamics
We…
…operate national eID infrastructure
…operate a Trust Center on premises
…operate the Public Key Directory for the ICAO
5. 525.10.2016
Taming Startup Dynamics
What we want
Any error during startup ⇒ shutdown immediately
In any trade-off that has security on one side, favor that side
Open network ports iff all sub-systems report success
Unmanned reboots shouldn’t pose a threat
6. 625.10.2016
Example of what is broken
CountDownLatch latch = new CountDownLatch(1);
AtomicReference<Throwable> error = new AtomicReference<>();
framework.adapt(FrameworkStartLevel.class).setStartLevel(10,
new FrameworkListener() {
@Override
public void frameworkEvent(final FrameworkEvent event) {
if (event.getType() == FrameworkEvent.STARTLEVEL_CHANGED) {
latch.countDown();
} else if (event.getType() == FrameworkEvent.ERROR) {
error.set(event.getCause());
latch.countDown();
}
}
}
);
latch.await();
if (error.get() != null) {
System.out.println("SUCCESS!?");
// ... and what about DS + Blueprint
// + ConfigAdmin (Asynchronous after BundleEvent.ACTIVE) ?
}
7. 725.10.2016
How it could look like
AtomicBoolean systemStarted = new AtomicBoolean();
CountDownLatch latch = new CountDownLatch(1);
framework.adapt(FrameworkStartLevel.class).setStartLevel(10);
framework.registerService(SystemStartupListener.class,
new SystemStartupListener() {
@Override
public void systemStartedEvent(SystemStartedEvent event) {
systemStarted.set(event.getType() == SystemStartedEvent.SYSTEM_STARTED);
latch.countDown();
}
}
);
latch.await();
if (systemStarted.get()) {
System.out.println("SUCCESS!");
}
9. 925.10.2016
Parts of our solution (1)
StartupMonitor
Report state for one sub-system
SystemStartupController
Collect state and send events
SystemStartupListener
Receive live events or event playback
StartPhase
Higher level concept over start levels
11. 1125.10.2016
High level overview
Start Launcher
Framework starts to highest startlevel of phase 1
Phase 1 (wait for FrameworkEvent):
wait until all startup monitors signal success
increase start level to highest start level of next phase
...
Phase N (wait for FrameworkEvent):
wait until all startup monitors signal success
signal successful system startup
12. 1225.10.2016
Start Phases
SYSTEM
Handle low-level stuff, make sure db schemas are migrated, …
APPLICATION
Configure everything that makes the app run
ADMIN
Open network ports used for administrative purposes, start scheduler, thread pools
PUBLIC
Open up all public network ports
14. 1425.10.2016
Anatomy of a StartupMonitor (1)
public class MyStartupMonitor implements StartupMonitor<T> {
StartupMonitorState<T> prepareMonitoredItems(StartupPhase phase) {
unfinishedItems = //gather
erroneousItems = //empty set
latch = new CountDownLatch(unfinishedItems.size());
}
//code that does the monitoring and decrements latch on changes
StartupMonitorState<T> waitForResult(long timeout, TimeUnit unit) {
boolean finished = latch.await(timeout, unit);
return new MyState(finished, unfinishedItems, erroneousItems);
}
private class MyState implements StartupMonitorState<T> {
// cont. in the next slide
}
}
15. 1525.10.2016
Anatomy of a StartupMonitor (2)
private class MyState implements StartupMonitorState<T> {
...
void buildRepresentation(T item, StateRepresentationBuilder representation) {
representation
.show(item.getProperty()).reason(item.getCause())
.details().
.show(...).reason(...);
}
}
16. 1625.10.2016
Taming Startup Dynamics
Other gimicks:
Can wait on successful application of configuration (aka the missing ConfigurationEvent)
ManagedService(Factory) can define service dependencies (i.e. a DataSource) that depend on a
particular configuration property’s value (i.e. a datasource id)
Supervised shutdown
19. 1925.10.2016
Please note: This presentation is the property of Bundesdruckerei GmbH. All of the information contained herein may not be copied,
distributed or published, as a whole or in part, without the approval of Bundesdruckerei GmbH. Copyright 2016 Bundesdruckerei
GmbH
Disclaimer
Magnus Jungsbluth | Domagoj Ćosić
E-Mail: magnus.jungsbluth@bundesdruckerei.de | domagoj.cosic@bundesdruckerei.de
Telefon: +49 (30) 2598 – (3671|3635)