A simple understanding of OSGi
Just like a Java Web application needs to run in a container such as Tomcat, WebLogic. The OSGi packages developed by programmers also need to be run in OSGi containers. Currently, the main OSGi containers include Apache Felix and Eclipse Equinox. OSGi packages are called in OSGi Bundle
. Bundle
the entire life cycle is managed by the OSGi container. You can Bundle
implement a hot deployment by loading and unloading without stopping the service. It Bundle
's a black box for an external program. He simply registers the service interface for external invocation into the OSGi container, and the implementation is not visible to the outside. Bundle
calls between the different, also need to be implemented through the OSGi container.
How Bundles introduce jars
Just now Bundle
it was a black box, and all of his implementations were wrapped up in the box. In the development Bundle
, you can not avoid quoting some such as spring, Apache Commons and other open source packages. In order to Bundle
package, you can package the current Bundle
dependent jar with Bundle
The source code is packaged into a package (All-in-one). The result of this package is that the package is too large, often a few trillion or more than 10 trillion, so of course we are unacceptable. A better approach is described below.
Bundle and OSGi container contract
___ Bundle
can declare in the MANIFEST.MF
configuration file The packages he wants to run and the versions of those packages!!! The OSGi container, Bundle
when loaded, Bundle
provides the Bundle
required package!!! ___ When starting an OSGi container, you need to define the properties in the OSGi configuration file org.osgi.framework.system.packages.extra
. This property defines the package that the OSGi container can provide and the version of the package . When OSGi loads Bundle
, it will match the packages that he can provide and the packages and version lists that the bundle needs. If the match is unsuccessful, the exception is thrown directly:
Unable to execute command on bundle 248:unresolved constraint in Bundlecom.osgi.demo2 [248]: Unable to resolve 248.0:mis Sing requirement [248.0] osgi.wiring.package; (& (Osgi.wiring.package=org.osgi.framework) (version>=1.8.0) (! ( version>=2.0.0)))
It may also load Bundle
through, but run Bundle
times ClassNotFoundException
. These exceptions are caused by configuration files that are not configured. Understanding the configuration file configuration method, you can resolve 60% of the exception.
Import-package
The Bundle
properties in are configured in the Import-Package
following format:
<!-- Pom.xml - < Import-package > Javax.servlet,javax.servlet.http,org.xml.sax.*,org.springframework.beans.factory.xml;o Rg.springframework.beans.factory.config;version=4.1.1.release,org.springframework.util.*;version= "[2.5,5.0]" </import-package>
- Separated by commas between packages and packages
- You can use a wildcard character such as this to represent all packages under this package. If you do not want to use wildcards, the other packages under the same package can be delimited with each other
;
.
- If you need to specify a version of the package, add it after the package
;version="[最低版本,最高版本]"
. Where the [
representation is greater than or equal, ]
represents less than or equal, and )
represents less than.
Org.osgi.framework.system.packages.extra
The syntax is Impirt-Package
consistent with the basic, but org.osgi.framework.system.packages.extra
wildcard characters are not supported.
Org.springframework.beans.factory.*;version=4.1.1.release
Org.springframework.beans.factory.xml;org.springframework.beans.factory.config;version=4.1.1.release,
class file loading
In our usual development there are some cases where loading a class will be used this.getClassLoader().loadClass
. However, the class that is written in this method Bundle
class
will fail and will be reported ClassNotFoundException
. Bundle
Replace the method when you need to use the following classLoader.loadClass
methods
public void Start (Bundlecontext context) throws Exception { Class ClassType = context.loadclass (name);}
Issues when loading spring configuration files in bundles
Because Bundle
Class
of the loaded attributes, it causes the spring profile to load in the wrong times. So you need to change the classloader required for spring to start, making it call BundleContext.loadClass
to load class.
String Xmlpath = ""; ClassLoader ClassLoader=NewClassLoader (Classutils.getdefaultclassloader ()) {@Override PublicClass<?> loadclass (String name)throwsClassNotFoundException {Try { returnCurrentbundle.loadclass (name); } Catch(ClassNotFoundException e) {return Super. LoadClass (name); } } }; Defaultlistablebeanfactory beanfactory=Newdefaultlistablebeanfactory (); Beanfactory.setbeanclassloader (ClassLoader); Genericapplicationcontext CTX=NewGenericapplicationcontext (beanfactory); Ctx.setclassloader (ClassLoader); Defaultresourceloader Resourceloader=NewDefaultresourceloader (classLoader) {@Override Public voidSetclassloader (ClassLoader ClassLoader) {if( This. getClassLoader () = =NULL) { Super. Setclassloader (ClassLoader); } } }; Ctx.setresourceloader (Resourceloader); Xmlbeandefinitionreader Reader=NewXmlbeandefinitionreader (CTX); Reader.loadbeandefinitions (Xmlpath); Ctx.refresh ();
Web Application Integration OSGi
This has been chosen Apache Felix
to develop, mainly because Apache Felix
it is the top-level project of Apache. Community active, support for OSGi functionality is complete, and documentation examples are more comprehensive. In fact, OSGi supports two ways of deploying Bundle
.
- Deploy OSGi containers separately and provide Web services externally via OSGi's own web middleware (currently only jetty)
- Embed OSGi containers in a web app, then use middleware such as WebLogic to run web Apps
From the overall consideration of the project, we chose the second option.
Bundleactivator Development
Development Bundle
, you need to develop one first BundleActivator
. When OSGi is loaded Bundle
, the method that is called first is BundleActivator
start
Bundle
initialized. When unloaded Bundle
, methods are called stop
to release the resource.
Public void throws Exception; Public void throws Exception;
Called in the start
method context.registerService
to complete the registration of the external service.
New Hashtable ();p rops.put(new string[]{"/login", "/logout"= Context.registerservice ( Servlet. class New Dispatcherservlet (), props);
- The first parameter of the Context.registerservice method represents the type of service, because we provide a Web request service, so the service type here is one
javax.servlet.Servlet
, so you need to javax.servlet.Servlet
pass in the method
- The second parameter is the service processing class, where a routing servlet is configured, followed by a corresponding program to process the specific request.
- The third parameter is a
Bundle
property that provides services externally. In the example, the Hashtable
support is defined in Bundle
servlet-pattern
. The web app where the OSGi container is located Bundle
servlet-pattern
determines whether customer requests are distributed to this by definition Bundle
. servlet-pattern
This name is random, not the name required by the OSGi framework.
Application Services Integrated OSGi container
- First the project needs to add the following dependencies
<Dependency> <groupId>Org.apache.felix</groupId> <Artifactid>Org.apache.felix.framework</Artifactid> <version>5.6.10</version> </Dependency> <Dependency> <groupId>Org.apache.felix</groupId> <Artifactid>Org.apache.felix.http.bundle</Artifactid> <version>3.0.0</version> </Dependency> <Dependency> <groupId>Org.apache.felix</groupId> <Artifactid>Org.apache.felix.http.bridge</Artifactid> <version>3.0.18</version> </Dependency> <Dependency> <groupId>Org.apache.felix</groupId> <Artifactid>Org.apache.felix.http.proxy</Artifactid> <version>3.0.0</version> </Dependency>
< Listener > < Listener-class >org.apache.felix.http.proxy.ProxyListener</listener-class> </listener>
- Developed
ServletContextListener
to initialize and launch the OSGi container, please refer Apache Felix
to the example program provided. The examples provided in the ProvisionActivator
package that will scan /WEB-INF/bundles/
and load them Bundle
. (Of course, the provisionactivator provided in the example do not Bundle
have mechanisms such as Autodiscover registration, which need to be increased on their own.) Please refer to the subsequent bundle auto-load section)
Routing development
With the above configuration, only the OSGi container is loaded into the Web application. You also need to modify the code for the Web application route.
Bundle
after being loaded into the OSGi container, you can bundleContext.getBundles()
get all the loaded in the OSGi container by means of the method Bundle
.
- A method that can be called
Bundle
bundle.getRegisteredServices()
to obtain Bundle
all the service services that are provided externally. getRegisteredServices
ServiceReference
The array returned by the method. In the preceding article we call context.registerService(Servlet.class, new DispatcherServlet(), props)
we have registered a service that getRegisteredServices
returns only one object for the data ServiceReference
.
- Gets the value that can be supplied by the method in the
Bundle
ServiceReference
object that getProperty
gets context.registerService
passed in props
. This allows us ServiceReference.getProperty
to obtain the Bundle
services that can be provided by invoking the method.
- Through the interface provided above, we can
Bundle
cache the corresponding ServiceReference
and Bundle
corresponding servlet-pattern
. When the user requests to enter the application server, through the cache servlet-pattern
can determine Bundle
whether to provide the service requested by the user, if you can provide the following way to invoke the Bundle
service provided.
servicereference sr =this. Bundlecontext.getservice (SR); Servlet.service (request, response);
Bundle Automatic Loading
In the Apache Felix
example provided ProvisionActivator
, only the directory is loaded at system startup /WEB-INF/bundles/
Bundle
. When the files under the folder Bundle
are updated, the OSGi container is not automatically updated Bundle
. So Bundle
the logic of automatic loading needs our own increase. Here are some ideas for implementation:
- When the folder is loaded for the first time
Bundle
, the Bundle
last update time corresponding to the package is recorded.
- Create a separate thread in the program to scan the
/WEB-INF/bundles/
directory, comparing Bundle
the update time by one. If it is not in memory, get the object from OSGi Bundle
and then call it and the stop
uninstall
method to unload it from the OSGi container.
- After uninstalling, then invoking
bundleContext.installBundle
and bundle.start
loading the latest Bundle
into the OSGi container
Bundlelistener
The last problem, by the way above, can be implemented Bundle
by automatic loading. But just now we've introduced that in the routing program, we cache all the corresponding and all the corresponding in the OSGi container Bundle
ServiceReference
Bundle
servlet-pattern
. So Bundle
after Automatic Updates, we also need to update the cache in the routing program synchronously. bundleContext
BundleListener
Bundle
BundleListener
The callback method that can be called when the state in the OSGi container is updated by registering in bundleChanged
. Then we can bundleChanged
write the logic to update the route cache in the callback method
this. Bundlecontext.addbundlelistener (new Bundlelistener () { @Override publicvoid bundlechanged (bundleevent event) { if (event.gettype () = = bundleevent.started) { initbundle (Event.getbundle ()); Else if (Event.gettype () = = bundleevent.uninstalled) {= event.getbundle (). Getsymbolicname (); Indexes.remove (name); } } });
Java Web Application Integration OSGi