So far the Toast Back End has been largely headless—content is written to the console, but there is no user interaction. In this chapter we add a web-based user interface to Toast. The goal here is not to create a fancy UI but rather to talk about some commonly used extensibility mechanisms and metaphors set in the context of providing a UI for Toast.
We use this context to introduce the Whiteboard Pattern, a powerful style of using OSGi services to aggregate operations. In building the portal we also look at how to insulate our domain code from OSGi in cases where explicit OSGi use is inevitable.
In this chapter we do the following
• Introduce and explore the Whiteboard Pattern
• Build on our current back end infrastructure and create an extensible servlet structure by composing request handlers
• Use the extensibility infrastructure to create a simple web UI portal for fleet management
• Demonstrate the use of higher-order cardinality and dynamicity in Declarative Services
• Implement a strategy for isolating domain code from OSGi infrastructure
Our modest Toast Back End has already demonstrated some of the value of OSGi on servers—we are able to reuse bundles on the client and server, and composing collections of servlets is straightforward. In the larger context, the benefits of OSGi seen on desktops are at play on servers. In this chapter we explore some of these and create a simple extensible web UI portal using techniques similar to those used in other scenarios.
In the Toast context we have a rich client UI. To create a similarly rich server UI would require significant effort and the use of technology such as the Eclipse Rich Ajax Platform (RAP). Here, we will create a simple portal that can be dynamically extended with new actions. For the most part we focus on the portal extensibility mechanisms and leave aside the details of web design and compelling workflows. Figure 13-1 shows a screen shot of the portal and the list of vehicles being managed.
The Toast Back End manages a set of vehicles. A UI on the back end should allow users to browse and interact with the vehicles. Since we are using web browsers and HTTP, Java servlets are a convenient means of implementing this functionality. We could have each new piece of functionality implemented as an independent servlet, but this gets challenging to coordinate, not to mention that it makes it harder to demonstrate interesting OSGi capabilities. Instead, the Toast Portal uses one servlet, the PortalServlet
, and the Whiteboard Pattern to enable extensibility.
The bulk of this chapter is a tour of the web portal code. As such, you should load the Chapter 13 sample from the Samples Manager to help in the exploration.
PortalServlet
The PortalServlet
acts as a request dispatcher. Incoming requests are inspected and matched with, and dispatched to, a suitable handler. It is a subclass of javax.servlet.http.HttpServlet
, and the following snippet shows its doGet
method—the only significant functionality in the class. The two most important lines are highlighted in the snippet.
The portal servlet accepts HTTP GET requests and uses the information supplied to determine an action to execute. To make the portal extensible, an action lookup mechanism is introduced. This isolates the servlet from the details of how actions are contributed. As we see in the first highlighted line, the portal servlet gets the relevant action from the action lookup mechanism. Once the action is found, handling of the request is delegated to the action. The portal is not involved in producing the response to any requests other than requests to the root of the servlet URL space or unrecognized requests.
As with the other cases in Toast, a DS component is needed to get the HttpService
and register the required servlets. From an architectural point of view the portal is a configuration of the portal servlet and an action lookup mechanism. Together they define how actions are contributed, discovered, and executed.
The portal component combines the action lookup mechanism, business logic, and the control center to create a PortalServlet
that is then registered with the HttpService
. The key code for the Portal
component is shown in the following snippet. In practice, center
, lookup
, and http
are all fields containing services statically referenced by the portal component’s portal.xml
.
There are many possibilities for implementing the action lookup mechanism. All must implement the interface shown in the following IActionLookup
snippet. Here getAction
allows for the lookup of an action, and getAvailable
and getActionProperty
let clients interrogate the structure of the available actions.
In this chapter we will implement an OSGi service-based action lookup mechanism. In particular we introduce the Whiteboard Pattern as a means of discovering actions registered as services. The Whiteboard Pattern inverts the typical service orientation. In the registration approach used so far in the book, clients of, say, the HttpService
discover the service and register their interests and capabilities. In the Whiteboard Pattern, the interested parties leverage the OSGi service registry by registering services, and the event sources discover them by querying the service registry. The registered services neither know nor care how they are used.
In keeping with our use of Declarative Services, we implement an IActionLookup
that uses DS and the OSGi services following the Whiteboard Pattern. Using the whiteboard approach, action providers register their actions as services with the OSGi service registry. The action lookup mechanism then discovers all available action services and makes them available to the portal. Chapter 15 gives much more detail on the Whiteboard Pattern and dives deeply into the implementation detail of the portal.
So far we have used DS in one way—to discover single services to which our components are statically bound. For a whiteboard component we need to allow both multiple service object discovery and dynamic service contribution. The following snippet shows the markup for the service-based IActionLookup
component that meets these needs:
The snippet declares that the component references 0 or more IPortalAction
services. By allowing for 0 references, the component says the referenced service is optional—the portal can run without any actions. With the upper bound set to n
, the component allows for many services to be bound to it.
As we are building a dynamic system, the IPortalAction
service reference has its policy
set to dynamic
. This allows referenced services to come and go without affecting the activation state of the component—the service action lookup component, and thus the portal, keeps running even if the actions change.
As IPortalAction
services come and go, addAction
and removeAction
maintain a catalog of the discovered services. To get a sense of how this works, take a look at the addAction
snippet:
The first thing to notice is that the addAction
method’s parameter is of type ServiceReference
, whereas normally it has been the actual service type, for example, IPortalAction
. One of the advantages of using DS is that it enables laziness—declared services are not instantiated until they are actually referenced. By using a ServiceReference
, we tell the DS runtime to delay the instantiation of the service object even further until we programmatically dereference the ServiceReference
object by calling the DS component’s ComponentContext.locateService(String, ServiceReference)
method. If the service represented by the ServiceReference
is never used, it is never instantiated. Since typically only a few portal actions are used, this saves time and space.
The second point of interest in this code is the PortalAction
wrapper class. A PortalAction
is an IPortalAction
that wraps a ServiceReference
discovered by DS. The PortalAction
presents an opaque façade that lazily gets the real service when the action is to be executed. The following snippet illustrates the IPortalAction.execute
method:
Notice that the ComponentContext
is used to look up the cached ServiceReference
. The execute request is then delegated to the real action implementation. Readers interested in more detail on these points should review the ServiceActionLookup
and PortalServlet
code and look at Section 15.2.6, “The Whiteboard Pattern.”
The final key element of the addAction
method is the use of service properties. The first line of the method retrieves the action property from the service reference. When the service provider registered or declared the service, it supplied a value for this property. The portal uses the value as an identifier for the action. Incoming HTTP requests identify the action to run by including the related ID as a parameter in the HTTP request. The PortalServlet
extracts the parameter and passes it to the IActionLookup
. Refer back to the servlet code in Section 13.2, “The PortalServlet
,” to see the control flow.
Adding an action using the Whiteboard Pattern is as easy as registering an IPortalAction
service with the OSGi service registry. This can be done using declarative or traditional service techniques. Since we’ve adopted DS, we declare a DS component for each portal action.
Action implementations need only implement the simple interface shown in the following snippet:
Shown next is the code for the browse action. This action presents a UI allowing users to browse aspects of a particular vehicle. This action simply generates a web page with all the browsing actions for a particular vehicle.
The browse service is contributed to the system using the following DS component declaration:
The key part of this markup is the two <property>
elements—the action
and the label
. As we saw, the portal uses the action
property value to identify actions. Similarly, the label
property is used to affect the appearance of the action in the UI—the value of this property is presented to the user.
To add a bit more flexibility to action contributions, IActionLookup
includes a simple action hierarchy model using paths in the action
property. For example, the action
property for the set of actions related to browsing a vehicle all start with browse/
. The portal UI can then dynamically identify the browsing capabilities by looking only for the actions with that prefix.
Of course, actions can be contributed from any bundle. For example, the following snippet shows how the UI for tracking a vehicle is contributed by the org.equinoxosgi.toast.backend.tracking.ui
bundle. Notice the path in the action
property value.
This example portal is not intended to be comprehensive or even sophisticated. It serves two purposes: First, it gives the Toast Back End a UI; and second, it motivates a discussion around the Whiteboard Pattern and domain code isolation issues. The approach taken here is very similar to that of several OSGi console implementations. As with those systems, there are a number of pros and cons.
Pros:
• Contributing new actions is relatively easy.
• Contributed actions can be consumed by any number of subsystems in any way they choose.
• Using DS, the contributing bundles need not be started for action contributions to be recognized.
• The actions can be POJOs and so are testable using standard testing techniques.
Cons:
• In this example, it is the action providers, the producers, that determine the relative positioning of the actions.
• Internationalization of the action labels is not directly supported.
• The OSGi service registry is not scoped. As a result, it does not directly support multiple portals running in one framework having different sets of actions or the simultaneous use of the action service interface in other contexts.
• Actions contributed to the portal may be used by unintended consumers.
In the end, none of these individual topics should drive your adoption or rejection of the approach. In some cases a pro is a con and vice versa. The needs of your system should drive the mechanism you choose. For more discussion of module collaboration, see Chapter 15, “Declarative Services,” and Chapter 16, “Extensions.”
In this chapter we added an extensible web-based UI portal to the Toast Back End. The portal uses the Whiteboard Pattern to enable the contribution of new UI actions and content. Using the Whiteboard Pattern makes it easy for portal contributors to add functionality but introduces some subtleties that need to be handled by the portal.
As we saw, overall it was quite straightforward to add an extensible web UI to Toast. The example portal is simplistic but suitable for our purposes. From these modest beginnings it is easy to see how you can benefit from the same modular approaches used in RCP development when building the back end UI. Technologies such as Rich Ajax Platform take this a step further and enable rich server-side UIs using client-side programming models.