IllegalArgumentException: A ServletContext is required - java

Can't get my spring boot application running on AWS instance.It works fine on my machine but it looks like autowiring resolves correctly in one environment but not in another.Looks like I need to clean up configuration classes a bit.Any ideas here? Thanks much.
**Main class:**
#Configuration
#EnableAutoConfiguration
#EnableConfigurationProperties
#ComponentScan
public class Data {
public static void main(String[] args) throws Exception {
SpringApplication.run(Data.class, args);
}
}
Configuration:
#EnableWebMvc
#Configuration
public class AquilaDataWebConfig extends WebMvcConfigurerAdapter {
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/charts/**").addResourceLocations(
"file:///var/lib/aquila/");
registry.addResourceHandler("/**").addResourceLocations(
"classpath:/static/");
}
#Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:/index.html");
}
#Bean
public InternalResourceViewResolver defaultViewResolver() {
// Need this so we can forward to index.html.
return new InternalResourceViewResolver();
}
}
Exception:
Error starting ApplicationContext. To display the auto-configuration report enabled debug logging (start with --debug)
2015-07-09 19:30:55.773 ERROR 18723 [main] --- o.s.boot.SpringApplication : Application startup failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'defaultServletHandlerMapping' defined in class path resource [org/springframework/web/servlet/config/annotation/DelegatingWebMvcConfiguration.class]: Instantiation of bean failed; nested exception is org.springframework.beans.factory.BeanDefinitionStoreException: Factory method [public org.springframework.web.servlet.HandlerMapping org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport.defaultServletHandlerMapping()] threw exception; nested exception is java.lang.IllegalArgumentException: A ServletContext is required to configure default servlet handling
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:601)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1113)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1008)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:505)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:302)
at

In your application.properties (or application.yml), make sure that spring.main.web-environment isn't set to false. This should solve the missing servlet context problem.

You can use Spring Cloud
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-aws-autoconfigure</artifactId>
<version>{spring-cloud-version}</version>
</dependency>
</dependencies>
Spring Cloud AWS also provides dedicated Spring Boot support. Spring Cloud AWS can be configured using Spring Boot properties and will also automatically guess any sensible configuration based on the general setup.
Also here is an example that uses Elastic Beanstalk to deploy on AWS.
There are two differences between the War variant and the Jar variant. The War variant doesn’t need an embedded tomcat because it will be deployed in a tomcat server so the pom.xml has the spring-boot-starter-tomcat dependency set to “provided”. The Jar variant has the scope tags removed to include this dependency in the jar.
org.springframework.boot spring-boot-starter-tomcat provided
The second difference is the ServletInitializer
public class ServletInitializer extends SpringBootServletInitializer {
#Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SpringBootAwsApplication.class);
} }
Now that you have this application created, we need to generate the war file for deployment onto Amazon AWS. Right click on the pom.xml and select Run As->Maven Install. This will run the build and create the war file in the target folder of your application.
Deploy your application using Amazon Elastic Beanstalk
1) Login to Amazon AWS.
2) In the main control panel select Elastic Beanstalk under Deployment & Management.
3) Click on Create Application in the top right corner.
4) Enter the Application Name and click Next.
5) Environment Tier – Web Server
6) Predefined Configuration – Tomcat
7) Environment Type – Single instance
8) Click Next
9) Select Upload your own, click Browse and locate the war you created earlier.
10) When the application is uploaded you will see the next page where you select your URL.
11) Enter a name and click check availability to see if you can use it.
12) Click Next
13) We don’t need a RDB in this example so click next here.
14) In this next step you are defining the EC2 instance that will be created, if you are using a free trial then stick to the free t1.micro instance type.
15) EC2 Key Pair, can be left unselected. You won’t need it for now and most likely you won’t have one configured yet. This will be covered in a later post.
16) Click Next.
17) In Environment Tags click next again because we don’t care about this.
18) Review the configuration, and then click Launch.

I am assuming that you have other #Configuration in your package, which get selected by your #ComponentScan (DelegatingWebMvcConfiguration that appears in your exception is, most likely, imported by #EnableWebMvc somewhere in external #Configuration).
Possible solution is to use a filter in your component scan.
#ComponentScan( excludeFilters = { #Filter(type = FilterType.ANNOTATION, value = Configuration.class) })
also if possible include basePackages = { "org.yourpackage" } in your #ComponentScan
If possible also try to include for better performance
#Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
It tells Spring to use the container's default servlet for certain requests, like for static resources.

Related

Spring boot on Google appengine standard - REST API

I'm trying to run my code on local appengine simulator through the command mvn appengine:run and there is no error, it is just that, it can't find any of the RestController (eg: No file found for: /setiaalam/amenities).
Also, there is no Spring Boot logo been display from the startup, so I suspect I need to specific the servlet init for it? It is running fine in my own Apache Tomcat Eclipse environment, but this is only working if I were to 'run' the main class.
To be more specific, there is no custom servlet i'm creating, I just want to migrate it to Google Cloud AppEngine Standard - although no error, there is no Spring Boot startup logo at all. Trying to access any of the GET API that works locally using Postmen always return 404. No issue when try to access it from previous Apache Tomcat localhost.
Yes, I'm following the github guideline here:
Link to Github for Spring boot Google Appengine Standard
It didn't mention anything on modifying the web.xml.
And I missing something here?
Code (The main app):
#SpringBootApplication
#EnableJpaAuditing
public class SetiaAlamApplication{
public static void main(String[] args) {
SpringApplication.run(SetiaAlamApplication.class, args);
}
}
Code(1 of the controller):
#RestController
#RequestMapping("/setiaalam")
public class AmenityController {
#Autowired
AmenityDAO amenityDAO;
//service to get all amenities
#GetMapping("/amenities")
public List<Amenity> getAllAmenities(){
return amenityDAO.findAll();
}
Code (The needed SpringBootServletInitializer):
public class ServletInitializer extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(SetiaAlamApplication.class);
}
}
The application.properties:
# Spring DATASOURCE (DataSourceAutoConfiguration & DataSourceProperties)
spring.datasource.url = jdbc:mysql://ipaddress:3306/setiaalam?
useSSL=false&autoReconnect=true&failOverReadOnly=false&maxReconnects=10
spring.datasource.username = hidden
spring.datasource.password = hidden
# Hibernate Properties
# The SQL dialect makes Hibernate generate better SQL for the chosen
database
spring.jpa.properties.hibernate.dialect =
org.hibernate.dialect.MySQL5Dialect
# Hibernate ddl auto (create, create-drop, validate, update)
spring.jpa.hibernate.ddl-auto = update
Ok, issue fixed. How I fix it?
Step 1
Download the latest Eclipse.
Step 2
This follow by adding Spring Tool feature from the Eclipse Market. Please take note, if you can't find the Eclipse Market, you need to add that 1st. Simple google will guide you on how to do it.
Step 3
Then follow by adding Google Cloud Tool (from Eclipse Market).
Step 4
Once added, create a new Spring project (through Eclipse as previously I didn't but using spring.io).
Step 5
Based on the generated single main application, change the needed configuration on the pom.xml according to the Google guide for Google App Engine Standard.
Step 6
Once done, create a simple 1 controller that return just a single String text. Deploy locally - on Jetty of course, once working, move 1 by 1 all the classes to this new project.
Important Notes
Of course, if your project like mine that require more and extra dependency such as Google API, Hibernate, you need to manually add it into the pom.xml.
Also, for sure you will encounter the issue from Eclipse 'Unable to change Dynamic Web Module facet', i follow the simple guide of changing the project file org.eclipse.wst.common.project.facet.core.xml accordinly to 3.0 for the jst.web.
Thanks

Spring context indexer not working for dependency jar

I have some library jar lib.jar (made using spring boot but packaged as normal jar without spring boot plugin) which is made of spring boot and contains spring.components file generated by spring-context-indexer.
Now, I'm using this jar in my application which also has spring-context-indexer and its own spring.components file and uses some of the bean defined in lib.jar.
When I start my application, spring should register all beans defined in spring.components of lib.jar and spring.components of application. But spring isn't registering any of bean of lib.jar.
I tried using basePackages property of #SpringBootApplication but no results.
I even copied all entries of spring.components of lib.jar into spring.components of my application but no result.
Can anyone please help me?
TL;DR
If you're using Spring Data, #SpringBootApplication.scanBasePackages is not enough, you also need #EnableJdbcRepositories (or *Jpa* or whatsoever).
package application;
// without this annotation all Repository classes
// from library will be missing
#EnableJdbcRepositories({
"application",
"library"
})
#SpringBootApplication(
scanBasePackages = {
"application",
"library"
}
)
public class Application {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
}
Some more info
Okay, maybe I'm a bit late, but I decided to investigate this case a bit.
That's what I've found as of 2 Feb 2022:
All META-INF/spring.components files are loaded in CandidateComponentsIndexLoader.doLoadIndex. You can use debug to check whether it sees file from lib or not
CandidateComponentsIndexLoader then creates CandidateComponentsIndex, which is then stored in the component scanner, for me it is AnnotationConfigServletWebServerApplicationContext.scanner.componentsIndex
Then in ClassPathScanningCandidateComponentProvider findCandidateComponents is called, which, if componentsIndex is not null, just gets components from that index by provided basePackage.
That's why missing basePackage is crucial.
I haven't dug into the Spring Data algorithms, but in my case Spring hadn't been generating library Repositories until I added the #EnableJdbcRepositories with packages.
P.S. All links represent files at the 5.3.15 tag, latest atm.

Excluding PropertySource from scanning in SpringBoot WebMvcTest or other application context for WebMvcTest

I develop web app with Spring Boot. I have problem with unit test for web layer.
For these tests I'm using annotation #WebMvcTest. Problem is that my main configuration class contains #PropertySource with java arg, which contains path to external properties file, and when I start my unit web test, error is occured that this java arg can't be parsed(of course I can add this java arg to run configuration of my test, but my web unit tests don't need this file).
My main config class:
#SpringBootApplication
#PropertySource(value = {"${pathto.configfile}"})
#EnableAspectJAutoProxy
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
My first solution was to create separate configuration class with "!test" profile, and relocate #PropertySource("..") to it. And to my unit test add #ActiveProfiles("test")
My second Configuration class:
#Configuration
#Profile({"!test"})
#PropertySource(value = {"${pathto.configfile}"})
public class PropertyConfiguration {
}
For unit web test this solution works very good. But new problem appears in starting of my web app. In my external properties file I have
property spring.profiles.active. To this property I assign or db or file value. Depending on its value, apropriate implementation of Repository is created and injected to my service layer. When value is db, app starts good,
but when value is file error is being thrown: NoSuchBeanDefinitionException.
When I come back to my previous version(without second configuration file), app starts good in both cases(but not web unit tests)
So, explain please, why when value of spring.profiles.active=db, app starts good, but when spring.profiles.active=file- failed.And how I can solve my task?
I attempted to find how I can add other application context to my web unit tests, but I didn't find.
Any idea?:)
For my database repositories I'm using Spring Data JPA, so I don't create implementation of these repositories, but I create implementations of my file
repositories, and my implementations had #Profile("file"). After deleting this annotation from implementations, it leaved only on interfaces. I don't know why one config class worked, but two classes didn't. But problem is solved)

Running Spring Boot application through Eclipse picks up test classes

I am developing a Spring Boot application using STS with the Gradle plugin.
I have a different configuration for tests, to prevent our Selenium tests from having to login.
So in src/test/java/etc I have something like this:
#Configuration
#EnableGlobalMethodSecurity(prePostEnabled = true)
#EnableWebSecurity
public static class SecurityConfig extends WebSecurityConfigurerAdapter
{
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().anyRequest().permitAll();
}
}
Whereas in src/main/java I have an equivalent class that configures login etc, requiring login for all pages.
If I run the application through the Gradle plugin (bootRun), everything works fine.
However, if I run or debug it through Eclipse directly (e.g. right clicking on the project, Run As->Spring Boot App or by clicking
the run/debug buttons in the Spring or Java view) then the test config is applied, so access is granted to all pages without login.
I'm guessing that the test classes are being included in the classpath when I start the application this way.
Is there an easy way to prevent this from happening?
When you run the test from eclipse, the classpath is prepared by eclipse (and not by maven or gradle).
Eclipse only uses one classpath per project and does not know anything about dependency scopes (like 'compile' or 'test').
So the classpath always contains any resources of a referenced project.
You cannot change this behavior of eclipse.
You need to use naming conventions, profile etc. to avoid accidental use of test resources.
You can append #TestComponent to you test configuration class. These bean configurations will be skipped during component scan of your application. Depending on the component scan configuration, you need to define an #ComponentScan exclude filter:
excludeFilters = #ComponentScan.Filter(value = TestComponent.class, type = FilterType.ANNOTATION))

Dynamic Web Application - platform with components

I am doing a research on how to make a proper structure for my web application.
It will be a web application serving as a platform for additional, independent components.
The components must be able to map requests by using the #Controller annotaion.
So far I have learned, that:
The platform will be deployed as a .war file on Tomcat.
The platform classpath location will contain components in a form of .jar files.
My question is:
How to setup the components and the platform, so that platform will make use of the components' #Controllers?
So far I have the platform.war running on Tomcat. It is annotation based Spring configuration.
I also have the first component, it is a single Java class with #Controller annotation and first mapping. For some reason when I include this component in the classpath of the platform and try to access the url mapped in the component, the application returns 404 error. In the log files it says "No mapping found for HTTP request" so it does not initialize the component's #Controller.
For further explanation click here.
In your JAR file, create a package defining your namespace, i.e: "com.platformproject.web". Then all you need to do is put the JAR file in WEB-INF/lib (or better use Maven Modules) and scan the annotations at startup:
MvcConfig.java
#EnableWebMvc
#Configuration
#ComponentScan(basePackages = { "com.platformproject.web" })
public class MvcConfig extends WebMvcConfigurerAdapter { ... }

Categories