I have an application that runs locally with a bean in Application.java for Spring Boot called cacheManager
#Bean(name="cacheManager")
#Primary
public CacheManager getCacheManager() {
return new EhCacheCacheManager();
}
Since it worked locally I deployed to a server and apparently there is another application with a CacheManger that's competing for it's space
because I get following stacktrace:
Caused by: net.sf.ehcache.CacheException: Another unnamed CacheManager
already exists in the same VM. Please provide unique names for each
CacheManager in the config or do one of following:
1. Use one of the CacheManager.create() static factory methods to reuse same CacheManager with same name or create one if necessary
2. Shutdown the earlier cacheManager before creating new one with same name. The source of the existing CacheManager is:
DefaultConfigurationSource [ ehcache.xml or ehcache-failsafe.xml ] at
net.sf.ehcache.CacheManager.assertNoCacheManagerExistsWithSameName(CacheManager.java:626)
at net.sf.ehcache.CacheManager.init(CacheManager.java:391) at
net.sf.ehcache.CacheManager.(CacheManager.java:269) at
org.springframework.cache.ehcache.EhCacheManagerUtils.buildCacheManager(EhCacheManagerUtils.java:54)
at
org.springframework.cache.ehcache.EhCacheCacheManager.afterPropertiesSet(EhCacheCacheManager.java:74)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1687)
at
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1624)
... 32 common frames omitted
I attempted to put
#Bean(name="cacheManager")
#Primary
public CacheManager getCacheManager() {
return net.sf.ehcache.CacheManager.create();
}
but then net.sf.ehcache.CacheManger.create() doesn't return a spring CacheManger. I tried changing the returning CacheManager to net.sf.ehcache.CacheManager, but I get this locally:
Caused by: java.lang.IllegalStateException: No CacheResolver
specified, and no unique bean of type CacheManager found. Mark one as
primary (or give it the name 'cacheManager') or declare a specific
CacheManager to use, that serves as the default one. at
org.springframework.cache.interceptor.CacheAspectSupport.afterSingletonsInstantiated(CacheAspectSupport.java:212)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:781)
at
org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:866)
at
org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
at
org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122)
at
org.springframework.boot.SpringApplication.refresh(SpringApplication.java:737)
at
org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:370)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:314)
at
org.springframework.boot.web.support.SpringBootServletInitializer.run(SpringBootServletInitializer.java:151)
at
org.springframework.boot.web.support.SpringBootServletInitializer.createRootApplicationContext(SpringBootServletInitializer.java:131)
at
org.springframework.boot.web.support.SpringBootServletInitializer.onStartup(SpringBootServletInitializer.java:86)
at
org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:169)
at
org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5156)
at
org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
... 42 more
I think converting is the answer, but the answer could also be some sly code move.
Suggestions?
Extra Information: This is in a webservice
Unless you deploy an ehcache.xml configuration file for Ehcache, you get the default embedded configuration. This configuration does not name the CacheManager and as the first exception indicates, you cannot have more than one in a single JVM.
The easiest solution is to have an ehcache.xml, not in a package, and then it will be picked up by your deployment.
The answer to my problem was to let Spring decide the cache manager, so all I needed to do was add #EnableCaching on my Application.java and then use #Cacheable on the methods I wanted to cache on the server.
Related
I'm receiving a NullPointerException in a method with the Annotation #Bean, but before I never got this NullPointerException there:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [dev.teamnight.command.CommandFramework]: Circular reference involving containing bean 'config' - consider declaring the factory method as static for independence from its containing instance. Factory method 'commandFramework' threw exception; nested exception is java.lang.NullPointerException
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:651) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 68 common frames omitted
Caused by: java.lang.NullPointerException: null
at dev.teamnight.nightbot.Config.commandFramework(Config.java:84) ~[classes/:na]
at dev.teamnight.nightbot.Config$$EnhancerBySpringCGLIB$$2d4937ec.CGLIB$commandFramework$3(<generated>) ~[classes/:na]
at dev.teamnight.nightbot.Config$$EnhancerBySpringCGLIB$$2d4937ec$$FastClassBySpringCGLIB$$3fbdfefd.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331) ~[spring-context-5.2.6.RELEASE.jar:5.2.6.RELEASE]
at dev.teamnight.nightbot.Config$$EnhancerBySpringCGLIB$$2d4937ec.commandFramework(<generated>) ~[classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.2.6.RELEASE.jar:5.2.6.RELEASE]
... 69 common frames omitted
The code of the file causing the Exception:
#Configuration
#PropertySource("file:bot.properties")
public class Config {
#Autowired
private ShardManager shardManager;
#Autowired
private PermissionProvider permissionProvider;
#Autowired
private LanguageProvider languageProvider;
#Autowired
private PrefixProvider prefixProvider;
#Autowired
private HelpProvider helpProvider;
#Bean
public ShardManager shardManager() throws LoginException, IllegalArgumentException {
DefaultShardManagerBuilder builder = DefaultShardManagerBuilder.createDefault(this.botToken)
.enableIntents(GatewayIntent.GUILD_MEMBERS)
.setMemberCachePolicy(MemberCachePolicy.ALL)
.setStatus(OnlineStatus.IDLE)
.setShardsTotal(this.totalShards)
.addEventListeners(Arrays.asList(this.jdaListener()));
return builder.build();
}
#Bean
public CommandFramework commandFramework() {
Logger log = LogManager.getLogger();
//Line causing the error below!
log.error("Name of helpProvider: " + this.helpProvider.getClass().getCanonicalName());
return new FrameworkBuilder()
.addOwners(Arrays.stream(this.ownerIds).filter(ownerId -> ownerId.matches("^(\\d)+$")).map(ownerId -> Long.parseLong(ownerId)).collect(Collectors.toList()))
.setLogger(NightBot.logger())
.allowBots(false)
.allowDM(false)
.allowMention(true)
.allowRainbowColors(true)
.withPrefixProvider(this.prefixProvider)
.withLanguageProvider(this.languageProvider)
.withPermissionProvider(this.permissionProvider)
.withHelpProvider(this.helpProvider)
.withCustomArgumentProcessor(new NamedArgumentProcessor())
.registerClient(this.shardManager)
.build();
}
}
As I said, before the code fully worked, and I changed nothing on the Config.java file, so this error is confusing me as hell.
You are seeing this NullPointerException because the autowired HelperProvider not being injected in.
You are attempting to call .getClass() on a null object.
General Dependency Injection Debugging
Just want to add a quick basic mentions, since the main answer to this question has to make some assumption on your codebase. There are several things that you want to check when dependency injection fails.
Configuration files - Are these being loaded in correctly. Is your component scanning recently disabled?
Do your Components have the necessary #Component and or #Service/#Controller/#Repository annotations?
The Likely Root Problem - Circular Reference - BeanInstantiationException
There is also a mention of a circular reference in your stacktrace which is likely what is causing the problem.
Caused by: org.springframework.beans.BeanInstantiationException: Circular reference involving containing bean 'config' - consider declaring the factory method as static for independence from its containing instance. Factory method 'commandFramework' threw exception; nested exception is java.lang.NullPointerException
Since you received this error message, you have introduced a circular reference. This means... that a component relies on a component which relies on that component.
For example:
#Component
class HelperProvider {
#Autowired
Config config;
}
and
#Configuration
class Config {
#Autowired
HelperProvider helperProvider
}
This pattern will stump the spring framework because they rely on each other to load each component up. It can happen if you introduce a dependency further down the line. Say HelperProvider requires HelperHelperProvider which requires Config.
You will want to check for any new injections of Config or CommandFramework in your recent edits.
What you should do
You will want to redesign your configuration to break it's reliance on the HelperProvider. Doing this can resolve a lot of headaches in your future.
What you shouldn't do
It's possible to lazyload the component or configuration with #Lazy annotation. That way the bean is only instantiated upon invocation. It's recommended if you want to learn more nuances in booting up the Spring ApplicationContext, but it will become a nightmare to maintain down the road.
The error you are getting is most probably because you have a cyclic dependency in your project. And since you are getting it in commandFramework method as illustrated in error it is because of the HelpProvider class in which you are using the Config class again.
So when spring is creating a bean of Config it has to create a bean of HelpProvider which in terms wants to create a bean of Config(Autowired in HelpProvider) and leads to a never ending cycle giving the error. Also check if HelpProvider has the #component annotataion.
Config -> HelpProvider -> Config -> HelpProvider -> Config .............
Solution:-
Either you can remove the config from the HelpProvider and try to redesign it.
Use #Lazy on Config in HelpProvider which will load this dependency on object instantiation. Will suggest it as a last resort.
Currently I am using AspectJ Load Time Weaving to intercept the constructor of a base entity for auditing purposes. However when running the application I am getting incredibly inconsistent results revolving around the aspectOf() method that aspectJ weaves into LTW classes.
In some cases the application runs, the weaving is done correctly, and the code is functioning as expected. Other times I am met with:
java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
Currently I am using https://github.com/subes/invesdwin-instrument to dynamically attach the instrumentation agent into the JVM so our deployment guys don't need to do any extra configuration.
My Spring application main:
#SpringBootApplication
#EntityScan(basePackages = {"ca.gc.cfp.model"})
public class CfpWsApplication {
public static void main(final String[] args) {
DynamicInstrumentationLoader.waitForInitialized();
DynamicInstrumentationLoader.initLoadTimeWeavingContext();
if (!InstrumentationLoadTimeWeaver.isInstrumentationAvailable()) {
throw new IllegalStateException(
"Instrumentation is not available AspectJ weaver will not function.");
}
SpringApplication.run(CfpWsApplication.class, args);
}
The LTW Aspect:
#Aspect
public class BaseEntityAspect {
Logger logger = LoggerFactory.getLogger(BaseEntityAspect.class);
/** Application context property needed to fetch the AuditDate bean */
#Autowired private ApplicationContext context;
#AfterReturning(
"onBaseEntityCreated() && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
public void injectAuditTimeStamp(JoinPoint joinPoint) {
try {
AuditDate auditDate = context.getBean(AuditDate.class);
Object entityTarget = joinPoint.getTarget();
// Inject the auditing date for this Entity instance
if (entityTarget instanceof BaseEntity) {
BaseEntity baseEnt = (BaseEntity) entityTarget;
baseEnt.setAuditDate(auditDate.getAuditingTimeStamp());
}
} catch (NullPointerException e) {
logger.error(
e.getMessage()
+ " Not yet in the conext of an httpRequest, the AuditDate bean has not yet been instantiated.");
}
}
#Pointcut(
"execution(ca.gc.cfp.model.entity.BaseEntity.new(..)) && !within(ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect)")
public void onBaseEntityCreated() {}
}
The Aspect config class with a temporary factory method using the Aspects utils to let spring know it should ask aspectJ for the woven Aspect:
#Configuration
#EnableAspectJAutoProxy
public class AspectConfig {
/**
* Static factory for access to the load time woven aspect. This allows the aspect to be injected
* with the application context and beans from the spring IoC
*/
#Bean
public BaseEntityAspect getBaseEntityAspect() {
return Aspects.aspectOf(BaseEntityAspect.class);
}
}
aop.xml:
<aspectj>
<weaver options="-verbose -showWeaveInfo -Xreweavable -debug">
<include within="ca.gc.cfp.model" />
<include within="ca.gc.cfp.model..*" />
<include within="ca.gc.cfp.core.cfpws.repository.aspect..*"/>
</weaver>
<aspects>
<aspect name="ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect" />
</aspects>
</aspectj>
In a lot of cases when I run with this configuration I get the following:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect]: Factory method 'getBaseEntityAspect' threw exception; nested exception is org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:622) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 19 common frames omitted
Caused by: org.aspectj.lang.NoAspectBoundException: Exception while initializing ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at org.aspectj.lang.Aspects.aspectOf(Aspects.java:50) ~[aspectjrt-1.9.4.jar:1.9.4]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig.getBaseEntityAspect(AspectConfig.java:22) ~[classes/:na]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.CGLIB$getBaseEntityAspect$0(<generated>) ~[classes/:na]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58$$FastClassBySpringCGLIB$$84edb9e.invoke(<generated>) ~[classes/:na]
at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244) ~[spring-core-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:363) ~[spring-context-5.1.4.RELEASE.jar:5.1.4.RELEASE]
at ca.gc.cfp.core.cfpws.configuration.AspectConfig$$EnhancerBySpringCGLIB$$1cae4c58.getBaseEntityAspect(<generated>) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_211]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_211]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_211]
at java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_211]
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.1.4.RELEASE.jar:5.1.4.RELEASE]
... 20 common frames omitted
Caused by: java.lang.NoSuchMethodException: ca.gc.cfp.core.cfpws.repository.aspect.BaseEntityAspect.aspectOf()
at java.lang.Class.getDeclaredMethod(Class.java:2130) ~[na:1.8.0_211]
at org.aspectj.lang.Aspects.getSingletonOrThreadAspectOf(Aspects.java:134) ~[aspectjrt-1.9.4.jar:1.9.4]
at org.aspectj.lang.Aspects.aspectOf(Aspects.java:45) ~[aspectjrt-1.9.4.jar:1.9.4]
... 31 common frames omitted
However this is not always the case, occasionally the application runs just fine, the code executes as expected and there are no issues. This has been eluding me for a couple weeks now and I cannot seem to figure out what could be causing this code to occasionally work and occasionally not work. Could it be that CGLIB proxies are hiding the aspectOf() method from the compiler??
EDIT / UPDATE: I was able to remove the use of the above third party dependency for dynamically loading the java agent into the Spring application context. I instead used javaagent arguments and it is working fine from within my IDE. However building and running via Maven in a terminal is still causing issues. I've specified the javaagent argument via the Environment variable : MAVEN_OPTS. After doing so maven seems to be picking it up but my class still isn't being woven.
With regard to your own answer: The AspectJ Maven plugin helps you with compile-time weaving (CTW), not load-time weaving (LTW). For LTW you need to make sure that the weaving agent is active before any of the target classes have been loaded because LTW works at class-loader level. AspectJ Maven is only necessary if either you use aspects in native syntax (not annotation-based) or you want to use CTW.
I don't know about this invesdwin-instrument tool, but the AspectJ weaver offers its own capability to attach it during runtime. A third-party tool should not be necessary. Anyway, I do recommend to modify the Java command line in order to ensure the weaving agent is in place before anything else is loaded in your container. Your deployment guys ought to help you make sure that the -javaagent:... parameter is there. It is their job! To work around that in order to make life easier for them but the behaviour of your application potentially unpredictable is not going to improve its stability.
As per Kieveli's comment. My TL was thinking similarly. I'm new to Maven, Spring boot, and AOP. Looking into it externally run Maven and the build from within Eclipse seem to be "clobbering" one another.
I've been using maven via MINGW64 to clean / install / build, and the project doesn't specify usage of the AspectJ compiler which is likely why the factory method is not being woven into the class.
We're discussing a solution to this as we use Jenkins for build automation on our server. I am thinking that this Maven plugin may be the solution. https://www.mojohaus.org/aspectj-maven-plugin/
I have a spring boot application that's using multi-upload to update sometimes large amounts of files 10K+. In those cases, I'm hitting this exception. I'm guessing it's looking at my "files" parameter and seeing that it's an array > 10K and flagging this exception. I'm also sending another parameter that's an array of strings that are associated with the list of files, its size being the number of files, > 10K
java.lang.IllegalStateException: More than the maximum number of request parameters (GET plus POST) for a single request ([10,000]) were detected. Any parameters beyond this limit have been ignored. To change this limit, set the maxParameterCount attribute on the Connector.
at org.apache.tomcat.util.http.Parameters.addParameter(Parameters.java:204) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.parseParts(Request.java:2860) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.parseParameters(Request.java:3177) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.Request.getParameter(Request.java:1110) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.11.jar:8.5.11]
I understand the exception, but I'm trying to figure out where in my application.properties I can adjust this. I've set spring.http.multipart.max-file-size and spring.http.multipart.max-request-size there. I'm not finding anything equivilant to the maxParameterCount in this source.
Also, assuming there's a way I can set it for the instance running locally with spring boot (tomcat embedded), will the change also work in the deploymenet environment, or does that require changing a tomcat configuration?
Update: I found a solution that works when running locally with spring boot. I assume since this is changing the Tomcat Embedded instance, that this wouldn't apply to a deployed full tomcat environment -- I am wondering if there's a solution that would work in both tomcat instances.
#Configuration
public class TomcatCustomizationConfiguration {
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
final int maxHttpRequests = 50000;
TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
tomcatFactory.addConnectorCustomizers(connector -> connector.setMaxParameterCount(maxHttpRequests));
return tomcatFactory;
}
}
According to the Spring documentation, you can add missing configuration yourself by extending the WebServerFactoryCustomizer:
If a configuration key doesn’t exist for your use case, you should then look at WebServerFactoryCustomizer. You can declare such a component and get access to the server factory and the chosen web stack.
As there is no server.tomcat.max-parameter-count configuration yet, you can add it kind of the same way as OPs configuration code:
#Configuration
public class TomcatCustomizationConfiguration implements WebServerFactoryCustomizer<TomcatServletWebServerFactory> {
#Value("${server.tomcat.max-parameter-count:10000}")
private int maxParameterCount;
#Override
public void customize(TomcatServletWebServerFactory factory) {
factory.addConnectorCustomizers(connector -> connector.setMaxParameterCount(maxParameterCount));
}
}
N.B. I actually found the solution at this blog.
I am doing unit tests for a rest controller, which is only a small part of a bigger application.
My test context isn't recognized by my application and I have the following exception : java.lang.IllegalStateException: Failed to load ApplicationContext
This is my test class:
Test RestController
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(locations = "classpath:/META-INF/spring/context-test.xml")
#WebIntegrationTest
public class MyRestControllerTest extends AbstractTransactionnalTest {
#Autowired
private IManager manager;
#Test
// my unit tests
}
The thing is that if instead of locations = "classpath:/META-INF/spring/context-test.xml" I use classes = Production.class with the following application class, it works fine:
#Configuration
#EnableAutoConfiguration
#EnableTransactionManagement
#EnableScheduling
#ImportResource({ "classpath:/META-INF/spring/context-production.xml" })
public class Production {
// class content
}
I've read all the posts with similar problem and I know it is linked to the #Configuration and #EnableAutoConfiguration annotation however when I tried a custom configuration class which used these annotation and imported the settings from the context.xml it did not work.
I ideally wish not to add any configuration class and would like to only add a bean to my test-context.xml.
Is it possible to solve this issue with a bean in my context.xml or an annotation on TestRestController ?
Here is my stack trace:
java.lang.IllegalStateException: Failed to load ApplicationContext
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
... 26 more
Caused by: org.springframework.context.ApplicationContextException: Unable to start embedded container; nested exception is org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.onRefresh(EmbeddedWebApplicationContext.java:133)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:531)
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118)
... 35 more
Caused by: org.springframework.context.ApplicationContextException: Unable to start EmbeddedWebApplicationContext due to missing EmbeddedServletContainerFactory bean.
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.getEmbeddedServletContainerFactory(EmbeddedWebApplicationContext.java:185)
... 39 more
Here is the bean I used to mock the manager in my test-context.xml :
<bean id="IManager"
class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.service.impl.Manager"/>
Update :
I tried to used a custom manager mock where the database is replaced with a list.
If I remove the annotation #WebIntegrationTest, the application context loads correctly however I get another exception because the server isn't launched without the #WebIntegrationTest annotation.
I/O error on GET request for network address :Connection refused
I am running on spring 1.3.7.
#ContextConfiguration defines class-level metadata that is used to determine how to load and configure an ApplicationContext for integration tests. Specifically #ContextConfiguration declares the application context resource locations or the annotated classes that will be used to load the context.
#ContextConfiguration("/test-config.xml")
public class XmlApplicationContextTests {
// class body...
}
Spring Boot provides a
#SpringBootTest
annotation which can be used as an alternative to the standard spring-test
#ContextConfiguration
annotation when you need Spring Boot features. The annotation works by creating the ApplicationContext used in your tests via SpringApplication.
You can use the webEnvironment attribute of #SpringBootTest to further refine how your tests will run.
Spring Boot’s #*Test annotations will search for your primary configuration automatically whenever you don’t explicitly define one.
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structured your code in a sensible way your main configuration is usually found.
If you want to customize the primary configuration, you can use a nested #TestConfiguration class. Unlike a nested #Configuration class which would be used instead of a your application’s primary configuration, a nested #TestConfiguration class will be used in addition to your application’s primary configuration.
http://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html 40.2
I have an applicationContext.xml which defines a bean called "baseDataSource"
<bean id="baseDataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="resourceRef" value="true"/>
<property name="jndiName" value="java:/MySQLDS20"/>
</bean>
Now ordinarily this is created fine within a jboss application using Spring and Hibernate. But when I try to instantiate this IOC container as part of unit testing via
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WEB-INF/applicationContext.xml"})
I recieve this error:
Caused by: javax.naming.NoInitialContextException: Need to specify class name in environment or system property, or as an applet parameter, or in an application resource file: java.naming.factory.initial
at javax.naming.spi.NamingManager.getInitialContext(NamingManager.java:662)
at javax.naming.InitialContext.getDefaultInitCtx(InitialContext.java:307)
at javax.naming.InitialContext.getURLOrDefaultInitCtx(InitialContext.java:344)
at javax.naming.InitialContext.lookup(InitialContext.java:411)
at org.springframework.jndi.JndiTemplate$1.doInContext(JndiTemplate.java:154)
at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:87)
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)
at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178)
at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)
at org.springframework.jndi.JndiObjectLocator.lookup(JndiObjectLocator.java:105)
at org.springframework.jndi.JndiObjectFactoryBean.lookupWithFallback(JndiObjectFactoryBean.java:201)
at org.springframework.jndi.JndiObjectFactoryBean.afterPropertiesSet(JndiObjectFactoryBean.java:187)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1477)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1417)
I have thoroughly read the discussion here
http://forum.spring.io/forum/spring-projects/data/7448-problem-running-junit-test-with-jndi-datasource
But no answer there seems to either solve the issue or clearly explain what is going on. My question is this:
How do I maker this datasource work? I cannot copy and paste my applicationContext.xml into some testApplicationContext.xml just for the purpose of modifying this one single bean. What can I do to create the container and autowire in my junit tests without changing this bean or duplicating the xml config (my CTO will shoot me)
I cannot copy and paste my applicationContext.xml into some testApplicationContext.xml just for the purpose of modifying this one single bean.
You don't have to. You only have to create a configuration containing only an override for the datasource, then load both in your testcase.
<bean id="dataSource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource>
</bean>
Then in your test case load this together with your actual file and the bean definition will override the one in your actual configuration.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:WEB-INF/applicationContext.xml", "applicationContext-test.xml"})
The latter should only contain beans you want to override/replace.
To inject a custom JNDI context into your unit test you could try the following:
#BeforeClass
public static void setUp() {
DataSource ds = null; // Construct data source manually
ds.setURL("..."); ds.setUser("..."); ds.setPassword("...");
SimpleNamingContextBuilder builder = null;
try {
builder = SimpleNamingContextBuilder.emptyActivatedContextBuilder();
builder.bind("java:java:/MySQLDS20",ds);
} catch (NamingException e) {
logger.error(e);
}
}
This will expose the required JNDI name through the InitialContext.
But I would recommend you to extract the baseDataSource into a separate configuration file and then use a specific configuration file for your tests instead.
Like this:
In src/main/resources/applicationContext.xml:
<import resource="datasource.jndi.xml" />
JUnit Test Class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({
"classpath*:/datasource.test.xml",
})
public class MyTests
{
...
}
To inject a custom JNDI context into your unit test [Gregor Koukkoullis]
(this is a good method to integrate DataSource into JNDI, but you also need to init DataSource, then inject to JNDI Context)
You only have to create a configuration containing only an override for the datasource [M. Deinum]
(I think If you can define the datasource in new *.xml, so this method is easy to implements)
But David Williams need no copy *.xml, the purpose is very clearly, no change of configration, no change of source code, just test with JNDI.
below is my method:
1. search JNDI env in your remote Server.
2. with setup() method to init your Server JNDI properties.
then no change of configration, no change no test code, no addition datasource config, just add setup method to init remoter Server JNDI.