© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
A. Prakash, S. I. BashaHands- On Liferay DXPhttps://doi.org/10.1007/978-1-4842-8563-3_1

1. OSGi Basics

Apoorva Prakash1   and Shaik Inthiyaz Basha2
(1)
BANGALORE, India
(2)
Nellore, AP, India
 

This chapter dives deep into OSGi concepts, along with its essential features, its architecture, services, the Service Registry, and a few other crucial topics that fall under the basics of OSGi concerning Liferay DXP. Further, you learn about bundles with a straightforward example in the next chapters. By the end of this chapter, you will understand the unlimited potential of OSGi.

Understanding OSGi

The Open Services Gateway Initiative (OSGi) was founded in March, 1999 and is managed by the OSGi Alliance.

The OSGi Alliance now refers to the framework specification as OSGi or the OSGi Service Platform. To create a Java-based service framework that can be managed remotely, the vendors of networking providers and embedded systems came together and created a set of standards. OSGi was initially developed to be a gateway for managing Internet-enabled devices like smart appliances. The Java software framework is embedded in a gateway hardware platform, such as a set-top box or cable modem. This software framework acts as a central message dealer to the home’s LAN (Local Area Network). The core goal is to efficiently manage cross-dependencies for software developers by creating a standardized middleware for intelligent devices.

Liferay uses OSGi extensively for product development. Other noteworthy companies include Oracle WebLogic, Eclipse Foundation, IBM WebSphere, Atlassian Jira and Confluence, and JBoss. These are the notable companies that are using OSGi for their product development.

Let’s look at what makes OSGi different from other frameworks.

How Is OSGi Different?

The OSGi framework is different from other frameworks based on Java, especially Spring. More than one application can exist in the same container in the OSGi bundle runtime environment. The OSGi container takes care of access to the dependencies required by each component in the container. The OSGi framework also supports standardized dependency injection, as defined by the Aries Blueprint project.

In OSGi, bundles can consume services exposed to other bundles. A bundle can define and declare a version of bundles. The runtime will automatically load all its bundles to resolve all dependencies.

Note

In OSGi, if any bundle dependencies require multiple versions of the same bundle, they are also available side by side.

OSGi is a modularity layer for the Java platform. OSGi’s core specifications define a component and service model for Java. OSGi provides a service-oriented development model, allowing for a service-oriented architecture within a virtual machine.

For example, large Java applications can be challenging to deploy and manage. In order to update deployment, the system/servers need to be cycled, and the application build and deployment may cause system outages. But OSGi provides an isolated module cycling/updating capability to increase availability.

A Deeper Look at OSGi

OSGi offers an elegant solution for handling dependencies, by requiring dependency declarations within units of modularity. Multiple applications can coexist in the same container in OSGi, and the OSGi bundle runtime environment manages them. The OSGi container will ensure each component is sufficiently isolated and approach any dependencies required to access.

OSGi has two parts. The first part is called the bundle. It has modular component specifications, which are generally referred to as plugins. The specification will help determine the bundle’s interaction and lifecycle infrastructure. The second part is the Service Registry, which is beneficial to understanding how bundles discover, publish, and bind to services in the Service Oriented Architecture (SOA) at the Java Virtual Machine (JVM) level.

OSGi Architecture

The OSGi architecture is used in different Java-based applications. It is illustrated in Figure 1-1 and consists of several layers that work on top of the hardware and operating system:

A model of the O S G I architecture with different layers including application and bundles, security, and hardware.

Figure 1-1

OSGI service gateway

  • Bundles: An OSGi bundle is a Java ARchive (JAR) file that contains resources, Java code, and a manifest that describes the bundle and its dependencies. For an application, the OSGi bundle is the unit of deployment. You learn more about bundles in the next section of the chapter.

  • Services: The services layer in the OSGi architecture will offer a “publish find bind” model for old Java objects to connect bundles dynamically. To simplify, an OSGi service is a Java object instance registered to an OSGi framework with a set of properties.

  • Lifecycle: The lifecycle layer provides the API used to start, install, uninstall, update, and stop objects.

  • Modules: This layer defines how to export and import code by bundle.

  • Security: This layer handles the security aspects.

  • Execution Environment: This layer defines the available classes and methods in a specific platform.

Now that you’ve learned a bit about the OSGi basics, the next section explores OSGi bundles.

OSGi Bundles

Bundles are modular Java components. Creating and managing bundles is facilitated by OSGi, and bundles can be deployed in a container. A developer uses OSGi specifications and tools to create one or more bundles. The bundle’s lifecycle is defined and managed by OSGi. It also supports the bundles’ interactions and hosts them in a container. The OSGi container is roughly parallel to a JVM. Similarly, bundles can be treated as Java applications with distinctive abilities. OSGi bundles run as client and server components inside the OSGi container.

So, bundles are nothing more than OSGi components, and they are in the form of standard JAR files. The only difference between regular JAR files and bundles is the manifest header (also referred to as bundle identifiers). These manifest headers tell the runtime that this JAR is not a standard JAR file but an OSGi bundle. These bundle identifiers consist of two main parts—Bundle-SymbolicName and Bundle-Version. You must use a combination of these two to export and import the services. This combination of Bundle-SymbolicName and Bundle-Version (semantic versioning) creates a unique identifier for OSGi bundles and thus for dependencies.

Note

Logically, a bundle has an independent lifecycle with a piece of functionality. It can work independently with start, stop, and remove.

Technically, a bundle is a JAR file containing some OSGi-specific headers in the MANIFEST.MF file.

As depicted here, the Bundle-SymbolicName is com.handsonliferay.employee.portlet and the Bundle-Version is 1.2.3.2022.
Bundle-Name: handsonliferay-employee-portlet
Bundle-SymbolicName: com.handsonliferay.employee.portlet
Bundle-Version: 1.2.3.2022
Export-Package: com.handsonliferay.employee
Let’s deep dive into understanding them:
  • Bundle-SymbolicName: A unique identifier that refers to the bundle. This is generally understandable human text so that developers can understand the functionality written inside it. The best practice is to name it as class packages are named. In the previous example, you created an API with the symbolic name com.handsonliferay.employee.portlet, which any OSGi bundle can import to consume the exposed services. This naming convention is a standard Liferay development approach.

  • Bundle-Version: 1.2.3.2022. There are four parts to a version number, each separated by three dots (see Figure 1-2). This versioning scheme is also known as bundle semantic versioning. Let’s look at part of the semantic versioning process:
    • Major, which is 1 in this case. The major version is changed when there are code changes that can break the code used in the APIs of this bundle.

    • Minor, which is 2 in this example. This means there are API changes in the bundle, which may include some fixes. It refers to APIs of this bundle and it will not break the code.

    • Micro, which is 3 is this example. This changes when there are minor changes and no compatibility issues.

    • Qualifier, which is used when there is no impact to compatibility. It’s used to tag snapshots or nightly builds.

From left to right, an arrow is divided into four sections: major, minor, macro, and qualifier. They are labeled 1, 2, 3, and 2022.

Figure 1-2

Bundle version

OSGi Bundle Rules

With these details, you can now learn how OSGI bundles are supposed to work in real-time. Let’s look at the OSGI bundle rules:
  1. 1.

    You want code that may include or exclude some application configuration. Using this approach, you will get the primary benefit of modularity.

     
  2. 2.

    You want code that should update independently from other code. Using this approach, you get another primary benefit of modularity.

     
  3. 3.

    You want code that has a set of specific dependencies on other libraries. This way, you decrease the chance of conflicts by isolating those dependencies.

     
  4. 4.

    Some of the interface code might have different implementations. This way, without making any other changes, you can swap out implementations.

     

Importing and Exporting Bundles

The following example exports a service called com.handsonliferay.employee.api with version 1.0.0. The Employee API is exposed with a Symbolic-Name of com.handsonliferay.employee.api and a version number of 1.0.0:
Bundle-Name: Employee-api
Bundle-SymbolicName: com.handsonliferay.api
Bundle-Version: 1.0.0
Export-Package: com.handsonliferay.api; version=1.0.0

Importing Bundles

To understand bundle importing, you must understand version ranges. As you have already learned, an OSGi bundle can be exported with a specific version number, so multiple OSGi bundles have the same symbolic name but different versions. They are essentially nothing but different versions of the same OSGi bundle. There may be cases when you have more than one valid bundle version from various deployed OSGi bundles. To solve this scenario, you can mention ranges in the import statement. Square brackets and parentheses are used for this purpose. Square brackets denote inclusiveness, whereas parentheses indicate exclusiveness. You can see this with the following example:

[2.1, 3.0) means include version 2.1 up to, but not including, 3.0.
Import-Package: com.handsonliferay.employee; version="[2.1,3.0)"

This section has explained the basics of OSGi bundles; in the next section, you explore the OSGi bundle lifecycle.

OSGi Bundle Lifecycle

OSGi is a very dynamic platform, and bundles are the core of this mechanism. A bundle is a state-aware unit, meaning a bundle has several states that it can traverse through and know what state it is in. In traditional OSGi, you have a BundleActivator, where you have start() and stop() methods that are invoked upon the start and stop of the bundle, respectively.

Note

Activators are nothing but classes that implement the org.osgi.framework.BundleActivator interface.

The OSGi bundle lifecycle layer puts on bundles that can be dynamically started, installed, updated, stopped, and uninstalled. These bundles depend on the module layer for class loading, but it will add an API to manage modules at runtime. See Listing 1-1.
package com.handsonliferay.employee.osgi;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
public class Activator implements BundleActivator{
        @Override
        public void start(BundleContext context) throws Exception {
                System.out.println("Starting Hands On Liferay");
        }
        @Override
        public void stop(BundleContext context) throws Exception {
        System.out.println("Stopping Hands on Liferay");
        }
}
Listing 1-1

Bundle Activator Class

Bundle States

Now that you understand that the bundle is state-aware and uses the start and stop methods of a BundleActivator, it’s time to look at all the possible states that the OSGi bundle can traverse through (see Figure 1-3).

An illustration of the bundle states from installed, resolved, to uninstall. The resolved state leads to starting, active, and stopping. The starting to the active state is a policy.

Figure 1-3

Bundle states

  • Installed: This state depicts that the bundle has entered the OSGi runtime. Nothing else; it’s not available; running or resolving the dependencies means a bundle is available in the OSGi runtime. If a bundle stays in this state for a long time, that means that it’s waiting for some of the bundle’s dependencies to be met.

  • Resolved: This state shows that the bundle has been installed successfully, and the OSGi runtime resolves all the dependencies with the help of the installed bundles in the OSGi runtime. The bundle is available for the next stage, which is Start. Sometimes, the runtime will skip this state if a bundle is started by getting all the required dependencies.

  • Starting: At this state, the entry-level classes are initialized, and it’s a temporary state that the bundle goes through while it is starting and once all dependencies are met.

  • Active: This state shows that the bundle is up and running.

  • Stopping: This is a temporary state similar to starting; it goes through this when the bundle stops. All the destructors are called during this state.

  • Uninstalled: This states shows that the bundle has been removed successfully from the OSGi container.

A bundle lifecycle state will be managed, meaning a bundle can change its state by itself upon deployment, and developers and administrators can manage its state. There are various GUI and command-line tools available to do this. Gogo shell and Apache Felix are two of the most popular tools. You’ll see these tools in detail in later chapters.

This section has explained the OSGi bundle lifecycle; in the next section, you explore the OSGi components.

OSGi Components

Any Java class inside a bundle can be declared a component. This can be achieved with the help of declarative services (DS), which provide a service component model on top of the OSGi services. A component can publish itself as a service and make itself available to other components. Similarly, it can consume services published by already installed components.

OSGi components have an independent lifecycle and are reusable, which means you can stop them and start them again without reinstalling them. They will traverse through their lifecycle events again and again. They can have properties and activation policies. An OSGi bundle can have lifecycle methods for activation, deactivation, and configuration.

DS service components are marked with the @Component annotation; they implement or extend a service class. These service components can refer to and use each other’s services. The Service Component Runtime (SCR) registers component services and handles them by binding them to other components that reference them.

There are two parts to this process—service registration and service handling.
  • Service registration: When a module containing a service component is installed, the SCR creates a config, binds it with the specified service type, and makes a reference in the Service Registry.

  • Service handling: When a module referencing a service exposed from another module is installed, SCR searches the Service Registry for a component whose configuration matches the required service type. Once the component is found, SCR binds an instance of that service to the referring member.

Note

To understand in a nutshell, you can say—when a module with an exposed service is deployed, SCR registers it in the Service Registry and when a module importing a service is deployed, SCR searches for it in the Service Registry and returns its instance.

@Component annotation is a declaration to make the class an OSGi component. @Referance annotation marks a field to be injected with a service, and once the Service Registry finds the essential service, it is injected with the resolved service. It can only be used in a @Component class.

Note

For example, a class with a declarative services component, @Component, can use @Reference to bind to another OSGi service.

Let’s look at an OSGi component with an example; refer to Listing 1-2.
package com.apress.handsonliferay.portlet;
import com.handsonliferay.apress_service_builder.model.ApressBook;
@Component(
        immediate = true,
        service = EmpolyeeService.class
)
public class Employee implements EmpolyeeService {
}
Listing 1-2

OSGi @Component Declaration

This section has explained the OSGi component; in the next section, you explore OSGi services.

OSGi Services

An OSGi service is a Java class or interface and a service property, shown as name or value pairs. The OSGi service is nothing but a component registered to an OSGi container Service Registry (SR) by the Service Component Runtime (SCR). Listings 1-3 and 1-4 show how the service interface is declared and implemented.
package com.handsonliferay.service.api;
public interface EmployeeService{
        public String getEmpName();
        public void setEmpName(String name);
}
Listing 1-3

Service Interface

package com.handsonliferay.service.Impl;
import com.handsonliferay.employee.api.EmployeeService;
import org.osgi.service.component.annotations.Component;
@Component(
                immediate = true,
                service = EmpolyeeService.class
        )
public class EmployeeServiceImpl implements EmployeeService{
        @Override
        public String getEmpName() {
                return _name;
        }
        @Override
        public void setEmpName(String name) {
                _name = name;
        }
}
Listing 1-4

Service Implementation

Service Registry

The service layer contains a Service Registry and within that framework, services will be registered and used by other applications and clients. In the Service Registry, a bundle can register an implementation of a service. Lookup is performed via a Java interface and service properties by the bundles.

A bundle is responsible for the runtime service dependency management activities, including discovery, publication, and binding, as well as adapting to changes resulting from the dynamic of services in the bundle. Service providers and service requestors are both part of a bundle in OSGi.
  • In the Service Registry, service providers publish their services.

  • To find the services and bind to service providers, service requestors use the Service Registry.

It provides a mechanism to publish services to an OSGi container and dynamically look up and bind published services. It will hide the client’s implementation details, which will give loose coupling, and consuming clients may have the policies and filters for binding specific implementations.

Declarative Services

Declarative services are how OSGi handles problems due to instantiation. If you want to create two interfaces, instantiate classes using them, and provide a concrete instance of an interface, you need some way. Declarative services are the way to do this with less code implementation. An OSGi declarative service is also called a Service Component Runtime (SCR).

OSGi will also support XML configuration; this configuration is similar to the configuration that the Spring Framework supports. When you drop an XML configuration file into the OSGI-INF/ path inside your bundle JAR file, the parsing will happen automatically once the OSGi container starts the bundle. It allows you to declare and consume services via XML metadata and annotations. Figures 1-4 and 1-5 show how a bundle is registered in the Service Registry.

A service registry process model entails registering O S G I bundle book A P I to service and registering service to O S G I bundle book portlet via get and listen.

Figure 1-4

Service Registry process

The process of interface implementation registration and lookup is illustrated by a model of the O S G I service registry.

Figure 1-5

OSGi Service Registry

You must use the following header in the bundle’s MANIFEST file to identify the XML file by OSGi service component runtime:
  • Service-Component: OSGI-INF/com.handsonliferay.employees.configuration.xml

The standard declarative service annotations are listed here:
  • @Component

  • @Reference

  • @Activate

  • @Deactivate

  • @Modified

A concrete example is shown in Listing 1-5.
package com.handsonliferay.employee.component;
import com.handsonliferay.employee.serviceprovider.services.EmpService;
import org.osgi.service.component.annotations.Component;
@Component(
                name = com.handsonliferay.employees.EmployeeComponent
        )
public class EmployeesComponent {
        @Activate
        public void activate() {
                System.out.println( "Component is Activated");
        }
        @Deactivate
        public void deactivate() {
                System.out.println( "Component is Deactivated");
        }
        @Modified
        public void modified() {
                System.out.println( "Component is Modified" );
        }
        @Reference
        public void setEmpService(Empservice empservice) {
                System.out.println( "Component is setService()" );
                System.out.println( empservice.greetHello());
        }
}
Listing 1-5

Concrete Implementation Using Annotation

This section has explained the OSGi services; in the next section, you learn about Liferay’s OSGi architecture.

Liferay’s OSGi Architecture

You should now understand what an OSGi container is and how bundles and services work inside it. It’s time to look at how Liferay DXP is designed to have an embedded OSGi container to leverage OSGi features. All Liferay DXP applications, be it portlets, services, hooks, or anything else, are OSGi applications (bundle components), so they run inside embedded OSGi containers. But this does not mean standard OSGi applications cannot run inside; they can work well inside Liferay DXP’s OSGi container as well.

In the straightforward standard OSGi environment, an OSGi container works inside the Java runtime, and all the OSGi bundles run inside the OSGi container. But in the case of Liferay DXP, which embeds an OSGi container, there are two more layers between the Servlet container (Tomcat or any other server) and the Liferay Web application. Most of the services and functionalities in Liferay DXP leverage the OSGi component and service model. Figure 1-6 helps to elucidate this model.

A Liferay O S G I architecture model with four layers: Java, Apache Tomcat, Liferay, and O S G I runtime. The O S G I includes four Liferay applications.

Figure 1-6

Liferay OSGi architecture

This section has explained Liferay’s OSGi architecture; in the next section, you explore the main OSGi features.

OSGi Features

The OSGi application framework defines a standard dependency injection mechanism for Java components. This dependency injection mechanism is driven from the Spring Framework and is extended declaratively to register components in OSGi interfaces as services in OSGi SR. This is one of the most valuable features of OSGi.

OSGi uses a different kind of model for assembling bundles. OSGi tools include a model for assembling an application into a deployable unit. The unit may consist of several bundles and must consist of metadata that describes the version and dependencies.

The OSGi tools also include extensions beyond the OSGi Enterprise Expert Group specifications to provide complete integration of OSGi modularity with Java enterprise technologies.

To overcome the issues faced by applications of Java EE, OSGi modularity provides standard mechanisms. Here are the benefits provided by the OSGi framework:
  • OSGI provides adaptable changing requirements; applications are portable and easier to reengineer.

  • The framework provides a standardized form as part of the application server runtime, rather than a third-party library to the declarative assembly and simplified unit test of the Spring Framework.

  • The framework allows you to deploy a web application as a set of versioned OSGi bundles with a dynamic lifecycle to integrate with the Java EE programming model.

  • Application bundle dependencies and versions administration are also supported.

  • OSGI also supports simplifying and standardizing third-party library integration.

  • OSGI applications have the capability of accessing external bundle repositories.

  • The framework also provides isolation for enterprise applications composed of different versioned bundles with dynamic lifecycles.

  • The framework strengthens service-oriented design at the module level.

Summary

In this chapter, you explored the essential features of OSGi, including its architecture, its services and Service Registry, and a few other crucial topics that fall under the basics of OSGi with respect to Liferay DXP. You also explored bundles with a straightforward example, which should be enough to grasp the potential of OSGi. However, OSGi is a vast specification, so we encourage you to explore more on the Internet.

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

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