peaberry extends Guice to support injection of dynamic services. It provides OSGi integration out of the box and has plug-in support for other registry-based service frameworks.
This talk introduces peaberry and shows how it can be used to seamlessly blend both OSGi services and Eclipse extensions in the same application, as well as help switch between the two approaches.
2. Today's menu Origin of peaberry OSGi-fying Guice Service providers Service registry abstraction Blending services & extensions Future ideas
3. Origin Began as lab project at community site new annotations @Inject @OSGiService("(Currency=GBP)") StockQuote quote; patched Guice to support service “auto-binding” @OSGiService(...) StockQuote quote; // magically added for you bind(StockQuote.class)...
4. Simplification Auto-binding introduced too much magic @Inject @OSGiService("(Currency=GBP)") StockQuote quote; NO new annotations service bindings now explicit, just like pure Guice aim to be a true extension to Guice – no patches! so...
5. Squeezing Guice into a bundle Guice now has OSGi metadata thanks to Guice's type-safety meant no major classloading issues ... but AOP proxies initially didn't work in OSGi AOP proxies need to see client types AOP support types BND internal &
6. Bridge class loader Don't want to expose AOP internals (repackaged CGLIB) parent parent com.google.inject.internal.* loadClass loadClass so load proxy classes using “bridge” class loaders
7. Bridge class loader (2) No dependency on OSGi ! – only used when needed of bridge classloaders allows re-use ... as well as eager unloading of proxy classes BUT cannot apply bridging to package-private types as not visible from other classloaders weak cache
8. Why peaberry? Guice can now be used in OSGi – so what's missing? no support for dynamic OSGi services! each injector uses immutable set of explicit bindings so ... new Injector on every service change? or ... Provider<T> that returns dynamic proxies? :( :)
9. Service Provider @Inject StockQuote quote; quote.price(“JAVA”); get unget price injector get K P K P K P P Service Registry p r o x y p r o x y
10. Service binding Fluent API helps you get the right service Provider<T> bind( iterable( A.class ) ).toProvider( service( A.class ).multiple() ); @Inject A bestService; @Inject Iterable<A> allServices; // each element is a proxy import static org.ops4j.peaberry.Peaberry.service; import static org.ops4j.peaberry.util.TypeLiterals.iterable; * bind( A.class ).toProvider( service( A.class ).single() );
12. Service binding (2) So is that it? ... no, you can also service( A.class ).filter( /* apply a filter */ )... service( A.class ).in( /* query a specific registry */ )... service( A.class ).out( /* watch for service changes */ )... service( A.class ).decoratedWith( /* apply decoration */ )... each stage of the builder creates an immutable copy which means you can share and re-use builders
13. Service Registry public interface ServiceRegistry { <T> Iterable<Import<T>> lookup(Class<T> clazz, AttributeFilter filter); <T> void watch(Class<T> clazz, AttributeFilter filter, ServiceWatcher<? super T> watcher); } public interface AttributeFilter { boolean matches(Map<String, ?> attributes); } Pluggable API – integrate all kinds of service registries simple, allows lazines s z z z , no dependency on OSGi service filters not just limited to LDAP strings but we do provide an LDAP adapter (among others)
14. Import public interface Import<T> { T get(); // may throw unchecked ServiceUnavailableException Map <String, ?> attributes(); void unget(); boolean available(); } Tracking service use is very important ! public interface ImportDecorator<S> { <T extends S> Import<T> decorate(Import<T> service); } can easily apply deco to change dynamic behaviour (e.g. sticky services) ation to imported services
15. Concurrent Import public synchronized T get() { count ++; if ( null == service ) { final Iterator<Import<T>> i = services .iterator(); if (i.hasNext()) { service = i.next(); instance = service .get(); } } return instance ; } public synchronized void unget() { if (0 == -- count && null != service ) { final Import<T> temp = service ; instance = null ; service = null ; temp.unget(); } } Single services wrap iterables to look like single imports avoids thread-locals, provides basic service affinity
17. Export public interface Export<T> { void put(T instance); void attributes(Map<String, ?> attributes); void unput(); } Can alter/remove exported instance, update attributes public interface ServiceWatcher<S> { <T extends S> Export<T> add(Import<T> service); } watchers can receive imports and (re-)export them but wait, isn't a registry a bit like a watcher?
18. Service Registry (revisited) public interface ServiceRegistry extends ServiceWatcher<Object> { // etc... public interface Export<T> extends Import<T> { // etc... which leads to interesting possibilities ... Yes, and exports are also related to imports // like pushing services from one registry to another for (Import<A> i : extensionRegistry.lookup(A.class, null)) { serviceRegistry.add(i); } but you don't have to use the raw API to export
19. Service binding (revisited) Exported service handles can be injected bind( export( A.class ) ).toProvider( service( (A)null ).export() ); @Inject Export<A> exportedService; import static org.ops4j.peaberry.Peaberry.service; import static org.ops4j.peaberry.util.TypeLiterals.export; * bind( export( A.class ) ).toProvider( service( aImpl ).export() ); otherwise published when export handle is created can defer publishing a service by passing in null
20. Eclipse extensions Eclipse has its own registry for plug-in this is like another service registry, but less dynamic extensions so ... how can we map the service class to an instance? take inspiration from Eclipse Riena, use bean mapping ! unless its a compatible executable extension OR we're looking up IconfigurationElement class
21. Mapping extensions Use @MapName, @MapContent from peaberry or Riena Same approach as Riena for mapping bean types But use @ExtensionBean to configure point id @ExtensionBean("examples.menu.items") public interface Item { String getLabel(); @MapName("label") String toString(); @MapContent String getContent(); }
23. Blending services and extensions binder.install(osgiModule(context, eclipseRegistry())); peaberry 1.1-rc2 lets you combine registries Clients can continue to use the same injection points @Inject StockQuote quote; YOU can choose where the service comes from or even whether to use a dynamic service at all ! service proxies will then query both OSGi and
24. Summary Guice can now be used in OSGi peaberry adds dynamic services to Guice model easy to switch between services and non-services can mix'n'match coming soon: lifecycles, configuration support ... like OSGi services and Eclipse extensions different service registries