Include a REST API in Vaadin war app without using Spring boot - spring

I have a Vaadin (v23) app deployed as a war that provides a UI to other wars running in the same Tomcat instance. I now have a need to expose a couple of public endpoints in the Vaadin app via a REST API, but all of the examples I've found assume an app built on Spring-boot, which this isn't. Is there any way of adding a simple, public API to the Vaadin app without having to resort to rebuilding it as a Spring-boot application?
I using tried spring-webmvc with #RestController and overriding startup configurations, but all attempts to navigate to the API are ignored.

Thanks for the helpful comments. I ended up doing it the old-fashioned way, via a static inner class of my MainView:
#WebServlet("/api/*")
public static class CustomFormServlet extends HttpServlet {
...
}
It's an inner class to share some things with MainView. I'm sure there's better ways, but it works and it's only to serve a couple of public endpoints.

Related

Spring boot web app - not able to pick the static content

Spring boot web app with angular bundle was working fine earlier
Then I added a gradle dependency which is a library that is built within my organization to achieve some api calls. And after that the view/angular build is not picking up by spring boot application.
My project structure looks like below
web-app\
src\main\java\
WebApplication.java
\resources\public\index.html
\public\main.5214684651aera5146.js
\public\styles.3asd44654e1asd135fsdf.js
\public\other-angular-build-files
application.properties
I haven't added any view controllers/resource handlers by implementing WebMvcConfigurer. Spring boot was able to pick the index.html file as welcome page and I was able to see the page on http://localhost:8080.
But now it is not working, I have gone though below article and tried with different location of view - public, static, resources, META-INF\resources but nothing workout.
https://spring.io/blog/2013/12/19/serving-static-web-content-with-spring-boot
Can someone help how can I debug this issue or override MVC config so that my spring boot should pick the index.html as the welcome page.
In addition to M. Deinum answer you can try to restrict #ComponentScan only in your app and manually create beans from classes from library

Migrating Spring web application to Spring Boot

I have a web project, and I depoly it on tomcat easily. Infact I have a WebAppInitializer class that implements WebApplicationInitializer (this class it's really fat), as you know every application server that supports servlet 3.0, it can easily detect it and try to boot it. Now I wonder that it could be possible to use spring boot starter and without any further configuration, I pass my WebAppInitializer to it and spring boot based on my WebAppInitializer boots my project?
I just want to use the approach of spring-boot to deploy application on Tomcat and I don't want to use other spring-boot's facilities.
Yes, it's an old question. But I do not see an accepted answer and the one closest to a working one only has a link to an external resource. So here it is.
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container-context-initializer
28.4.2 Servlet Context Initialization
Embedded servlet containers do not directly execute the Servlet 3.0+
javax.servlet.ServletContainerInitializer interface or Spring’s
org.springframework.web.WebApplicationInitializer interface. This is
an intentional design decision intended to reduce the risk that third
party libraries designed to run inside a war may break Spring Boot
applications.
If you need to perform servlet context initialization in a Spring Boot
application, you should register a bean that implements the
org.springframework.boot.web.servlet.ServletContextInitializer
interface. The single onStartup method provides access to the
ServletContext and, if necessary, can easily be used as an adapter to
an existing WebApplicationInitializer.
Scanning for Servlets, Filters, and listeners
When using an embedded
container, automatic registration of classes annotated with
#WebServlet, #WebFilter, and #WebListener can be enabled by using
#ServletComponentScan.
[Tip] #ServletComponentScan has no effect in a standalone container,
where the container’s built-in discovery mechanisms are used instead.
I've tried it. It works
In my use case I have project containing a few dozens of webapps, designed to run on Tomcat as WAR. Lots of logics was neatly crafted into WebApplicationInitializers and it seemed there should be an easier way to reuse all this. Adding implements ServletContextInitializer to those initializers and exposing them as beans through #Configuration classes lit my webservers up with SpringBoot's embedded Tomcat.
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-create-a-deployable-war-file describes precisely how to do it
From the Spring Boot docs:
Servlet 3.0+ applications might translate pretty easily if they already use the Spring Servlet 3.0+ initializer support classes. Normally all the code from an existing WebApplicationInitializer can be moved into a SpringBootServletInitializer. If your existing application has more than one ApplicationContext (e.g. if it uses AbstractDispatcherServletInitializer) then you might be able to squash all your context sources into a single SpringApplication. The main complication you might encounter is if that doesn’t work and you need to maintain the context hierarchy. See the entry on building a hierarchy for examples. An existing parent context that contains web-specific features will usually need to be broken up so that all the ServletContextAware components are in the child context.
So yes, it's possible but you need to convert it to SpringBootServletInitializer, which seems to be quite similar.

Can Jersey resources classes be instantiated in Spring Boot if they're external?

I'm developing a Spring Boot app using Jersey for Rest.
The app will regularly have Rest controller and supporting classes added to it, basically adding small, specific logic at specific endpoints.
I'd like to be able to drop jars containing Jersey controllers into a directory specified by loader.path and have them created (including Spring auto-wiring) by just restarting the server.
Some Jersey resources form the base of the application and these are working fine because I've registered them via a ResourceConfig class but I'd really like to add resources without requiring a rebuild of the app.
Is this possible? I'm failing to get the resources created, I'm not even sure they're in the classpath.
In application.properites, I added:
-Dloader.path=lib/,resources/
and I've copied the jars into these folders, but they don't get instantiated.
Anybody got any ideas?

Can Spring Boot be used with OSGi? If not, any plans to have an OSGi Spring Boot?

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 updatable like OSGi offers.
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);
}
}
One possibility is to embed OSGi into your Spring Boot Application, to make some parts of your application accessible through the framework. See https://stackoverflow.com/a/4673904/173101 to see how you can start OSGi programmatically.
But in general there is nothing like "OSGi-Support". OSGi can be integrated into every Java-Application and, vice versa, you can package every Java-Code (also your Spring-Boot application) into an OSGi-bundle to start it within an OSGi container (although it probably wouldn't make much sense at all).
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?
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

Run code on a Spring MVC app deployment

I need to run some code when my application is deployed into a web container — and that class needs to be injectible by the Spring container.
I tried implementing ServletContextListener and registering the class as a listener, but injection wouldn't work.
What's the idiomatic way of doing this?
To make your class injectible by Spring container, you can extend SpringBeanAutowiringSupport.

Resources