Java Web Application Integration OSGI
A simple understanding of OSGI
Just as Java Web applications need to run in containers such as Tomcat and Weblogic. OSGI packages developed by programmers also need to run in OSGI containers. Currently, mainstream OSGI containers include Apache Felix and Eclipse Equinox. The OSGI package is calledBundle
.
Bundle
The entire lifecycle is managed with the OSGI container. You canBundle
Load and detach to achieve hot deployment.
Bundle
It is a black box for external programs. It only registers a service interface for external calls to the OSGI container, but is invisible to the external. DifferentBundle
The call between them also needs to be implemented through the OSGI container.
How to introduce jar into Bundle
As mentioned earlierBundle
It is a black box. All his implementations are packaged into his own "box. In developmentBundle
You cannot reference some open-source packages such as Spring and Apache commons. InBundle
During packaging, you canBundle
Dependent jar andBundle
The source code is packaged into a package (all-in-one ). The packaging result is that the output package is too large, usually several megabytes or dozens of megabytes, which is unacceptable. The following describes a better method.
Contract between Bundle and OSGI container
Bundle
You canMANIFEST.MF
In the configuration file, declare the packages he wants to run and the version of these packages !!! While the OSGI container is loadingBundle
Will beBundle
ProvideBundle
Required package !!!When starting the OSGI container, You need to define it in the OSGI configuration fileorg.osgi.framework.system.packages.extra
, Attribute. This property definesThe packages and packages available in the OSGI container. OSGI LoadingBundle
The packages that can be provided by the Bundle and the packages and version lists required by the Bundle are matched. If the matching fails, an exception is thrown:
Unable to execute command on bundle 248: Unresolved constraint in bundlecom.osgi.demo2 [248]: Unable to resolve 248.0: missing requirement [248.0] osgi.wiring.package; (&(osgi.wiring.package=org.osgi.framework)(version>=1.8.0)(!(version>=2.0.0)))
It may also loadBundle
Pass, but runBundle
TimesClassNotFoundException
. These exceptions are caused by the absence of configuration files. After understanding the configuration method of the configuration file, the 60% exception can be solved.
Import-Package
InBundle
OfImport-Package
The attribute is configured in the following format:
<!--pom.xml--> <Import-Package>javax.servlet,javax.servlet.http,org.xml.sax.*,org.springframework.beans.factory.xml;org.springframework.beans.factory.config;version=4.1.1.RELEASE,org.springframework.util.*;version="[2.5,5.0]"</Import-Package>
- Separate packages with commas
- You can use wildcard characters such as * to indicate all packages in this package. If you do not want to use wildcards, other packages under the same package can use one another.
;
Separated.
- If you want to specify the version of the package, add
; Version = "[minimum version, Maximum version]"
. Where[
Equal to or greater,]
Indicates less than or equal,)
Indicates less.
Org. osgi. framework. system. packages. extra
Syntax andImpirt-Package
Basically consistent, justorg.osgi.framework.system.packages.extra
Wildcards are not supported.
Incorrect Method
org.springframework.beans.factory.*;version=4.1.1.RELEASE
The correct method:
org.springframework.beans.factory.xml;org.springframework.beans.factory.config;version=4.1.1.RELEASE,
Class file loading
In normal development, loading a Class will usethis.getClassLoader().loadClass
. However, this method is used to loadBundle
Theclass
Will fail, will reportClassNotFoundException
. InBundle
Use the following method to replaceclassLoader.loadClass
Method
public void start(BundleContext context) throws Exception { Class classType = context.loadClass(name); }
Question about loading Spring configuration files in Bundle
BecauseBundle
LoadClass
Will cause errors when loading the Spring configuration file. Therefore, you need to change the ClassLoader required for Spring startup so that it can callBundleContext.loadClass
To load the Class.
String xmlPath = "";ClassLoader classLoader = new ClassLoader(ClassUtils.getDefaultClassLoader()) { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { return currentBundle.loadClass(name); } catch (ClassNotFoundException e) { return super.loadClass(name); } } }; DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); beanFactory.setBeanClassLoader(classLoader); GenericApplicationContext ctx = new GenericApplicationContext(beanFactory); ctx.setClassLoader(classLoader); DefaultResourceLoader resourceLoader = new DefaultResourceLoader(classLoader) { @Override public void setClassLoader(ClassLoader classLoader) { if (this.getClassLoader() == null) { super.setClassLoader(classLoader); } } }; ctx.setResourceLoader(resourceLoader); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(ctx); reader.loadBeanDefinitions(xmlPath); ctx.refresh();
Web Application Integration OSGI
Selected hereApache Felix
Mainly becauseApache Felix
Is a top-level project of Apache. Active Communities, complete support for OSGI functions, and comprehensive documentation examples.
Actually, OSGI supports two deployment methods.Bundle
.
- Independently deploy OSGI containers to provide external Web services through OSGI Web middleware (currently only jetty)
- Embed OSGI containers into Web applications, and then use Weblogic and other middleware to run Web applications.
From the overall consideration of the project, we chose the second solution.
BundleActivator Development
DevelopmentBundle
First, you need to developBundleActivator
. OSGI LoadingBundle
First, callBundleActivator
Ofstart
MethodBundle
. DetachBundle
Will callstop
To release the resource.
public void start(BundleContext context) throws Exception;public void stop(BundleContext context) throws Exception;
Instart
Method callcontext.registerService
To complete external service registration.
Hashtable props = new Hashtable();props.put("servlet-pattern", new String[]{"/login","/logout"})ServiceRegistration servlet = context.registerService(Servlet.class, new DispatcherServlet(), props);
- The first parameter of the context. registerService method indicates the service type. Because we provide the Web request service, the service type here is
javax.servlet.Servlet
, So you needjavax.servlet.Servlet
Passed in to Method
- The second parameter is the service processing class. A routing Servlet is configured here, and a corresponding program will be used to process specific requests.
- The third parameter is
Bundle
Attributes of the external service. In the exampleHashtable
Defined inBundle
Supportedservlet-pattern
. The Web application where the OSGI container is located passes throughBundle
Definedservlet-pattern
Determine whether to distribute customer requests to thisBundle
.servlet-pattern
This name is random and is not the name required by the OSGI framework.
Application Service integration OSGI container
- First, add the following dependencies to the project:
<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>
- Development
ServletContextListener
Used to initialize and start the OSGI container
SeeApache Felix
Provided example program. TheProvisionActivator
Yes/WEB-INF/bundles/
, LoadBundle
Package. (Of course, The ProvisionActivator provided in this example does not containBundle
Automatic Discovery of registration and other mechanisms, these logics need to be added on their own. Refer to the subsequent automatic Bundle loading chapter)
Route Development
The above configuration only loads the OSGI container into the Web application. You also need to modify the Web application routing code.
- In
Bundle
After being loaded to the OSGI container, you can usebundleContext.getBundles()
Obtain all the loadedBundle
.
- Yes
Bundle
Ofbundle.getRegisteredServices()
Method to obtainBundle
All external services.getRegisteredServices
Method returnServiceReference
. We callcontext.registerService(Servlet.class, new DispatcherServlet(), props)
We have registered a service,getRegisteredServices
Only one data is returned.ServiceReference
Object.
- Obtain
Bundle
Services available
You can useServiceReference
ObjectgetProperty
Method acquisitioncontext.registerService
Passed inprops
. In this way, we can callServiceReference.getProperty
Method to obtainBundle
The service that can be provided.
- Through the interface provided above, we can
Bundle
CorrespondingServiceReference
AndBundle
Correspondingservlet-pattern
Cache. After a user request enters the application serverservlet-pattern
Can be judgedBundle
Whether the Service requested by the user can be provided by callingBundle
Provided services.
ServiceReference sr = cache.get(bundleName); HttpServlet servlet = (HttpServlet) this.bundleContext.getService(sr); servlet.service(request, response);
Automatic Bundle Loading
InApache Felix
TheProvisionActivator
, Will only be loaded at system startup/WEB-INF/bundles/
DirectoryBundle
. WhenBundle
When the file is updated,Bundle
. SoBundle
The logic for automatic loading needs to be added by ourselves. The following provides implementation ideas:
- For the first time
Bundle
Time, recordBundle
The last update time corresponding to the package.
- Create an independent thread in the program for scanning
/WEB-INF/bundles/
Directory, compare them one by oneBundle
. If it is inconsistent with the memory, it is obtained from OSGI.Bundle
Object and then call itsstop
Anduninstall
Method To uninstall it from the OSGI container.
- After uninstallation, call
bundleContext.installBundle
Andbundle.start
Add the latestBundle
Load to OSGI container
BundleListener
The last problem can be solved through the above method.Bundle
. But as we introduced just now, in the routing program, we will cache allBundle
CorrespondingServiceReference
And allBundle
Correspondingservlet-pattern
. SoBundle
After automatic update, we also need to update the cache in the routing program synchronously.
You canbundleContext
Medium RegistrationBundleListener
WhenBundle
After the status is updatedBundleListener
OfbundleChanged
Callback method. Then we canbundleChanged
Write the logic for updating the route cache in the callback Method
this.bundleContext.addBundleListener(new BundleListener() { @Override public void bundleChanged(BundleEvent event) { if (event.getType() == BundleEvent.STARTED) { initBundle(event.getBundle()); } else if (event.getType() == BundleEvent.UNINSTALLED) { String name = event.getBundle().getSymbolicName(); indexes.remove(name); } } });