The development of software programming today can be seen as a process of qualitative changes caused by quantitative changes. Initially, process-oriented program development requires developers to write a large amount of Process Code. As the Process Code continues to accumulate (volume changes to production), from the perspective of code maintenance and reuse, process development is becoming increasingly inadaptive, with qualitative changes, and object-oriented development is gradually adopted. Because object-oriented development encapsulates the process well and can well describe the Demand Model in actual applications from the object-oriented perspective, object-oriented development has gradually become the mainstream. Similarly, with the continuous application of object-oriented development (volume change), a large number of reusable classes and packages have emerged, making it increasingly difficult to maintain these classes/packages, although the object-oriented programming mechanism can be well adapted to the development of small-scale applications, the object-oriented mechanism is difficult to adapt as the application system grows, qualitative changes are generated, and component-oriented development is introduced into the development process. Currently, component-oriented development can still be considered as a kind of exploration, and there is no uniform standard to follow.
From another perspective, history is the basis of today's existence, and today is not the case without history. In the same way, application systems adopt component-oriented development and implementation, and components still need to be constructed by objects. To a certain extent, objects are the functional processes of encapsulation, And they complement each other. Whether from top to bottom or from bottom, in the implementation process of the application system requirement model, the system designer should fully focus on the three layers of components, objects and functional processes.
Osgi can be seen as an idea and basic environment for component development. The application system requirement model in the osgi environment requires developers to have a full understanding of components and object development. One of the criteria for measuring whether a system should be implemented is whether the system is loosely coupled and highly cohesive. The key to achieving this goal in object-oriented development is interface/abstract applications. osgi component-oriented development makes full use of the object class package encapsulation mechanism.
1. Bundle construction policy
Generally, when building an application system, we need to pay full attention to the business requirement model (domain model) of the application. Similarly, when using software code to implement the business requirement model, we should also pay full attention to the architecture model of the software system. When osgi technology is used to implement application systems, bundle components are displayed in front of us. At this time, we must first figure out What bundle we want to build.
1.1 define bundle construction requirements and implementation Granularity
From the perspective of the Demand Model, bundle can be seen as a function module implementation in the demand model. Therefore, before developing a bundle, system developers must clarify the functional requirements of the bundle. The boundaries of functional modules in the demand model can be large or small. Similarly, the Implementation granularity of bundle can be large or small. In extreme cases, a small bundle can only implement a small function, A large bundle can be used to implement the entire business system.
Take logging as an example. If logging is output only to the console, a class can implement the entire function. If logging is output to the database system, developers need to use a bundle to implement the entire function, or divide the logging function and the database storage that records information into two different bundle implementations, make a decision in two or more options. Generally, this issue does not require too much attention in the existing software development methods. However, in osgi development, developers must determine the implementation granularity of bundle as needed.
1.2 determine the bundle type.
In the process of object-oriented system development, we often design some common functions into tool classes, and encapsulate multiple tool classes into a tool class package. Because osgi development is built on object-oriented development, bundle development also has this feature. Another feature of osgi development is the introduction of service mechanisms. A bundle can publish the functions it provides into one or more services for other bundle to search and use. In addition, component development based on the osgi environment is also different from the common component development mode, because we can take advantage of some features provided by the osgi environment (see the next section to take full advantage of the features of the osgi environment ). To sum up, we can divide bundle into the following types (in actual development, there is no clear boundary ):
This bundle is essentially different from the tool class or tool class package in common development methods. It only publishes these tool classes to the osgi environment, and these tool classes do not depend on any osgi environment or feature. Generally, this bundle is suitable for the introduction of many third-party components to the osgi environment for osgi developers. For example, we can change the Toolkit released by Apache open source project log4j by modifying manifest under its META-INF directory. in the MF file, you can directly encapsulate the metadata information of the bundle into a tool bundle that can be used by other bundle in the osgi environment.
- Bundle dependent on osgi features
Such bundle can be referenced by other bundle to provide functions for it. However, such bundle cannot be used without the osgi runtime environment. For example, a bundle that provides the data cache function may use the data storage area of the bundle to cache data. The location of the data storage area of the bundle is transparent to the bundle developers (see the next section to make full use of the features of the osgi environment ). If you want to migrate the data cache function of this bundle to an osgi runtime environment, you must modify it to implement the processing function for the cache location.
- Reference and/or publish osgi service bundle
Such bundle can also be considered as a bundle dependent on the osgi feature. The only difference is that the bundle fully utilizes the service features in osgi and references the services and (or) released by other bundle) release your own services to the osgi environment. The service mechanism provided by the osgi environment allows the implementation of the system to follow the maximum loose coupling.
1.3 hide the functional interfaces of bundle
In addition to dealing with the development scale of the system, component development has the most important goal of reducing system coupling. When using osgi bundle for development, developers should try to block the internal implementation mechanism of the functions provided by bundle, and only expose the interactive interfaces provided by other bundle. In osgi, this can be implemented in two ways: one is the class package that is referenced by other bundle through export, and the other is to publish services to the osgi environment.
In equinox, class packages implemented inside the bundle are usually marked with "internal", such as org. Eclipse. Equinox. Internal. cm. reliablefile. A class package labeled as "internal" is usually not included in the export list, or marked by the "x-internal" attribute (this attribute is provided only in the eclipse development environment ).
A good mechanism for hiding bundle functional interfaces is through the osgi service. Developers only release their own bundle Interface Class packages to other bundle for visibility. At the same time, they register the implementation of function interfaces as services in osgi, other bundle obtains the service by searching the osgi service registry and calls the service function through the service interface.
1.4 apply the standard services provided by osgi as much as possible
The osgi Alliance defines standard services for some frequently used functions, such as application management services, log services, event services, configuration management services, user management services, and HTTP Services. If the osgi environment used by the developer provides the implementation of the above services and these services meet the requirements of the system, the developer should try to use them as much as possible, instead of defining your own implementations for these similar features.
2. Make full use of the characteristics of the osgi Environment
2.1 dynamic characteristics of the osgi Environment
Osgi is a dynamic environment. Changes in the internal state of the osgi runtime environment interact through the event publishing/listening mechanism. Developers should make full use of the event listening mechanism in the osgi environment when building bundle. For example, developers can register a bundle event listener in their bundle to handle events such as installation, startup, stop, and uninstallation of other bundle.
There are three main types of events in the osgi runtime environment:
StartedFramework started
ErrorAn error occurs when a bundle is started.
WarningA bundle triggers a warning.
InfoA bundle triggers an info-type event.
Packages_refreshedPackageadmin. refreshpackage operation completed
Startlevel_changedStartlevel. setstartlevel operation completed
InstalledThis event is released after bundle is installed in the osgi environment.
ResolvedBundle is successfully parsed.
Lazy_activationBundle will be delayed for activation
StartingBundle is being activated
StartedBundle activated successfully
StoppingBundle stopped
StoppedBundle is being stopped
UpdatedBundle updated
UnresolvedBundle is unresolved
UninstalledBundle uninstalled
RegisteredService registered
ModifiedService modified
UnregisteringThe service is being canceled
For more information about the above events, see the osgi API documentation.
The following code demonstrates how to implement the frameworklistener, bundlelistener, and servicelistener interfaces in the bundle component, and use bundlecontext to register and listen to various events in the osgi environment:
Package com. example;
Import org. osgi. Framework. bundleactivator;
Import org. osgi. Framework. bundlecontext;
Import org. osgi. Framework. bundleevent;
Import org. osgi. Framework. bundlelistener;
Import org. osgi. Framework. frameworkevent;
Import org. osgi. Framework. frameworklistener;
Import org. osgi. Framework. serviceevent;
Import org. osgi. Framework. servicelistener;
Public class activator implements bundleactivator, frameworklistener,
Bundlelistener, servicelistener {
Public void start (bundlecontext context) throws exception {
// Register the listener
Context. addframeworklistener (this );
Context. addbundlelistener (this );
Context. addservicelistener (this );
}
Public void stop (bundlecontext context) throws exception {
}
// Process framework events
Public void frameworkevent (frameworkevent event ){
If (event. GetType () & frameworkevent. Error )! = 0 ){
System. Err. println ("framework error:" + event. getbundle ());
}
}
// Handle bundle events
Public void bundlechanged (bundleevent event ){
If (event. GetType () & bundleevent. Started )! = 0 ){
System. Err. println ("bundle started:" + event. getbundle ());
} Else if (event. GetType () & bundleevent. Stopped )! = 0 ){
System. Err. println ("bundle stopped:" + event. getbundle ());
}
}
// Process service events
Public void servicechanged (serviceevent event ){
If (event. GetType () & serviceevent. Registered )! = 0 ){
System. Err. println ("service registered :"
+ Event. getservicereference ());
}
}
}
2.2 bundle lifecycle and osgi Context
In the Java virtual machine running environment, the life cycle of a class begins with loading a virtual machine, and ends with uninstalling or stopping a virtual machine. Developers do not need to intervene. In the osgi environment, developers can participate in the bundle lifecycle process and obtain the context of the bundle running in the osgi environment.
By implementing Org. osgi. framework. bundleactivator interface, and specify the implementation class of this interface in the bundle metadata header attribute "bundle-Activator", such as "bundle-Activator: COM. example. activator ". When a bundle is started or stopped, the osgi framework injects the running context bundlecontext of the bundle into the implementation class instance, developers can use this context to access osgi environment information, access other bundle in the osgi environment, and register event monitoring, publish or reference services.
The following code snippet demonstrates how to use the bundle running context to interact with the osgi environment:
Public void start (bundlecontext context) throws exception {
// Obtain all installed bundle in the osgi Environment
For (bundle Bundle: context. getbundles ()){
System. Out. println ("bundle symbolic name :"
+ Bundle. getsymbolicname ());
}
// Obtain attributes in the osgi runtime environment (the search scope includes system attributes)
System. Out. println ("osgi. Framework ="
+ Context. getproperty ("osgi. Framework "));
// Register other bundle
Context. installbundle ("file: C: // test_bundle_1.0.0.jar ");
// Construct the datacache. File file in the data storage area of the bundle.
Context. getdatafile ("datacache. File ");
// Register the listener
Context. addframeworklistener (this );
Context. addbundlelistener (this );
Context. addservicelistener (this );
}
2.3 bundle resource management features
In general application system development, developers usually need to consider the storage path of configuration files and the storage path of temporary system files. Because bundle is actually composed of a group of file resources in the osgi environment, developers can make full use of this feature during bundle development and use bundle to store configuration information related to bundle. The osgi environment constructs a serialized data storage zone for each bundle at runtime. developers can use this storage zone to store the temporary information generated by the bundle at runtime.
The following code snippet shows how to obtain resources in the bundle through the functional interfaces provided by Bundle:
- Context. getbundle. getentry ("") only queries bundle internal resources and returns the bundle URL, which is related to implementation, for example, bundleentry: // 256/
- Context. getbundle. getentry ("/") only queries bundle internal resources and returns the bundle URL, which is related to implementation.
- Context. getbundle. getentry ("/build. properties ") only query bundle internal resources and return bundle internal build. the URL of the properties file, which is related to the implementation, such as bundleentry: // 256/build. properties
- Context. getbundle (). getresource ("build. properties ") use the bundle's classloader to query Resources in the bundle class. If the bundle cannot be parsed (resolved), only the internal resources of the bundle are queried and the internal build of the bundle is returned. the URL of the properties file, which is related to the implementation.
After obtaining the URL of the preceding resource, the developer can convert it to a File url related to the bundle running system, for example, file: C:/test_bundle_1.0.0.
The following code snippet shows how to obtain resources in the data storage zone during bundle running through the bundlecontext interface:
- Context. getdatafile ("") returns the location of the data storage zone when the bundle is running, for example, E:/worksapce /. metadata /. plugins/org. eclipse. PDDE. core/cobweb/org. eclipse. osgi, bundles, 256, and data
- Context. getdatafile ("config. xml") returns the config. xml file in the data storage zone when the bundle is running. If the file does not exist, the file is created.
2.4 Service-oriented component features
The osgi service layer provides a dynamic service release, binding, and search model. The so-called service refers to a Java object that implements some APIs. In object-oriented system development, interfaces play a crucial role in separating system coupling. The osgi environment makes full use of this.
The osgi runtime framework maintains a service registry. A bundle can publish a service to the Service Registry through the bundle context (only the interface implemented by the Service, the instance and attribute of the service are specified ), you can also use the bundle context to find the services registered by other bundle from the service registry based on the Interface Name and service attributes. In this mode, the coupling relationship between bundle only exists at the interface level, but it is irrelevant to the interface implementation.
The following code snippets show how to publish services to the osgi service registry and how to find Services released by other Bundle:
Public void start (bundlecontext context) throws exception {
// Obtain the service reference of the osgi Log Service
Servicereference logsr = context. getservicereference (logservice. Class. getname ());
// Obtain the logservice instance based on the log service reference
Log = (logservice) Context. getservice (logsr );
// Create a stringservice interface service instance
Stringservice Ss = new stringserviceimpl (log );
// Set stringservice attributes
Hashtable
// Use bundlecontext to register and publish the stringservice
Context. registerservice (stringservice. Class. getname (), SS, properties );
}
3. Bundle development practices
Practice 1. Construct a utility bundle
This example encapsulates a third-party log4j Toolkit (version 1.2.13) as an osgi bundle component. The procedure is as follows:
- Step 1: create the "org. Apache. log4j" directory under the C root directory and copy the log4j. 1.2.13.jar file to the directory;
- Step 2: Build the META-INF directory under the "org. Apache. log4j" directory, and create the file named "manifest. MF" under this directory;
- Step 3: edit the "manifest. MF" file and add the following content to the file:
Manifest-version: 1.0
Bundle-manifestversion: 2
Bundle-Name: Apache log4j
Bundle-symbolicname: org. Apache. log4j
Bundle-version: 1.2.13
Bundle-classpath:.; log4j. 1.2.13.jar
Export-package: org. Apache. log4j; version = "1.2.13 ",
Org. Apache. log4j. config; version = "1.2.13 ",
Org. Apache. log4j. helpers; version = "1.2.13 ",
Org. Apache. log4j. JDBC; version = "1.2.13 ",
Org. Apache. log4j. JMX; version = "1.2.13 ",
Org.apache.log4j.net; version = "1.2.13 ",
Org. Apache. log4j. Or; version = "1.2.13 ",
Org. Apache. log4j. Or. JMS; version = "1.2.13 ",
Org. Apache. log4j. Or. Sax; version = "1.2.13 ",
Org. Apache. log4j. SPI; version = "1.2.13 ",
Org. Apache. log4j. varia; version = "1.2.13 ",
Org. Apache. log4j. xml; version = "1.2.13"
After the preceding steps, the osgi bundle named org. Apache. log4j has been built. Developers can reference the log4j class package in other bundle as in normal development mode to obtain the logger instance logging.
Practice 2. Build a service-type bundle
The bundle in this example is composed of the stringservice interface, stringserviceimpl class, and activator class. This bundle releases the stringservice interface service and references the osgi standard logservice service.
The stringservice interface is the string service implementation interface released by bundle in this example.
Package com. example;
Public interface stringservice {
String Concat (string str1, string str2 );
}
The stringserviceimpl class is the implementation class of the stringservice interface, which uses the osgi standard logservice to record logs.
Package com. example. Internal;
Import org. osgi. Service. log. logservice;
Import com. example. stringservice;
Public class stringserviceimpl implements stringservice {
Private logservice log;
Public stringserviceimpl (logservice log ){
This. log = log;
}
Public String Concat (string str1, string str2 ){
String STR = str1 + str2;
If (log! = NULL)
Log. Log (logservice. log_info, "Concat string result is" + Str );
Return STR;
}
}
The activator class is the bundle lifecycle entry. It obtains the osgi logservice Based on the injected bundlecontext and registers the stringservice.
Package com. example. Internal;
Import java. util. hashtable;
Import org. osgi. Framework. bundleactivator;
Import org. osgi. Framework. bundlecontext;
Import org. osgi. Framework. constants;
Import org. osgi. Framework. servicereference;
Import org. osgi. Service. log. logservice;
Import com. example. stringservice;
Public class activator implements bundleactivator {
Private logservice log;
Public void start (bundlecontext context) throws exception {
// Obtain the service reference of the osgi Log Service
Servicereference logsr = context. getservicereference (logservice. Class. getname ());
// Obtain the logservice instance based on the log service reference
Log = (logservice) Context. getservice (logsr );
// Create a stringservice interface service instance
Stringservice Ss = new stringserviceimpl (log );
// Set stringservice attributes
Hashtable
// Use bundlecontext to register and publish the stringservice
Context. registerservice (stringservice. Class. getname (), SS, properties );
}
Public void stop (bundlecontext context) throws exception {
}
}
The following is a list of bundle metadata:
Manifest-version: 1.0
Bundle-manifestversion: 2
Bundle-Name: Example plug-in
Bundle-symbolicname: COM. Example
Bundle-version: 1.0.0
Bundle-Activator: COM. example. Internal. Activator
Bundle-vendor: egrid
Eclipse-lazystart: True
Import-package: org. osgi. Framework; version = "1.3.0 ",
Org. osgi. Service. log; version = "1.3.0"
Export-package: COM. Example
4. Conclusion
Through the discussion in this article, developers can clarify some decisions and precautions for bundle development in the osgi environment. In the next article, we will discuss in detail the details of bundle programming in the osgi environment. Click here to download this article.