Chapter 7. Client/Server Interaction

At this point Toast operates as a stand-alone application. To fully implement the emergency scenario, however, we need a service center to which we can report emergencies. In this chapter we implement a separate OSGi runtime to be that service center. By chapter’s end, the client’s emergency application is sending emergency information to the service center using simple HTTP for communications.

Since Toast is growing in size and complexity, it’s also the right time to introduce some effective patterns for handling configurable runtime parameters as well as logging.

The goals of this chapter are to

• Implement the service center as a separate runtime, using OSGi and servlets

• Enhance the client side to allow the emergency monitor to communicate with the service center over HTTP

• Present mechanisms for logging and handling command-line parameters

• Launch both the service center and the client and test their interaction

In previous tutorial chapters, we showed every line of code and every change. In this and subsequent chapters, however, the volume of code makes that approach infeasible. Instead we’ll ask you to use the Samples Manager to load each project and then we provide you with detailed instructions for building the rest of the code in the chapter. If you prefer, you still have the option to load all the finished code for the chapter and just read along with the tutorial sections.

7.1 The Back End

Back end is the term Toast uses for the server side. It’s important to understand that the implementation of the Toast Back End assumes that it is running as an entirely separate OSGi runtime on an entirely separate Java VM. It might even be running on an entirely separate computer. It’s also headless; that is, it has no user interface.

The back end is a server that listens for clients to report emergencies. The simplest way to handle messaging of this sort is to use HTTP. The OSGi specification defines an HTTP service that allows applications like Toast to register servlets. Interchangeable implementations of that service are available from a variety of sources. We use the one provided by Equinox.

7.1.1 The Core Bundles

Before we implement the back end emergency functionality, we need to set up two core bundles. The org.equinoxosgi.toast.core bundle provides a set of utility classes used by both the back end and the client runtimes. The details of the various utility classes in this bundle are covered in Section 7.3, “Utility Classes,” and are not important here. You can just load the org.equinoxosgi.toast.core bundle using the Samples Manager as discussed in Section 3.4.2, “Comparing.”

The other bundle we need is org.equinoxosgi.core.emergency. It contains a set of constants that are used by both the back end and the client runtimes to implement the emergency scenario.

• Create a new bundle project called org.equinoxosgi.toast.core.emergency. Refer to Section 4.2.1, “GPS Bundle,” for detailed instructions on creating a bundle project.

Next create the package and the interface:

• Create a new interface in this bundle called IEmergencyConstants and place it in a new package called org.equinoxosgi.toast.core.emergency. Fill in the code with the following snippet:

image

• On the Runtime tab of the bundle’s manifest, add the newly created package to the list of exported packages. Then save the manifest.

7.1.2 The Back End Emergency Bundle

With the two core bundles in place, let’s create the bundle with the back end’s application logic. It consists of a servlet to handle emergency notifications from the client and a component to register the servlet with the servlet container implemented by HttpService.

• Create a new plug-in project for the bundle named org.equinoxosgi.toast.backend.emergency.

• In the manifest editor, add the following bundles to the Automated Management of Dependencies list, making sure the Import-Package button is selected:

image

The javax.servlet bundle contains the necessary packages for HTTP communications. The org.eclipse.osgi.services bundle contains the interface HttpService.

Now create the servlet to handle incoming HTTP requests from clients. For now, each time it receives an emergency message, it simply logs the emergency information to the console.

• Create a class called EmergencyServlet in a package called org.equinoxosgi.toast.internal.backend.emergency with the class HttpServlet as its superclass. Use the following snippet to complete the class:

image

The key code here is the standard servlet doGet method. The method simply gathers the parameters from the HTTP request, logs the emergency event to the console, and responds to the client with a text message. In a more complete application the emergency event would go into a database and trigger any number of follow-on actions, but here this is enough to paint the picture.

Notice that the EmergencyServlet class is pure domain code; that is, it is unaware of OSGi. As a generic subclass of HttpServlet, it can be reused in a server setup that does not involve OSGi. In the Toast scenario, however, we are using OSGi and we need to register the generic EmergencyServlet with the HttpService. We need to create a component to achieve this:

• Create a class called Component in a package called org.equinoxosgi.toast.internal.backend.emergency.bundle. Use this snippet to complete the class:

image

Here the bulk of the work is in the startup component lifecycle method. startup creates the alias for the emergency servlet, instantiates the actual servlet, and then registers it with the HttpService. The shutdown method then takes care of unregistering the servlet when the component is deactivated. The setHttp method provides the hook that DS needs to inject the HttpService into the component.

All that remains is to describe the emergency component to DS by following these steps:

• Create a new service component for this bundle called component.xml in the OSGI-INF folder of this bundle. Refer to Section 6.4.1, “Modifying the GPS Bundle,” for detailed instructions.

• Use Component as the class for the component.

• Fill in startup and shutdown for the Activate and Deactivate fields, respectively.

• Add HttpService as a referenced service. Use http as the Name and setHttp as the Bind method. This means that when the HTTP service becomes available to the back end emergency component, DS invokes the setHttp method.

The back end emergency bundle is a typical top-of-the-food-chain bundle. It references one service, the HttpService, and provides no services. With the back end in place, we’re ready to make changes to the client side and hook them together.

7.2 The Client Side

Recall from the previous chapter that the emergency monitor bundle listens for the airbag to deploy, obtains the vehicle’s location from the GPS bundle, and then writes this information to the console. Now we want the emergency monitor to notify the back end we just created. The first step is a mechanism that handles the communication from the client to the back end. Once this is completed, we can modify the emergency monitor bundle to use this mechanism and talk to the back end.

7.2.1 The Channel Bundle

Since a mechanism that acts as a communication channel for clients to send messages to servers using HTTP is useful for more than just the emergency scenario, let’s expose it as a service and put it in a separate bundle:

• Create a new project for the bundle named org.equinoxosgi.toast.core.channel.sender.

• Add org.equinoxosgi.toast.core to the Automated Management of Dependencies list, making sure the Import-Package button is selected.

Now create the interface to define the channel service:

• Create an interface called IChannel in a package called org.equinoxosgi.toast.core.channel.sender. Use the following snippet to complete the interface:

image

The IChannel interface defines a single method that allows the client to send a message to the back end and to receive an InputStream containing the back end’s response.

The ChannelMessage class is a simple data structure that captures the idea of a function and a set of parameters. It is logically part of the IChannel service API because any consumer that invokes the send method needs to create a ChannelMessage first. As such, it should go in the same package as IChannel.

• Create a class called ChannelMessage in the same package as IChannel. Use the following snippet to complete the class:

image

image

Now that the service interface is complete, make sure that the service package is visible to other bundles:

• In the Runtime tab of the manifest editor, add org.equinoxosgi.toast.core.channel.sender to the list of exported packages.

Now we are set to implement the IChannel service, but we first need to decide where the implementation will go. Should the implementation go in its own bundle or in the same bundle as the service interface? If we expect there to be more than one implementation of the service, it makes sense to put the service definition in one bundle and place each implementation in its own separate bundle. In this case, we don’t expect more than this one implementation, so putting the interface and the implementation together is OK. If we decide later to add another implementation of IChannel, we can refactor our original implementation to be in a separate package from the service interface. This is one of the powers of OSGi’s modularity—clients of a service are isolated from the implementations, so implementers have more freedom.

• To create the implementation, make a new class called UrlChannel that implements IChannel and put it in a package called org.equinoxosgi.toast.internal.core.channel.sender. Complete the body of the class using this snippet:

image

The last step in completing the bundle is to create a component definition that exposes UrlChannel as an IChannel service:

• Create a new service component for this bundle called component.xml in the OSGI-INF folder of this bundle.

• Use the UrlChannel for the class of the component.

• Add IChannel as a provided service.

Notice that this bundle lies at the bottom of the food chain since it registers a service but requires none. Notice also that the service implementation class can be used directly as the component implementation class—a demonstration of DS’s POJO capabilities.

Bundle API Surface Area

A bundle’s API surface area is the total number of packages that the bundle exports via its Export-Package manifest header. As previously mentioned, less is more in this regard, and ideally a bundle has a small API surface area upon which other bundles can depend.

Minimize exported types—A bundle should minimize the number of types that it exports. A bundle should always divide its package namespace into API packages and internal packages. A common convention is to name internal packages by including a segment called internal. As new types are added to the bundle, a decision must always be made as to whether the type is API and should reside in an exported package, or is private implementation and should reside in an internal package that is not exported.

Favor exporting interfaces over classes—Ideally an exported package should contain only interfaces. When a class must be exported, it should be a simple data type that does not dictate how a service interface is implemented. If the bundle provides an OSGi service, the implementation of the service should reside in a private internal package.

Package names are API, too—Do not forget that an export package is part of the bundle’s public API upon which other bundles can depend. Special attention should be given to the naming of such packages. Keeping packages highly cohesive is recommended, and exported packages should contain small sets of closely related types and should have intention-revealing names.

Bundle-SymbolicName is API, too—A bundle’s Bundle-SymbolicName manifest header should also be treated as API. Not only does the Bundle-SymbolicName uniquely identify a bundle, but other bundles can use the Require-Bundle manifest header to declare a dependency upon the bundle by specifying its Bundle-SymbolicName. A bundle’s Bundle-SymbolicName is also accessible programmatically via the Bundle method getSymbolicName.Bundle objects can be accessed in a variety of ways, such as via the BundleContext, via the PackageAdmin service, and via BundleEvents that are fired to BundleListeners.

Artifacts should be placed, by default, in a bundle’s internal package. Artifacts that are placed in an exported package should be carefully scrutinized and reviewed since they contribute to the bundle API surface area. Packages are exported using the Export-Package manifest header.

As with all public APIs, renaming and refactoring packages breaks bundles that depend upon them and should therefore be avoided. For this reason it is important to structure and name exported packages carefully.

7.2.2 The Emergency Monitor Bundle

Now that we have a means of communicating to the back end, the emergency monitor on the client needs to be updated to use the channel support and tell the server about emergencies.

Let’s start by adding the necessary bundles to the client’s emergency monitor project:

• Open the manifest editor for org.equinoxosgi.toast.client.emergency and add the following bundles to the Automated Management of Dependencies list:

image

• In the Required Plug-ins list, add org.eclipse.equinox.common. Section 5.3, “Registering the Airbag Service,” provides a detailed discussion of the rationale for using Require-Bundle in this case.

With the necessary packages now available, we can modify the EmergencyMonitor class to talk to the back end via a channel. The following snippet outlines the changes needed. As with the other snippets, you can load this from the Samples Manager.

image

image

image

This class has three major parts: lifecycle and job management, setters for dependency injection, and the code for sending messages over a channel. Notice the setChannel method was introduced to accept the IChannel service when it becomes available.

The most significant change to EmergencyMonitor is in the domain logic. Now, when the airbag deploys and the deployed method is invoked, instead of simply printing the emergency message to the console, the monitor gathers readings from the GPS, instantiates a ChannelMessage, and sends it to the back end via the channel.

The emergency monitor uses a Job to communicate with the back end because it is a potentially long-running operation. Since deploy is called as a listener notification, it would be bad form to block the notifier thread with remote communications and prevent other listeners from hearing about the events.

Finally, we need to modify the component definition for the emergency monitor bundle to require the IChannel service:

• Open the component.xml in the OSGI-INF folder of this bundle.

• On the Services tab, in the Referenced Services section, press the Add... button, type in IChannel, and press OK.

• Then, with IChannel still highlighted, press the Edit... button. In the Name field, type in channel.

• In the Bind field, type in setChannel. Then save the file.

7.3 Utility Classes

Scattered throughout the code snippets on both the back end and the client-side implementations in this chapter you may have noticed references to a few new utility classes. Specifically, constants interfaces ICoreConstants and IEmergencyConstants have been introduced along with PropertyManager and LogUtility.

7.3.1 Constants

Toast uses Java interfaces to define constants that are shared between different bundles. ICoreConstants lives in the org.equinoxosgi.toast.core bundle and is intended to be used by any Toast bundle. IEmergencyConstants is broken out into a separate bundle called org.equinoxosgi.toast.core.emergency. The constants it defines are intended to be used only by the parts of Toast that implement the emergency scenario, both on the client and on the back end.

In later chapters, when Toast is divided into deployable features, it will be very handy that the constants needed by the emergency scenario can easily be installed and removed along with the rest of the code that implements the emergency scenario.

7.3.2 Properties

The PropertyManager class resides in the org.equinoxosgi.toast.core bundle. It is a simple helper class that exposes Java system properties and logs property accesses for debugging purposes. For example, in the code in the previous section, the EmergencyMonitor used the PropertyManager to fetch the value used to identify itself in messages to the back end.

For now the easiest way to define system properties is to use VM arguments when launching. To do this you place the key/value pairs in the VM Arguments section of the Arguments tab of your run configuration where each is preceded by –D. For example, to define the toast.id argument to have a value of Kevin131, add the following to the VM Arguments:

-Dtoast.id=Kevin131

In later chapters we introduce the notion of a product and two configuration files: config.ini and launcher.ini. Any of these can also be used to set up system properties.

7.3.3 Logging

Toast defines a class called LogUtility in org.equinoxosgi.toast.core. LogUtility provides a simple API for logging messages at various levels to both the OSGi Log Service and to the console. Chapter 17, “Logging,” provides a deep dive on logging.

LogUtility is used throughout Toast. The EmergencyMonitor, for example, uses LogUtility in two places, first when it receives a reply from the back end. It logs it at debug level, which makes it easy to suppress by setting the LogUtility’s log level to be LOG_INFO or higher. It uses LogUtility again if a communication error occurs. In this case it logs it at error level and passes along the exception as well as a descriptive message.

Why a Singleton and Not a Service?

It is not always obvious whether certain functionality should be implemented as a service or as a singleton. In the case of LogUtility and PropertyManager, one could argue both ways.

In this example it was a conscious decision to implement them as singletons. We decided that requiring users of these utilities to acquire them as services would often be more trouble than it was worth. For example, often the very reason for property fetching or logging is to report a situation concerning services. In other words, the utilities need to operate at a level even below services.

Also, had we chosen to make them services, any bundle that needed to fetch properties or do logging would need to acquire that service and then pass it down into the depths of the domain logic or contrive to make it available internally. This can needlessly clutter otherwise simple implementations and APIs.

For our needs, the LogUtility insulates Toast from the details of the logging configured into the system.

7.4 Running Toast

Now that Toast consists of two separate runtimes, we need two launch configurations. The original client launch configuration from the previous chapter still resides in org.equinoxosgi.toast.client.emergency. We’ll need to modify it to include the new bundles created in this chapter. We’ll also need to make another launch configuration to run the back end.

7.4.1 Running the Back End

Start by creating a new launch configuration for the back end:

• Create a new run configuration named backend. You can refer to Section 4.2.4, “Launching,” for detailed instructions.

• Select the following three bundles from the workspace:

image

• Select the following six bundles from the target platform:

image

The org.eclipse.equinox.http bundle contains an implementation of HttpService. Without it, there would be no server, and Toast’s back end servlet would not be able to register. The org.eclipse.equinox.ds and org.eclipse.equinox.util bundles provide the DS implementation.

• Add the following VM arguments:

image

The last two arguments are new in this chapter. The first of these configures the LogUtility to log all messages, including debug messages, to the console. The last argument sets the port for the HTTP server to 8080. If you use port 8080 for some other server on your computer, change this argument or shut down the conflicting program.

• On the Common tab, select the Shared file option and use /org.equinoxosgi.toast.backend.emergency as the folder.

• Run the backend launch configuration.

You should see the following log messages on the Console view:

Property: -Dtoast.backend.url=http://localhost:8080/toast
Component: Registered EmergencyServlet at /toast/emergency

The first message indicates that the PropertyManager has accessed the toast.backend.url system property. The second message indicates that the EmergencyServlet has been registered. You can check the servlet by pointing your web browser at http://localhost:8080/toast/emergency.

If the server is running properly, you should see a reply in your web browser such as 406 - Missing parameter: id. You’ll also see a stack trace in the Console view.

7.4.2 Running the Client

The client launch configuration needs to be updated to include three new bundles:

• Rename the Toast launch configuration to be client.

• In addition to the original bundles listed, add the following three bundles:

image

• With the backend launch configuration still running, run the client launch configuration.

On the Console view for the client, the first things to appear are two messages from the PropertyManager indicating that it has obtained properties for the client ID and for the back end URL:

Property: -Dtoast.id=ABC123
Property: -Dtoast.backend.url=http://localhost:8080/toast

After about five seconds, the airbag deploys and the URLChannel logs two further messages. The first one indicates the HTTP request that it sends to the back end. The second message shows the logical content of the message it sends. Finally, the EmergencyMonitor logs the reply it receives from the back end. This sequence repeats every five seconds.

image

In the meantime, take a look at the back end’s console by clicking the image icon in the Console view. Notice that the back end logs a message each time it receives an emergency notification from the client:

EmergencyServlet: Emergency: ABC123 (37.76999N, -122.44694E) 90deg 50kph

7.5 Summary

In this chapter we added a server-side runtime to the overall Toast application. The client and back end runtimes even share some common bundles. The client now notifies the back end when an emergency occurs. These two runtimes are both based on OSGi and can run independently on different workspaces or even different machines.

We created a variety of bundles along the way, some providing services, some referencing services, and some utility bundles that neither provide nor reference any services. Most significantly, this chapter covered the registration of a servlet into a server.

The power of modularity and concern with separation was once again evident. Toast now consists of seven bundles, up from three at the end of Chapter 6, “Services.” Figure 7-1 shows the three original bundles along with the four new bundles. The original three bundles are still present but have been updated to clearly separate domain logic from the client-server communications. We have also separated out two bundles that provide shared infrastructure for both client and server. The result is a system of seven highly cohesive bundles. We will see the advantages to this approach as we look at testing in the next chapter.

Figure 7-1 Bundle dependencies

image

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.133.134.151