程式扎記: [ OSGi In Action ] Introducing OSGi : OSGi revealed (1)

標籤

2011年7月7日 星期四

[ OSGi In Action ] Introducing OSGi : OSGi revealed (1)

Preface : 
This article will cover : 

* Understanding Java’s built-in support for modularity
* Introducing OSGi technology and how it improves Java modularity
* Positioning OSGi with respect to other technologies

The Java platform is an unqualified success story. But this success has come in spite of the fact that Java doesn’t have explicit support for building 
modular systems beyond ordinary object-oriented data encapsulation. These include the following : 

* Programming practices to capture logical structure
* Tricks with multiple class loaders
* Serialization between in-process components

But these techniques are inherently brittle and error prone because they aren’t enforceable via any compile-time or execution-time checks. The end result has detrimental impacts on multiple stages of an application’s lifecycle : 

Development - Unable to clearly and explicitly partition development into independent pieces.
Deployment - Unable to easily analyze, understand, and resolve requirements from the independently developed pieces composing a system.
Execution - Unable to manage and evolve the constituent pieces of a running system, nor minimize the impact of doing so.

Welcome to the OSGi Service Platform. The OSGi Service Platform is an industry standard defined by the OSGi Alliance to specifically address the lack of support for modularity in the Java platform. As a continuation of its modularity support, it introduces a service-oriented programming model, referred to by some as SOA in a VM, to help you clearly separate interface from implementation. This chapter will give you an overview of the OSGi Service Platform and how it helps you create modular and manageable applications using an interface-based development model. 

The what and why of OSGi : 
The simplest answer to this question is that it’s a modularity layer for the Java platform. Here we use modularity more or less in the traditional computer-science sense, where the code of your software application is divided into logical parts representing separate concerns, as shown in figure 1.1 : 
 
Figure 1.1 : Modularity refers to the logical decomposition of a large system into smaller collaborating pieces. 

If your software is modular, you can simplify development and improve maintainability by enforcing the logical module boundaries; we’ll discuss more 
modularity details in chapter 2. The notion of modularity isn’t new. The concept became fashionable back in the 1970s. OSGi technology is cropping up all over the place—for example, as the runtime for the Eclipse IDE and the GlassFish application server. Why is it gaining popularity now? To better understand why OSGi is an increasingly important Java technology, it’s worthwhile to understand some of Java’s limitations with respect to creating modular applications.When you understand that, then you can see why OSGi technology is important and how it can help. 

Java’s modularity limitations : 
Java is promoted as a platform for building all sorts of applications for domains ranging from mobile phone to enterprise applications. Most of these endeavors require, or could at least benefit from, broader support for modularity. Let’s look at some of Java’s modularity limitations. 
- LOW-LEVEL CODE VISIBILITY CONTROL 
Although Java provides a fair complement of access modifiers to control visibility (such as public, protected, private, and package), these tend to address low-level object-oriented encapsulation and not logical system partitioning. Java has the notion of a package, which is typically used for partitioning code. 
Sometimes, the logical structure of your application calls for specific code to belong in different packages; but this means any dependencies among the packages must be exposed as public, which makes them accessible to everyone else, too. Often, this can expose implementation details, which makes future evolution more difficult because users may end up with dependencies on your nonpublic API. 
To illustrate, let’s consider a trivial “Hello, world!” application that provides a public interface in one package, a private implementation in another, and a main class in yet another. 

Listing 1.1 Example of the limitations of Java’s object-orientated encapsulation
- (1) Simple Interface
  1. package org.foo.hello;   
  2. public interface Greeting {  
  3.     void sayHello();  
  4. }  
- (2) Interface Implementation
  1. package org.foo.hello.main;  
  2. public class GreetingImpl implements Greeting {  
  3.     final String m_name;     
  4.     public GreetingImpl(String name) {   
  5.         m_name = name;  
  6.     }  
  7.     public void sayHello() {  
  8.         System.out.println("Hello, " + m_name + "!");  
  9.     }  
  10. }  
- (3) Main class
  1. package org.foo.hello.main;  
  2. import org.foo.hello.Greeting;  
  3. import org.foo.hello.impl.GreetingImpl;  
  4. public class Main {  
  5.     public static void main(String[] args) {  
  6.          Greeting greet = new GreetingImpl("Hello World");  
  7.          greet.sayHello();  
  8.     }  
  9. }  

The upper way is common and mentioned in Javadoc, tutorials, blogs, or even email rants, but nothing stops a third party from constructing a newGreetingImpl using its public constructor (2) as is done at (3). 
You may argue that the constructor shouldn’t be public and that there is no need to split the application into multiple packages, which could well be true in this trivial example. But in real-world applications, class-level visibility when combined with packaging turns out to be a crude tool for ensuring API coherency.

- ERROR-PRONE CLASS PATH CONCEPT 
The Java platform also inhibits good modularity practices. The main culprit is the Java class path. Why does the class path pose problems for modularity? Largely due to all the issues it hides, such as code versionsdependencies, and consistency. Applications are generally composed of various versions of libraries and components. The class path pays no attention to code versions—it returns the first version it finds. Even if it did pay attention, there is no way to explicitly specify dependencies. The process of setting up your class path is largely trial and error; you just keep adding libraries until the VM stops complaining about missing classes. 

Figure 1.2 shows the sort of “class path hell” often found when more than one JAR file provides a given set of classes. Even though each JAR file may have been compiled to work as a unit, when they’re merged at execution time, the Java class path pays no attention to the logical partitioning of the components. This tends to lead to hard-topredict errors, such as NoSuchMethodError, when a class from one JAR file interacts with an incompatible class version from another : 
 
Figure 1.2 Multiple JARs containing overlapping classes and/or packages are merged based on their order of appearance in the class path, with no regard to logical coherency among archives. 

- LIMITED DEPLOYMENT AND MANAGEMENT SUPPORT 
Java also lacks support when it comes to deploying and managing your application. There is no easy way in Java to deploy the proper transitive set of versioned code dependencies and execute your application. The same is true for evolving your application and its components after deployment. 
Consider the common requirement of wanting to support a dynamic plugin mechanism. The only way to achieve such a benign request is to use class loaders, which are low level and error prone. Class loaders were never intended to be a common tool for application developers, but many of today’s systems require their use. A properly defined modularity layer for Java can deal with these issues by making the module concept explicit and raising the level of abstraction for code partitioning. 

With this better understanding of Java’s limitations when it comes to modularity, we can ponder whether OSGi is the right solution for your projects. 

Can OSGi help you ? 
Nearly all but the simplest of applications can benefit from the modularity features OSGi provides. Here are some common scenarios you may have encountered where OSGi can be helpful : 

- ClassNotFoundExceptions when starting your application because the class path wasn’t correct. OSGi can help by ensuring that code dependencies are satisfied before allowing the code to execute.

- Execution-time errors from your application due to the wrong version of a dependent library on the class path. OSGi verifies that the set of dependencies are consistent with respect to required versions and other constraints.

- Type inconsistencies when sharing classes among modules: put more concretely, the dreaded appearance of foo instanceof Foo == false. With OSGi, you don’t have to worry about the constraints implied by hierarchical classloading schemes.

- Packaging an application as logically independent JAR files and deploying only those pieces you need for a given installation. This pretty much describes the purpose of OSGi.

- Declaring which code is accessible from each JAR file, and having this visibility enforced. OSGi enables a new level of code visibility for JAR files that allows you to specify what is and what isn’t visible externally.

- Defining an extensibility mechanism for an application, like a plugin mechanism. OSGi modularity is particularly suited to providing a powerful extensibility mechanism, including support for execution-time dynamism.

As you can see, these scenarios cover a lot of use cases, but they’re by no means exhaustive. The simple and non-intrusive nature of OSGi tends to make you discover more ways to apply it the more you use it. Having explored some of the limitations of the standard Java class path, we’ll now properly introduce you to OSGi. 

An architectural overview of OSGi : 
The OSGi Service Platform is composed of two parts: the OSGi framework and OSGi standard services (depicted in figure 1.3). The framework is the runtime that implements and provides OSGi functionality. The standard services define reusable APIs for common tasks, such as Logging and Preferences. 
 
Figure 1.3 The OSGi Service Platform specification is divided into halves, one for the OSGi framework and one for standard services. 

The OSGi specifications for the framework and standard services are managed by the OSGi Alliance. The OSGi Alliance is an industrybacked 
nonprofit corporation founded in March 1999. The framework specification is now on its fourth major revision and is stable. Technology based on this specification is in use in a range of large-scale industry applications, including (but not limited to) automotive, mobile devices, desktop applications, and more recently enterprise application servers. 

Because there are so many standard services, we’ll discuss only the most relevant and useful services, where appropriate. For any service we miss, you can get more information from the OSGi specifications. For now, we’ll continue our overview of OSGi by introducing the broad features of the OSGi framework. 

The OSGi framework : 
The OSGi framework plays a central role when you create OSGi-based applications, because it’s the application’s execution environment. The OSGi Alliance’s framework specification defines the proper behavior of the framework, which gives you a welldefined API to program against. The specification also enables the creation of multiple implementations of the core framework to give you some freedom of choice; there are a handful of well-known open source projects, such as Apache Felix (http://felix.apache.org/), Eclipse Equinox (www.eclipse.org/equinox/), and Knopflerfish (www.knopflerfish.org/). This ultimately benefits you, because you aren’t tied to a particular vendor and can program against the behavior defined in the specification. 

OSGi technology is starting to pop up everywhere. You may not know it, but if you use an IDE to do your Java development, it’s possible you already have experience with OSGi. The Equinox OSGi framework implementation is the underlying runtime for the Eclipse IDE. Likewise, if you use the GlassFish v3 application server, you’re also using OSGi, because the Apache Felix OSGi framework implementation is its runtime. The diversity of use cases attests to the value and flexibility provided by the OSGi framework through three conceptual layers defined in the OSGi specification (see figure 1.4) : 
 
Figure 1.4 OSGi layered architecture 

- Module layer—Concerned with packaging and sharing code
- Lifecycle layer—Concerned with providing execution-time module management and access to the underlying OSGi framework
- Service layer—Concerned with interaction and communication among modules, specifically the components contained in them

Like typical layered architectures, each layer is dependent on the layers beneath it. Therefore, it’s possible for you to use lower OSGi layers without using upper ones, but not vice versa. The next three chapters discuss these layers in detail, but we’ll give an overview of each here. 
- MODULE LAYER 
The module layer defines the OSGi module concept, called a bundle, which is a JAR file with extra metadata (data about data). A bundle contains your class files and their related resources, as depicted in figure 1.5. Bundles typically aren’t an entire application 
packaged into a single JAR file; rather, they’re the logical modules that combine to form a given application : 
 
Figure 1.5 A bundle contains code, resources, and metadata. 

Bundles are more powerful than standard JAR files, because you can explicitly declare which contained packages are externally visible (that is, exported packages). In this sense, bundles extend the normal access modifiers (public, private, and protected) associated with the Java language. 

Another important advantage of bundles over standard JAR files is the fact that you can explicitly declare on which external packages the bundles depend (that is, imported packages). The main benefit of explicitly declaring your bundles’ exported and imported packages is that the OSGi framework can manage and verify their consistency automatically; this process is called bundle resolution and involves matching exported packages to imported packages. Bundle resolution ensures consistency among bundles with respect to versions and other constraints, which we’ll discuss in detail in chapter 2. 

- LIFECYCLE LAYER 
The lifecycle layer defines how bundles are dynamically installed and managed in the OSGi framework. If you were building a house, the module layer would provide the foundation and structure, and the lifecycle layer would be the electrical wiring. It makes everything run. 

The lifecycle layer serves two different purposes. External to your application, the lifecycle layer precisely defines the bundle lifecycle operations (install, update, start, stop, and uninstall). These lifecycle operations allow you to dynamically administer, manage, and evolve your application in a well-defined way. This means bundles can be safely added to and removed from the framework without restarting the application process. 

Internal to your application, the lifecycle layer defines how your bundles gain access to their execution context, which provides them with a way to interact with the OSGi framework and the facilities it provides during execution. This overall approach to the lifecycle layer is powerful because it lets you create externally (and remotely) managed applications or completely self-managed applications (or any combination). 

- SERVICE LAYER 
Finally, the service layer supports and promotes a flexible application programming model incorporating concepts popularized by service-oriented computing (although these concepts were part of the OSGi framework before service-oriented computing became popular). The main concepts revolve around the service-oriented publish, find, and bind interaction pattern: service providers publish their services into a service registry, while service clients search the registry to find available services to use (see figure 1.6) : 
 
Figure 1.6 Providers publish services into a registry where requesters can discover which services are available for use. 

The OSGi service layer is intuitive, because it promotes an interface-based development approach, which is generally considered good practice. Specifically, it promotes the separation of interface and implementation. OSGi services are Java interfaces representing a conceptual contract between service providers and service clients. This makes the service layer lightweight, because service providers are just Java objects accessed via direct method invocation. Additionally, the service layer expands the bundle-based dynamism of the lifecycle layer with service-based dynamism—services can appear or disappear at any time. The result is a programming model to avoid the bulky and brittle approaches of the past, in favor of being modular and flexible. 

This sounds well and good, but you may still be wondering how these three layers fit together and how you go about using them to create an application on top of them. In the next couple of sections, we’ll explore how these layers fit together using some small example programs. 

Putting it all together : 
The OSGi framework is made up of layers, but how do you use these layers in application development? We’ll make it clearer by outlining the general approach you’ll use when creating an OSGi-based application : 

1. Design your application by breaking it down into service interfaces (normal interface-based programming) and clients of those interfaces.
2. Implement your service provider and client components using your preferred tools and practices.
3. Package your service provider and client components into (usually) separate JAR files, augmenting each JAR file with the appropriate OSGi metadata.
4. Start the OSGi framework.
5. Install and start all your component JAR files from step 3.

If you’re already following an interface-based approach, the OSGi approach will feel familiar. The main difference will be how you locate your interface implementations (that is, your services). Normally, you might instantiate implementations and pass around references to initialize clients. In the OSGi world, your services will publish themselves in the service registry, and your clients will look up available services in the registry. After your bundles are installed and started, your application will start and execute as normal, but with several advantages. Underneath, the OSGi framework provides more rigid modularity and consistency checking, and its dynamic nature opens up a world of possibilities.


Supplement :
JavaWorld > OSGi 學習心得筆記
COSCUP 2008:OSGi簡介 (Try, twjug) 1/2
COSCUP 2008:OSGi簡介 (Try, twjug) 2/2

沒有留言:

張貼留言

網誌存檔

關於我自己

我的相片
Where there is a will, there is a way!