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

Reply via email to