cocoon-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Daniel Fagerstrom <dani...@nada.kth.se>
Subject [RT] OSGi based blocks
Date Tue, 14 Mar 2006 22:09:24 GMT
I have worked on implementing the blocks framework in terms of OSGi for 
some time. Not everything is working yet, but I think it is time to 
start discussing the involved ideas.

As already discussed I have refactored the blocks fw to reuse as much as 
possible form the servlet APIs. While studying OSGi R4 and especially 
the new declarative services (DS), I have seen that much of what we want 
to accomplish with blocks already is solved in OSGi, although sometimes 
in a different way.

I have experimented with implementing the blocks fw in terms of DS, and 
am quite happy with the results. The new architecture is less monolithic 
than the previous one and there is not much code at all, most of it is 
OSGi based design patterns.

                       --- o0o ---

So, now over to the actual design. As before we have a number of 
different parts (not everything implemented yet):

* A dispatcher - where the blocks are mounted with their respective URI 
prefixes.

* Inter block component management - a block can make its components 
available to other blocks, and use components from other blocks.

* Component manager bridges- the components within a block can be setup 
by our ECM/Spring container or maybe by some other kind of container.

* Inter block communication and polymorphism - a sitemap, and more 
generally a servlet, can call servlets in other blocks. And there is 
also inheritance so that one sitemap (or servlet) can extend another one.

* Dynamic deployment and configuration - the blocks can be installed, 
updated, reconfigured and removed dynamically while the rest of the 
framework is executing.

In the rest of the RT we will step through these parts and look at the 
design. But first a little bit about DS, that is used everywhere in the 
design and replaces block.xml among other things in the current design.

Declarative Services
====================

The declarative services simplify service (component) handling in OSGi 
by making it possible to declare services and their dependencies in an 
XML file (before service management wasn't configuration file based in 
OSGi).

The DS supports setter injection. Compared to Spring it is rather small 
and primitive. The cool thing about it is that it support dynamic 
dependencies. A dynamic dependency can have both a setter and an un-setter.

The framework also takes care of stopping and starting non-dynamic 
components when their dependencies comes and goes.

The DS can be used together with a configuration service that can 
override the default configurations in the DS XML file. The 
configuration service can take part of the responsibility that the 
wiring file and deployer takes in the current architecture.

There is not much documentation for the DS yet, so the main references 
are the specification [1] and the documentation of the service binder 
[2] that was a predecessor of DS.

The dispatcher
==============

The role of the dispatcher is that the blocks (servlets) are mounted in 
it based together with their URI prefixes. The dispatcher then call the 
blocks based on the incoming URIs. This is already handled by the OSGi 
HTTP service which provides a service that a servlet can lookup and 
register it self in.

A HTTP service implementation normally contains a HTTP server. But an 
alternative for embedding OSGi managed servlets in a servlet container 
is to use a HTTP service that acts as a bridge to the embedding servlet 
container [3].

It is also inconvenient to require the servlets to lookup the HTTP 
service. It is better to use the whiteboard pattern [4], and just 
register the servlets as services and have a component that reacts on 
the appearance and disappearance of servlet services, and register and 
unregister them in the HTTP service respectively.

Using DS a declaration (which is referred to by the Service-Component 
property in the manifest file) for a "whiteboard" adapter can look like [5]:

   <scr:component name="cocoon.activator">
     <scr:implementation class="org.apache.cocoon.blocks.osgi.Activator"/>
     <scr:reference name="LOG"
                    interface="org.osgi.service.log.LogService"
                    bind="setLog"/>
     <scr:reference name="HTTP"
                    interface="org.osgi.service.http.HttpService"
                    bind="setHttpService"/>
     <scr:reference name="Servlet"
                    interface="javax.servlet.Servlet"
                    cardinality="0..n"
                    policy="dynamic"
                    bind="setServlet"
                    unbind="unsetServlet"/>
   </scr:component>

which activates the class o.a.c.blocks.osgi.Activator [6] by calling its 
  "void activate(ComponentContext)" method if there is one. We can also 
see that the declaration refers to other services. It will not be 
activated until all its references are fulfilled. In this case it 
require a log service and an HTTP service to be present, and will insert 
these into the Activator instance by using its setLog and setHttpService 
methods.

The servlet reference is more interesting, it is dynamic an it has 
cardinality 0..n. This means that the activator can be connected to many 
servlets and that they can come and go dynamically. Each time a servlet 
service appears the setServlet method is called with it and each time 
one disappear the unsetServlet method is called.

The name attribute for the references are used for allowing service 
manager style lookup using the name, within the component. The service 
manager can be get through the ComponentContext.

A Servlet Service
-----------------

A bundle that provides a servlet, can register it as a service with a 
declaration like [5]:

   <scr:component name="cocoon.servlet3">
     <scr:implementation class="org.apache.cocoon.blocks.osgi.TestServlet"/>
     <scr:service>
       <scr:provide interface="javax.servlet.Servlet"/>
     </scr:service>
     <scr:property name="path" value="/test3"/>
   </scr:component>

compared to the whiteboard adapter we can see some new things, here we 
provide a service to the framework and it can be refered to by the name 
"cocoon.servlet3" (we should use a better naming scheme, I just adapted 
some examples from the specification while implementing the above).

The declaration also contains a property: path=/test3, that is looked up 
by the whiteboard adapter and used for mounting the servlet at that URI 
context.

                       --- o0o ---

This far we can see that by using what OSGi implementations already 
contain and some minimal glue code [6], we get the possiblity to 
dynamically register (and unregister) servlets within a webapp.

In the next step we will see how these servlets can share (dynamic) 
components.

Component Management
====================

Actually we already have inter block component management from OSGi. A 
bundle (block) can provide components by declaring them as services and 
it can depend on other components, possibly from other bundles by 
declaring them as references.

More specifically, for a servlet to depend on some components, we can 
add a number of set (and unset if we want dynamism) methods to it, and 
add the corresponding references in its declaration.

So, by just using OSGi, we get much of what the block architecture is 
intended for: dynamic component handling, packaging of servlets 
(sitemaps), sharing of components between blocks.

Component Manager Bridges
=========================

While DS is neat, it is not as flexible and powerful as Spring and we 
still have our legacy of Avalon components to take care of.

To create a bridge between OSGi services and Spring or Avalon component 
management we need two kind of adapters:

* An OSGi service to ServiceManager (or BeanFactory) adapter. This 
adapter just implement ServiceManager (or BeanFactory) and lookup the 
components OSGi services. It could be registered as an OSGi service it 
self and refered to by other components that needs a ServiceManager. We 
can even get dynamism by creating the adapter with DS and explicitly 
list the services that it should be able to provide, as references.

* A Spring component manager to OSGi services adapter. This adapter 
register all the components that is created by the Spring container as 
services. By letting the Spring container have a OSGi service to 
BeanFactory adapter as parent component manager, the Spring component 
manager can use components from other blocks as well, while creating new 
components.

We have already implemented this kind of bridge for ECM++ [7]. Now we 
need to implement it for the new Spring based container.

Inter Block Communication
=========================

The servlets (sitemaps) in the different blocks need to be able to call 
each other. Also it simplifies reuse of blocks if one block can extend 
another one (or rather that a servlets in one block can extend a servlet 
in another one). This is achieved with the block protocol.

One way of thinking about the inter block communication is to consider 
the servlet in the block to be embedded in an own container where the 
the servlets of the other blocks are available through the servlet 
context. This is the way I have implemented it, so other servlets can be 
called through the getNamedDispatcher method of the servlet context, 
with the block name as argument.

The implementation of calls to super blocks and polymorphism requires 
the use of a call stack, see [8] for details.

Block properties are accessed as servlet config (and context) init 
parameters.

In the OSGi implementation there is a BlockServlet that sets up the the 
communication with other blocks and creates the context that the servlet 
of the own block is executed within. A declaration of a BlockServlet 
might look like:

   <scr:component name="cocoon.blockServlet2">
     <scr:implementation 
class="org.apache.cocoon.blocks.osgi.BlockServlet"/>
     <scr:service>
       <scr:provide interface="javax.servlet.Servlet"/>
     </scr:service>
     <scr:property name="path" value="/test2"/>
     <scr:property name="attr" value="bar"/>
     <scr:reference name="blockServlet"
                    interface="javax.servlet.Servlet"
                    target="(component.name=cocoon.servlet2)"/>
     <scr:reference name="block1"
                    interface="javax.servlet.Servlet"
                    target="(component.name=cocoon.blockServlet1)"/>
   </scr:component>

Here we can see that we provide a service with the identifier 
"cocoon.blockServlet2" that is implemented by the mentioned BlockServlet 
and implements Servlet, it is mounted on the path "/test2". So the 
"whiteboard" part of the dispatcher described above, will take care of 
installing this block servlet in the HttpService of the framework.

The servlet reference with the special name "blockServlet" (should find 
a less confusing name) refer to the servlet that is embedded by the 
BlockServlet. The embeded servlet could e.g. be a sitemap servlet, and 
it could get the components it needs through the mechanism described in 
the sections about component management above.

The "target" attribute in a reference can contain constraints on what 
service that is refered to. The constraint 
"(component.name=cocoon.servlet2)" means that we want the particular 
servlet that is registered under the name "cocoon.servlet2". The 
constraint lanuage is the same as is used in LDAP, and it is possible to 
  create rather complex constraints if needed.

We can also see that there is a reference to a block servlet with the 
identifier "cocoon.blockServlet1", it will be made available through the 
  servlet context for "cocoon.servlet2" with the getNamedDispatcher 
method  using the name "block1".

All the properties (path and attr) are made available as init parameters 
in the servlet context for "cocoon.servlet2".

As we can see, the above DS configuration of a block servlet take care 
of most of what is configured in block.xml in the current block 
architecture.

The Block Protocol
------------------

OSGi have an URL service that make it possible to dynamically add 
protocols that are available through java.net.URL, much like the 
Excalibur sources. I have reimplemented the block source as an 
URLConnection that is registered as a protocol and can be used like in 
[9] (still buggy code).

Deployment
==========

I have not thought that much about deployment of OSGi based blocks, and 
assume Reinhard will have ideas about that.

For packaging the OSGi framework together with needed service bundles 
and an init configuration it is best to see what the Felix and 
Eclipse/Equinox communities have come up with.

Most OSGi implementations provide both telnet and http based consoles 
for installing, starting and stopping bundles. Deploy time configuration 
(wiring.xml) seem to have less well developed tool support.

There is a configuration service that can be used for deploy time 
configuration. With the configuration service one can override the 
properties and target attributes for references that is given in the DS 
files in the bundles.

The wiring.xml could be used for setting up the configuration service.

Conclusion
==========

It should be noted that I haven't referred to any Cocoon specifics 
above. That is one of the neat things about the architecture. It is 
completely orthogonal to and independent of the rest of Cocoon and it 
could be used together with any servlet based web framework.

It is obviously not exactly as the block architecture that was designed 
a couple of years ago. But it is rather close and by reusing so much of 
OSGi we get a rather small implementation that can be interesting for 
other communities. Much of the surrounding infrastuture will be needed 
by other OSGi users, so much can be developed together with other 
communities.

WDYT?

/Daniel


References
==========

[1] OSGI R4 specification 
http://www.osgi.org/osgi_technology/download_specs.asp?section=2
[2] Service binder http://gravity.sourceforge.net/servicebinder/
[3] Servlet container embedding 
http://www.eclipse.org/equinox/incubator/server/embedding_in_a_servlet_container.php
[4] Whiteboard pattern 
http://www.osgi.org/documents/osgi_technology/whiteboard.pdf
[5] 
http://svn.apache.org/repos/asf/cocoon/trunk/cocoon-blocks-fw/cocoon-blocks-fw-osgi-impl/META-INF/components.xml
[6] 
http://svn.apache.org/repos/asf/cocoon/trunk/cocoon-blocks-fw/cocoon-blocks-fw-osgi-impl/src/main/java/org/apache/cocoon/blocks/osgi/Activator.java
[7] The bridge was part of cocoon-core in package o.a.c.core.osgi, 
Reinhard moved it to the whiteboard, but the code seem to have 
disappeared on its way.
[8] Obsolete description of the sitemap blocks that contain an 
explanation of how polymorphism is implemented 
http://marc.theaimsgroup.com/?l=xml-cocoon-dev&m=111791016006393&w=2
[9] 
http://svn.apache.org/repos/asf/cocoon/trunk/cocoon-blocks-fw/cocoon-blocks-fw-osgi-impl/src/main/java/org/apache/cocoon/blocks/osgi/TestServlet2.java

Mime
View raw message