Can Spring Boot be used with OSGi? If not, any plans to have an OSGi Spring Boot (Apache Felix or Eclipse Equinox)? In my opinion, cloud applications must be highly modular and
Why not stick to the microservice patterns and just launch your OSGI app as a separate application communicating with your spring-boot services over their REST API. On the OSGI application side you can use Jetty / Jersey etc to easily manage REST communication
Yes, it's possible to run Spring Boot
apps in OSGI container.
First of all, you'll have to switch from Spring Boot jar
packaging to OSGI bundle
.
If you're using Maven
you can use org.apache.felix:maven-bundle-plugin
for doing that.
As Spring Boot
dependency jars are not valid OSGI
bundles, we should either make them valid bundles with bnd
tool or we can embed them into the bundle itself. That can be done with maven-bundle-plugin
configuration, particularly with <Embed-Dependency>
.
However, we need to start the bundle with Spring Boot
app somehow. The idea is to start Spring Boot in BundleActivator
:
@Import(AppConfig.class)
@SpringBootConfiguration
@EnableAutoConfiguration
public class SpringBootBundleActivator implements BundleActivator {
ConfigurableApplicationContext appContext;
@Override
public void start(BundleContext bundleContext) {
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
appContext = SpringApplication.run(SpringBootBundleActivator.class);
}
@Override
public void stop(BundleContext bundleContext) {
SpringApplication.exit(appContext, () -> 0);
}
}
You should also set context classloader to an OSGI classloader loading the bundle by Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
.
That is required because Spring
uses context classloader.
You can see this in action in my demo repo: https://github.com/StasKolodyuk/osgi-spring-boot-demo
I think it worth posting as separate answer (not everyone reads all comments to answers).
Excellent solution from @StasKolodyuk gives a way to run Spring Boot application in an OSGI environment.
But with limitation: Spring Boot's auto-mapping by annotation does not work because of lack of package scan support when run in OSGI.
Here is another trick, that finally allows Spring Boot app with components to be auto-taken from your code to be run in OSGI (I tested in Karaf).
Functional example is available at https://github.com/dimmik/osgi-spring-boot-demo
The trick is to provide appropriate ResourcePatternResolver to SpringApplication instance:
package by.kolodyuk.osgi.springboot;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.osgi.io.OsgiBundleResourcePatternResolver;
@SpringBootApplication
public class SpringBootBundleActivator implements BundleActivator {
ConfigurableApplicationContext appContext;
@Override
public void start(BundleContext bundleContext) {
// Set context classloader (main trick, to enable SpringBoot start at the first place)
Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());
// trick to enable scan: get osgi resource pattern resolver
OsgiBundleResourcePatternResolver resourceResolver = new OsgiBundleResourcePatternResolver(bundleContext.getBundle());
// and provide it to spring application
appContext = new SpringApplication(resourceResolver, SpringBootBundleActivator.class).run();
}
@Override
public void stop(BundleContext bundleContext) {
SpringApplication.exit(appContext, () -> 0);
}
public static void main(String[] args) {
SpringApplication.run(SpringBootBundleActivator.class);
}
}
There's actually plenty of good reasons for deploying Spring Boot into OSGi, the main one being performance, particularly startup performance if your Spring Boot service is a functional service (i.e. it starts, returns results, ends). An application I'm currently beta testing in Spring Boot starts up in ~ 0.5 seconds deployed to Equinox versus 3.5 seconds on its own. Other reasons might be integration to an OSGi based application or Java EE server.
That said, you can also run OSGi from Spring Boot, for performance reasons I would probably favour Concierge as an OSGi implementation over Felix or Equinox, simply due to its small size (unless your app needs all the features of the bigger implementations.
Another alternative would be to wrap the Spring libraries used by your Spring Boot application into MSF4J (from WSO2). This doesn't take much work and can give you a 10x faster startup with 1/10th the memory usage.
No, it doesn't support OSGi. Spring Boot is aimed to create microservices as packaged applications with every dependency and even the servlet containers packaged in an executable JAR, so it's highly modular and updateable, without the need to provide and configure an OSGi container.
Spring boot - well typical spring boot apps, are a little "fat" for osgi... if you are using the starter-web or jersey you'd need to add some sort of port determination scheme since ports are shared by all osgi "services" the system that the osgi runtime lives on.
The reason I would recommend avoiding spring-boot unless you can pare it down is the spring boot fat jar / war you create launches a sub class loader. This doesn't simplify things when you get confused about standard osgi class loader issues (com.whatever.someobject.MyClass not being the same in different bundles and classloaders because they are not "imported" from the same bundle that exports to all others) if there are any requirements for interbundle service communication.
I would suggest following guides to pare down to "spring boot core" if any exist you don't need the web listener and avoid all inter-bundle service interfaces that use any objects that aren't part of the standard imports (like core java se classes etc..). You only care about lifecycle right?