Arquillian, Spring WebApplicationInitializer, embedded Tomcat - spring

I'd like to write a unit (component) test for a REST resource of my web application. This test should target the exposed REST interface and test its behaviour by issuing requests and checking the responses.
The web application is configured without the use of web.xml and applicationContext.xml by making use of Spring's WebApplicationInitializer. This works fine when I just run the application on my (Tomcat) server.
When starting my Arquillian test, the ShrinkWrap archive is deployed on the embedded Tomcat server. As the embedded server runs with the classpath of my web application, it also finds my WebApplicationInitializer class. This is problematic, as it loads lots of dependencies, and should just be activated for the test.
How can I deactivate my WebApplicationInitializer (and provide an alternative implementation) in the test?
When using XML configuration files this corresponds to hiding the real configuration files and using a specific (empty?) configuration which is used in the test.

I worked around this issue by just disabling my WebApplicationInitializer instance using a static field which I set in Arquillian's #Deployment method.
You might also want to look into metadata-complete which causes Tomcat to not invoke the Spring part which delegates to the WebApplicationInitializer instance.

Related

Run one Spring application in two modes - as CommandLineRunner and SpringBootServletInitializer type

I need to run my Spring Boot app in two modes:
As command line application (without Tomcat) as CommandLineRunner
As SpringBootServletInitializer REST API app for standalone Tomcat.
Question: Is it possible? How can I achieve this setup?
I think there is a contradiction in the question:
If you use SpringBootServletInitializer then the artifact is supposed to be packaged as WAR and deployed on some webserver like tomcat. Tomcat is not embedded in this case.
If you want an Embedded Tomcat, then you don't need WAR, you should use a "usual" jar type of spring boot application artifact.
A couple of facts that might help:
There is nothing special about CommandLineRunner beans except the fact spring boot automatically will run a run method on all beans that implement this interface when the application context becomes available.
The fact that you have a web starter in dependencies usually means that you intend to run the web server (tomcat), however its not always the case, you can specify the web env. type to 'NONE' and an embedded tomcat won't be started (see this thread for more technical information)
Now given these two facts, and assuming you don't really build a WAR (in this case you'll have to host it on embedded tomcat, java doesn't recognize WARs) - you can define a "flavour" - profile for example and define the configuration for command line runner to be active only for certain profile and the property of not-running the tomcat to be relevant for another type of profile.
Update
Based on OP's comment:
So, the goal is being able to run:
java -jar myapp.war
in a way that only CommandLineRunner will run and no real tomcat will be loaded
On the other hand you want to put myapp.war to webapps folder of some tomcat and in this way, CommandLineRunner won't be invoked, right?
In this case, consider implementing the following suggestion:
create the application-cmd.properties and specify that the tomcat should not run (web env. type = NONE) - see the link that I've provided above
Add #Profile("cmd") on CommandLineRunner implementation so that it would run only when profile 'cmd' is specified.
Run java -jar myapp.war with --spring.profiles.active=cmd flag so that cmd profile will be loaded.
Don't change anything for tomcat deployment - profile cmd won't be activated and CommandLineRunner won't run

Prevent SpringBoot from creating an EmbeddedServletContainer

I'm trying to convert an existing spring application into a spring boot application. this application is not a web application but somewhere deep in it's maven dependencies hierarchy it includes a spring-web jar and therefore spring-boot tries to autoconfigure a WebEnvironment and in turn complains about not finding an EmbeddedServletContainerFactory. But this is intended as I'm using spring-boot-starter instead of spring-boot-starter-web.
My questing is: how can I prevent spring-boot from autodiscovering web specific items in the classpath? I already tried something like
#EnableAutoConfiguration(exclude = { EmbeddedServletContainerAutoConfiguration.class })
but it doesn't seem to work. Debugging the startup process I see that it runs into a method called deduceWebEnvironment() which is called unconditionally. This method returns true as soon as the following classes are on the classpath:
javax.servlet.Servlet, org.springframework.web.context.ConfigurableWebApplicationContext
But again, even this classes exist in the cp, I don't want to startup a web-application.
Try new SpringApplicationBuilder(YourApp.class).setWebEnvironment(false).run(args) You can also disable the web mode via configuration in application.properties
spring.main.web-environment=false
See the documentation

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.

End to end test across multi Spring Boot applications

Currently in our project, we are using Spring Integration to integrate many service and some protocol related endpoints.
The project is a multi Spring Boot applications, more than one executable jars will be deployed in production.
The question is:
How to run an end to end test which needs to run cross some of these applications, I have to run the one by one manually? In before none-Spring-Boot applications, I can use Maven tomcat7 plugin to complete this work(deploy the wars into an embedded tomcat and run it in pre-integration-test phase), now how to start up all related applications before I run my test. Assume I do not use Docker/Vagrant now.
Similar question found on stackoverflow, End to end integration test for multiple spring boot applications under Maven
How to run the end2end test automatically?
In an Spring Integration test, sometime I have to mock a http endpoint, so I wrote a simple Controller in test package to archive this purpose, but I want to run it at a different port, which make it more like an outside resource. How to run different #SpringBootApplicaiton classes at varied ports at the same time in the test for this purpose?
I am using the latest Maven, Java 8, Spring Boot 1.3.1.RELEASE.
Actually, Spring Boot comes with the embedded Servlet Container support. One of them is exactly Tomcat. The default on for the org.springframework.boot:spring-boot-starter-web.
With the org.springframework.boot:spring-boot-starter-test and its #SpringApplicationConfiguration and #WebIntegrationTest you can achieve your requirements, even with the random port.
Please, refer to the Spring Boot Reference Manual for more information:
To change the port you can add environment properties to #WebIntegrationTest as colon- or equals-separated name-value pairs, e.g. #WebIntegrationTest("server.port:9000"). Additionally you can set the server.port and management.port properties to 0 in order to run your integration tests using random ports.
With that your #SpringBootApplicaiton will be deployed to that embedded Tomcat and your test can get access to the ran services/controllers.
Note: it doesn't matter if your Spring Boot application has Spring Integration facilities. The behavior is the same: embedded Servlet Container and integration tests against #Value("${local.server.port}").

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?

Resources