Deploy Spring Boot (with JSP) to Elastic Beanstalk - spring

I am attempting to deploy my Spring Boot project to Amazon Elastic Beanstalk. I have tested and have no issue if I use the default Thymeleaf configuration, but when I switch to JSP based setup I get 404's as it cannot find the JSP's (located in src/main/webapp/WEB-INF/jsp)
I have attempted to deploy the sample (spring-boot-sample-tomcat-jsp) and find that this as well gives me a 404 when I run the provided test.
Here is how I have typically been configuring my Spring Boot Projects to allow for the use of JSP's.
Add Jasper and JSTL to pom.xml
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
</dependency>
<dependency>
<groupId>org.apache.tomcat.embed</groupId>
<artifactId>tomcat-embed-jasper</artifactId>
</dependency>
Override default view resolver configuration
#Configuration
#EnableWebMvc
public class MvcConfiguration extends WebMvcConfigurerAdapter
{
#Bean
public ViewResolver getViewResolver(){
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/jsp/");
resolver.setSuffix(".jsp");
return resolver;
}
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer){
configurer.enable();
}
}
Create folder for JSP's (src/main/webapp/WEB-INF/jsp)
Now this method works without issue until I deploy to Elastic Beanstalk where I find that no matter if I create a jar and use Java (in Elastic Beanstalk) or create a war and use Tomcat (in eb) I get a 404 when any controller tries to return a view in the WEB-INF/jsp folder.
Is the above method for switching to JSP's not recommended? Is there a better way to configure Spring Boot to use Tomcat/Jasper/JSP's?
I have attempted the method provided in the Spring Boot Samples on github here
But what's interesting is if I run the provided test i get the same 404.
Any help would be greatly appreciated. If there is a better way to deploy a Spring Boot project that utilizes JSP's I'd be happy to switch over, but currently I seem to have configured myself into a corner.
Thx!

I am answering as a novice developer in Springboot development. And, I am just playing around with AWS EB and SpringBoot app deployment.
Here are my findings,
WebMvcConfigurerAdapter is deprecated
SpringBoot app works seamlessly on AWS EB only when we extend application/main class with SpringBootServletInitializer
I tried a sample HelloWorld application extending WebMvcConfigurerAdapter, which worked seamlessly on localhost and failed miserable on AWS EB.
I switched the application class extending from WebMvcConfigurerAdapter to SpringBootServletInitializer, this worked on both localhost as well as AWS EB.
The example I tried is inspired from here: https://github.com/in28minutes/deploy-spring-boot-aws-eb/tree/master/03-spring-boot-web-application-h2
Here is the application class that I changed from extending SpringBootServletInitializer to WebMvcConfigurerAdapter, which didn't work and gave me 404.
https://github.com/in28minutes/deploy-spring-boot-aws-eb/blob/master/03-spring-boot-web-application-h2/src/main/java/com/in28minutes/springboot/web/SpringBootFirstWebApplication.java
Hopefully this helps ...!
Still finding out a reason about why we receive 404 when we extend Application class with WebMvcConfigurerAdapter. I Will update this same answer, once I find a reason.
Thank you...!

Related

Vaadin-SpringBootServletInitializer vs AppShellConfigurator .What is valid for production build on external Tomcat?

I'm building my project with Spring Boot and when uploading to Tomcat as a .war it was throwing errors.
In any case what fixed it is to change
public class Application implements AppShellConfigurator {
to
public class Application extends SpringBootServletInitializer implements AppShellConfigurator {
I've used a sampler from start.vaadin.com v22 to build upon which came by default with the
Application implements AppShellConfigurator
Like this it was running fine inside Intellij with the embedded Tomcat but when deploying to a standalone/external one , I had to change that to the latter.
Is that required ? if yes then the docs should be updated because at
https://vaadin.com/docs/v22/flow/production/production-build
there's no mention of it.
As M. Deinum said this is related to Spring Boot.
Usually you run the Spring Boot Application as an executable JAR. But If you want to run it as a WAR inside a Tomcat you have to use SpringBootServletInitializer.
The first step in producing a deployable war file is to provide a
SpringBootServletInitializer subclass and override its configure
method. Doing so makes use of Spring Framework’s servlet 3.0 support
and lets you configure your application when it is launched by the
servlet container. Typically, you should update your application’s
main class to extend SpringBootServletInitializer...
Source: https://docs.spring.io/spring-boot/docs/current/reference/html/howto.html#howto.traditional-deployment

What Is the Correct Way To Use AbstractReactiveWebInitializer

I've got a Spring WebFlux application running successfully as a standalone spring boot application.
I am attempting to run the same application in a Tomcat container, and following the documentation, I've created a class that extends AbstractReactiveWebInitializer. The class requires that I implement a method getConfigClasses that would return classes normally annotated with #Configuration. If the working spring boot app started with a class called ApplicationInitializer, then the resulting implementations would look like this:
#SpringBootApplication(scanBasePackages = "my.pkg")
#EnableDiscoveryClient
#EnableCaching
public class ApplicationInitializer {
public static void main(String... args) {
SpringApplication.run(ApplicationInitializer.class, args);
}
}
and
public class ServletInitializer extends AbstractReactiveWebInitializer {
#Override
protected Class<?>[] getConfigClasses() {
return new Class[] {ApplicationInitializer.class};
}
}
When deployed, the only thing that starts is ApplicationInitializer, none of the autoconfigured Spring Boot classes (Cloud Config, DataSource, etc) ever kick off.
The documenation states this is the class I need to implement, I just expected the remainder of the spring environment to "just work".
How should I be using this class to deploy a Reactive WebFlux Spring Boot application to a Tomcat container ?
Edit:
After some additional research, I've narrowed it down to likely just Cloud Config. During bean post processing on startup, the ConfigurationPropertiesBindingPostProcessor should be enriched with additional property sources (from cloud config), but it appears to be the default Spring properties instead, with no additional sources.
The misisng properties is causing downstream beans to fail.
Spring Boot does not support WAR packaging for Spring WebFlux applications.
The documentation you're referring to is the Spring Framework doc; Spring Framework does support that use case, but without Spring Boot.
you can extend SpringBootServletInitializer, add add reactive servlet on onStartup method

Spring Boot on Elastic Beanstalk worker tier

I am trying to deploy a spring boot app into one EB worker tier but seems that EB it is not ready to manage this kind of project.
Have I to mandatory generate a .war from my spring boot app?
Thanks!
I have found the problem.
EB expects a .war file and Spring Boot app usually is launche by a embedded Tomcat or Jetty.
I have found the solution in this guide:
http://spring.io/guides/gs/convert-jar-to-war/
Summing up:
Add tomcat dependency with provided scope in pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
Create a class extending SpringBootServletInitializer and load the entrypoint within this class. This way, we are indicating to the servlet container how to launch the app.
package com.proyecti.magma.conversionsworker.config.servlet;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import com.proyecti.magma.conversionsworker.entrypoint.Application;
public class ServletConfig extends SpringBootServletInitializer
{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
}

drools-6 (kie) auto scanning (from spring) of modules and sessions from kie workbench deployed artifacts

I am trying to build a web (spring-mvc) application with kie (drools 6) integrated via injection. I have used kie workbench to create a workflow, complied and deployed. I have added reference of this artifact in my project's pom.xml and added the local kie-workbench repository as per this blog post and it's working fine (pulling in the artifact as dependency in my maven/spring project). What I am trying to do is inject the kiesession in one of my service as dependency with following snippet -
#Service
public class TniServiceImpl implements TniService {
#Inject
#KSession("tniSession")
private KieSession tniSession;
...
}
In my root-context.xml, I have added the kie namespace as well along with reference to xsd. I have added org.kie.spring.KModuleBeanFactoryPostProcessor as well as per drools documentation. I am trying to make CDI injection work for KSession scanning and injection (it's already working for my other components in same project, using #Inject). So far I am always getting "No qualifying bean of type [org.kie.api.runtime.KieSession] found for dependency" error. Looks like spring is not able to scan the available kie modules and sessions therein. Need help on following -
Is CDI inject really supported with spring? Do I have to configure kmodules and kession explicitly as mentioned here?
Am I missing something here which should make this scanning and injection work?
My environment is following -
spring 3.2.6-RELEASE (including webmvc and other components)
kie-api-6.0.1.FINAL
kie-spring-6.0.1.FINAL
kie-internal-6.0.1.FINAL
I have already gone through following links but no luck (mostly they are not trying to do what I am) -
Loading Drools/KIE Workbench artifacts directly from the repository
why does loading Drools 6 KIE JAR into code fail?
I'll appreciate if anybody can guide me on what could be the missing piece here or if there's no option but to explicitly define all kmodules/ksessions in spring config file.
I had the same problem and found a solution here: http://drools.46999.n3.nabble.com/Spring-4-0-amp-Drools-6-0-1-Integration-issue-td4028052.html
Basically you will need to inject ApplicationContext instead of kieSession and get xml bean manually.
TniServiceImpl.java
#Service
public class TniServiceImpl implements TniService {
#Inject
ApplicationContext context;
KieSession kieSession;
#PostConstruct
public void postConstruct(){
kieSession = (KieSession) context.getBean("ksession1");
}
...
}
root-context.xml
<kie:kmodule id="kmodule1">
<kie:kbase name="kbase1">
<kie:ksession name="ksession1" />
</kie:kbase>
</kie:kmodule>
<bean id="kiePostProcessor" class="org.kie.spring.KModuleBeanFactoryPostProcessor" />
Hope this helps.
UPDATE:
Another way to achieve this is to keep xml identical and instead of trying to inject KieSession, inject KieBase. Then, with the instance of KieBase, create new KieSessions.
#Service
public class TniServiceImpl implements TniService {
#Autowired
private KieBase kbase;
/* inside some method */
#RequestMapping(method=RequestMethod.GET)
public #ResponseBody Data getData() {
KieSession ksession = kbase.newKieSession();
...
}
}
The above answer doesn't work with spring mvc. I found that this is a bug in the existing drools and they are fixing it in the next version. I am stuck at this point since I am using DROOLS in batch mode but I want it to be used in a REST Service hosted on websphere.
The above solution works perfectly within a batch program.
This is what I have working with the latest Spring MVC (Spring Boot)
#SpringBootApplication
public class DroolDemoApplication {
public static void main(String[] args) {
SpringApplication.run(DroolDemoApplication.class, args);
}
#Bean
public KieContainer kieContainer() {
return KieServices.Factory.get().getKieClasspathContainer();
}
#Bean
public KieSession kieSession() throws IOException {
return kieContainer().newKieSession("DroolDemoSession");
}
}
and below is the kmodule.xml
<kbase name="DroolDemoKbase" packages="rules">
<ksession name="DroolDemoSession" />
</kbase>
finally all you do in your controller is
#Autowired
private KieSession kieSession;
kieSession.fireAllRules();
hope this helps those folks still having issues
I had similar issues with the rules not being triggered, and I solved it by using the 6.2.0.Final version of the kie-ci and kie-spring. I tried versions: 7.7.0, 7.2.0, 6.5.0 and 6.4.0, but none of them worked.
...
<properties>
<kie.version>6.2.0.Final</kie.version>
</properties>
...
<dependencies>
...
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>${kie.version}</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>${kie.version}</version>
</dependency>
...
</dependencies>
What also helped was running mvn dependency:tree and seeing which versions of which artefacts/projects are being used.

How to migrate from traditional java web application (with web.xml) to spring boot?

I wanna switch my projects to spring-based product.
My first step is to transform my java web application from a generated WAR file, to a standalone executable jar, powered by spring boot.
Let's take a open source web application example from github.: Vaadin-Spring Web Application
The web.xml file can be found here.
The root-context file can be found here.
I hope that there are some guides for me to perform the transformation.
I have also submit an issue in the spring-boot project.
This application is not a Spring MVC application as far as I can tell - it would probably be a lot easier to migrate if it was. The goal (per the github issue) is to obtain an executable JAR. The basic plan though might be to migrate first to a WAR using Spring Boot and then to a JAR once that is working. It's a pretty simple app so all we really need to do is look at the web.xml and translate it into the relevant Spring Boot features. Here are some general guides:
Create a deployable WAR by extending SpringBootServletInitializer (e.g. in a class called Application), and add the Spring Boot #EnableAutoConfiguration annotation. Example:
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
Then add some configuration:
A #Bean of type Servlet or ServletRegistrationBean installs that bean in the container as if it was a <servlet/> and <servlet-mapping/> in web.xml
A #Bean of type Filter or FilterRegistrationBean behaves similarly (like a <filter/> and <filter-mapping/>).
The ApplicationContext in this case is rooted in an XML file, so the easiest first step is to #Import that into the Spring Application. This one is so simple that it can be recreated in a few lines as #Bean definitions.
Static resources can be moved to /public (or /static or /resources or /META-INFO/resources) in the classpath root
Once the WAR is working we make it executable by adding a main method to our Application, e.g.
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
See also the Getting Started Guide on Converting a JAR to a WAR.
As I said, the biggest problem with this specific app is that it isn't a Spring MVC app. As the Irishman might say "If I wanted to get to there, sir, I wouldn't be starting from here." This is an interesting question in general, but I recommend anyone else looking to migrate a Spring application to Spring Boot read the general advice here but maybe start another discussion somewhere else.
Anyway, I'll have a bash at converting this specific app (source code jars would be nice), and update this response if I learn anything new.

Resources