I'm triyng to deploy a spring-mvc webapp WAR-package on tomcat. Deploy proccess fails with following error: 'java.lang.IllegalStateException: No ServletContext set'
I guess something wrong with my configuration :(
Tomcat version: 8.5.46
Spring, spring-mvc version: 5.1.9.RELEASE
Example configuration code from spring.io: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
My webapp initializer:
package com.jbtits.spring.mvc.webac;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class AppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AppConfig.class);
applicationContext.refresh();
DispatcherServlet servlet = new DispatcherServlet(applicationContext);
ServletRegistration.Dynamic registration = servletContext.addServlet("webac", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
My webapp configuration:
package com.jbtits.spring.mvc.webac;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
#EnableWebMvc
#ComponentScan("com.jbtits.spring.mvc.webac")
public class AppConfig extends WebMvcConfigurationSupport {
}
Thats it, only two beans.
Tomcat failure output:
02-Oct-2019 18:02:52.971 WARNING [http-nio-8081-exec-84] org.springframework.context.support.AbstractApplicationContext.refresh Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
I found the solution: no need to call applicationContext.refresh(); in the org.springframework.web.WebApplicationInitializer#onStartup, cause it will be automaticaly called in the method org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext while servlet loading in the servlet container.
But I use example from spring.io docs: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet . Why they use it that way?
proof
Related
Here is my DatasourceConfiguration.java file. It's a configuration file, to set up the Hikari config and the QuartzDatasource is built based on the Hikari config.
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.quartz.QuartzDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class DatasourceConfiguration {
#Value("${spring.datasource.scheduler.jdbcUrl}")
private String jdbcUrl;
#Value("${spring.datasource.scheduler.username}")
private String userName;
#Value("${spring.datasource.scheduler.driverClassname}")
private String driverClassname;
#Value("${spring.datasource.scheduler.password}")
private String password;
#Bean
public HikariConfig hikariConfig() {
HikariConfig hikariConfig = new HikariConfig();
hikariConfig.setDriverClassName(driverClassname);
hikariConfig.setJdbcUrl(jdbcUrl);
hikariConfig.setUsername(userName);
hikariConfig.setPassword(password);
return hikariConfig;
}
#Bean(name = "quartzDataSource")
#QuartzDataSource
public DataSource dataSource() {
return new HikariDataSource(hikariConfig());
}
}
And here is my DatasourceConfigurationTest.java file:
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.ApplicationContext;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.sql.DataSource;
import java.sql.SQLException;
import static org.assertj.core.api.Assertions.assertThat;
#ExtendWith(SpringExtension.class)
#TestPropertySource(locations = "classpath:application.properties")
#ContextConfiguration(classes = DatasourceConfiguration.class)
class DatasourceConfigurationTest {
#Autowired
private ApplicationContext context;
#Test
void validateConfiguration() throws SQLException {
assertThat(this.context.getBeansOfType(DataSource.class)).hasSize(1);
HikariDataSource dataSource = this.context.getBean(HikariDataSource.class);
assertThat(dataSource.getConnection().getMetaData().getURL()).isEqualTo("jdbc:h2:mem:simple");
assertThat(dataSource.getMaximumPoolSize()).isEqualTo(42);
}
}
When I run the Unit Test, I recognized that it failed to load ApplicationContext.
Here is the log:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:124)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'getHikariConfig' defined in com.gm.gcc.vas.utility.messageretry.config.DatasourceConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariConfig]: Factory method 'getHikariConfig' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:658)
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:486)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.zaxxer.hikari.HikariConfig]: Factory method 'getHikariConfig' threw exception; nested exception is java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:653)
... 87 common frames omitted
Caused by: java.lang.RuntimeException: Failed to load driver class test-driver-classname in either of HikariConfig class loader or Thread context classloader
at com.zaxxer.hikari.HikariConfig.setDriverClassName(HikariConfig.java:491)
at com.gm.gcc.vas.utility.messageretry.config.DatasourceConfiguration.getHikariConfig(DatasourceConfiguration.java:37)
... 88 common frames omitted
Here is the properties file:
spring.datasource.scheduler.driverClassname=test-driver-classname
spring.datasource.scheduler.jdbcUrl=jdbc:test-url
spring.datasource.scheduler.password=test-password
spring.datasource.scheduler.username=test-username
As my understanding, when trying to create a connection, it failed because of dummy values.
I just wonder if my approach to do the unit test is correct or not, if yes, how can I fix the ApplicationContext error? Else, what is the correct way to ensure 100% converage.
If you want to execute integration tests, you need a running DB instance to which Spring can connect. Your test is not a unit test as it is extended with the SpringExtension.
You could use an embedded H2 database for your tests, you would then need to add H2 as test dependency in your pom.xml:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
</dependency>
In your application-test.properties:
spring.datasource.scheduler.driverClassname=org.h2.Driver
spring.datasource.scheduler.jdbcUrl=jdbc:h2:mem:test;DB_CLOSE_DELAY=-1
spring.datasource.scheduler.password=sa
spring.datasource.scheduler.username=sa
In order to have the application-test.properties loaded for your test you should either replace the #TestPropertySource(locations = "classpath:application.properties") with #ActiveProfiles("test") or reference the file #TestPropertySource(locations = "classpath:application-test.properties").
Another good alternative would be using the Testcontainers project which allows you to run the database that you use in production (like Postgres or MySQL) in a docker container. Here is a good tutorial by Baeldung.
I need your help please. I integrated Ehcache 3 with springboot 2. everything works perfectly in production. integration tests also work if i execute them manualy ( I'm using Intellij IDE ).
but when i run "mvn install", i get this error :
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'ehCacheManager' defined in class path
resource [com/afklm/belem/payment/config/EhCacheConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate [org.springframework.cache.CacheManager]: Factory method
'ehCacheManager' threw exception; nested exception is
javax.cache.CacheException: A Cache named [currenciesCache] already
exists
CurrencyRepository interface :
#Repository
public interface CurrencyRepository extends JpaRepository<Currency, String> {
/**
* Gets currencies from Cache.
* The real call to the DataBase will be performed if the response is not in cache
*
* #return list of {#link Currency}
*/
#Cacheable(cacheNames = "currenciesCache", key ="#root.methodName", unless = "#result==null or #result.size()==0")
default List<Currency> getCachedCurrencies() {
return findAll();
}
Cache configuration class
import org.ehcache.config.CacheConfiguration;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.ExpiryPolicyBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.cache.CacheManager;
import javax.cache.Caching;
import java.time.Duration;
import java.util.List;
#EnableCaching
#Configuration
public class EhCacheConfiguration extends CachingConfigurerSupport {
#Bean
public CacheManager ehCacheManager() {
//-> currencies cache configuration
CacheConfiguration<String, List> currenciesCacheConfig =
CacheConfigurationBuilder
.newCacheConfigurationBuilder(String.class, List.class, ResourcePoolsBuilder.heap(1000))
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofDays(30)))
.withDefaultEventListenersThreadPool()
.build();
javax.cache.CacheManager cacheManager = Caching.getCachingProvider().getCacheManager();
cacheManager.createCache("currenciesCache", Eh107Configuration.fromEhcacheCacheConfiguration(currenciesCacheConfig));
return new JCacheCacheManager(cacheManager);
}
Thanks for your help; i'm stuck
I think I faced this before when at least one of my tests had the #DirtiesContext annotation which re-initializes the Spring context. I got around it by making a call to the cacheManager.destroyCache method before attempting to create the cache. Can you try adding the statement cacheManager.destroyCache("currenciesCache"); before the cacheManager.createCache statement? Alternatively, you could try initializing the cache manager without the explicit call to the createCache method like it has been done in this Stack Overflow answer
I'm using #TestConfiguration annotation to define bean provider for the JUnit5 test class.
A test class is annotated with:
#Import(MyTestConfiguration.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT).
package com.example;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.context.annotation.Import;
import javax.annotation.Resource;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
#Import(MyTestConfiguration.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class MyTest {
...
}
A configuration class is annotated with
#TestConfiguration.
package com.example;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.annotation.Bean;
#TestConfiguration
public class MyTestConfiguration {
#LocalServerPort
private int port;
...
}
I'm trying to inject local server port inside the configuration class with #LocalServerPort annotation, but get a Failed to load ApplicationContext error Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'com.example.MyTestConfiguration': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'local.server.port' in value "${local.server.port}".
I was using #LocalServerPort successfully in the test class, but decided to move it along with other objects which use it to the MyTestConfiguration class to make test class more clear.
Add #Lazy annotation to the test configuration class. More info here.
From the documentation:
If this annotation is not present on a #Component or #Bean definition,
eager initialization will occur. If present and set to true, the #Bean
or #Component will not be initialized until referenced by another bean
or explicitly retrieved from the enclosing BeanFactory. If present and
set to false, the bean will be instantiated on startup by bean
factories that perform eager initialization of singletons.
I'm triyng to deploy a spring-mvc webapp WAR-package on tomcat. Deploy proccess fails with following error: 'java.lang.IllegalStateException: No ServletContext set'
I guess something wrong with my configuration :(
Tomcat version: 8.5.46
Spring, spring-mvc version: 5.1.9.RELEASE
Example configuration code from spring.io: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet
My webapp initializer:
package com.jbtits.spring.mvc.webac;
import org.springframework.web.WebApplicationInitializer;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
public class AppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext servletContext) throws ServletException {
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(AppConfig.class);
applicationContext.refresh();
DispatcherServlet servlet = new DispatcherServlet(applicationContext);
ServletRegistration.Dynamic registration = servletContext.addServlet("webac", servlet);
registration.setLoadOnStartup(1);
registration.addMapping("/");
}
}
My webapp configuration:
package com.jbtits.spring.mvc.webac;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
#Configuration
#EnableWebMvc
#ComponentScan("com.jbtits.spring.mvc.webac")
public class AppConfig extends WebMvcConfigurationSupport {
}
Thats it, only two beans.
Tomcat failure output:
02-Oct-2019 18:02:52.971 WARNING [http-nio-8081-exec-84] org.springframework.context.support.AbstractApplicationContext.refresh Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'resourceHandlerMapping' defined in org.springframework.web.servlet.config.annotation.DelegatingWebMvcConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.web.servlet.HandlerMapping]: Factory method 'resourceHandlerMapping' threw exception; nested exception is java.lang.IllegalStateException: No ServletContext set
I found the solution: no need to call applicationContext.refresh(); in the org.springframework.web.WebApplicationInitializer#onStartup, cause it will be automaticaly called in the method org.springframework.web.servlet.FrameworkServlet#configureAndRefreshWebApplicationContext while servlet loading in the servlet container.
But I use example from spring.io docs: https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-servlet . Why they use it that way?
proof
I'm trying to use the #Autowired annotation to set a property on a jax-rs restful service but I get a null pointer exception when the property is referenced. This is the first time I've tried using this annotation.
package com.pallelli.mvcpract.rest;
import javax.servlet.ServletContext;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import org.springframework.web.context.support.WebApplicationContextUtils;
import com.pallelli.hibpract.policymodel.PolicyDao;
import com.pallelli.hibpract.policymodel.beans.Risk;
#Service("riskService")
#Path("risk")
#Component
public class RiskService {
//#Context
//private ServletContext context;
#Autowired
private PolicyDao policyDao;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response storeRisk(Risk risk) {
//ApplicationContext ctx =
//WebApplicationContextUtils.getWebApplicationContext(context);
//policyDao = ctx.getBean(PolicyDao.class);
policyDao.addRisk(risk);
risk.setName(risk.getName()+" : processed");
return Response.ok(risk).status(200).build();
}
}
Everything works if I remove the comments so that policyDao is created using an application context, hence I think that spring is being made aware of the bean.
I'm using the following in mvc-dispatcher-servlet.xml to get spring to look for the beans.
<context:component-scan base-package="com.pallelli.mvcpract.rest" />
<context:component-scan base-package="com.pallelli.hibpract.policymodel" />
This is the PolicyDao class (and I know that it is 'wrong')
package com.pallelli.hibpract.policymodel;
import org.hibernate.Session;
import org.springframework.stereotype.Component;
import com.pallelli.hibpract.policymodel.beans.Risk;
#Component
public class PolicyDao {
public void addRisk(Risk risk) {
Session session = null;
try {
session = Main.getSessionFactory().openSession();
session.beginTransaction();
session.persist(risk);
session.getTransaction().commit();
}
finally {
if(session != null) session.close();
}
}
}
The debug log seems to suggest that the autowiring worked
20:10:41 DEBUG DefaultListableBeanFactory:220 - Creating shared instance of singleton bean 'riskService'
20:10:41 DEBUG DefaultListableBeanFactory:449 - Creating instance of bean 'riskService'
20:10:41 DEBUG InjectionMetadata:71 - Registered injected element on class [com.pallelli.mvcpract.rest.RiskService]: AutowiredFieldElement for private com.pallelli.hibpract.policymodel.PolicyDao com.pallelli.mvcpract.rest.RiskService.policyDao
20:10:41 DEBUG DefaultListableBeanFactory:523 - Eagerly caching bean 'riskService' to allow for resolving potential circular references
20:10:41 DEBUG InjectionMetadata:85 - Processing injected method of bean 'riskService': AutowiredFieldElement for private com.pallelli.hibpract.policymodel.PolicyDao com.pallelli.mvcpract.rest.RiskService.policyDao
20:10:41 DEBUG DefaultListableBeanFactory:220 - Creating shared instance of singleton bean 'policyDao'
20:10:41 DEBUG DefaultListableBeanFactory:449 - Creating instance of bean 'policyDao'
20:10:41 DEBUG DefaultListableBeanFactory:523 - Eagerly caching bean 'policyDao' to allow for resolving potential circular references
20:10:41 DEBUG DefaultListableBeanFactory:477 - Finished creating instance of bean 'policyDao'
20:10:41 DEBUG AutowiredAnnotationBeanPostProcessor:427 - Autowiring by type from bean name 'riskService' to bean named 'policyDao'
20:10:41 DEBUG DefaultListableBeanFactory:477 - Finished creating instance of bean 'riskService'
...
20:18:45 DEBUG DefaultListableBeanFactory:247 - Returning cached instance of singleton bean 'policyDao'
Any ideas as to why the autowired property on RiskService is null?
It seems like jax-rs is not able to process Spring annotations.
Some additional setup is required to make jax-rs objects aware of Spring Beans. Without proper initialization Autowired, Transactional or any other Spring annotation is not processed.
Assuming that you are using Jersey and Spring 3 you need to include library which provides a "bridge" between Jersey and Spring:
<dependency>
<groupId>org.glassfish.jersey.ext</groupId>
<artifactId>jersey-spring3</artifactId>
<version>2.22.1</version>
</dependency>
There is a separate library which provides support for Spring 4. For details see Jersey documentation and this example.
You managed to get ApplicationContext as it is stored as an attribute in ServletContext so it can be retrieved from any place in the application using static method call. As you noticed beans are properly initialized but both frameworks just do not talk to each other.