Overriding Bean Configuration with annotations - spring

Use case: I have my container configured via classpath scanning #ComponentScan. For my test configuration I need the ability to mock specific beans.
Due to the order of loading, beans loaded via classpath scan are not overriding properly when using #Configuration. The following code samples demonstrate the problem. BaseExample.java shows how it is possible to override beans via configuration. ScanExample.java shows that overriding a bean that was loaded via #ComponentScan is skipped (see final note).
A demo project is available on bitbucket.
// BaseExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class BaseExample {
private static final Logger log = LoggerFactory.getLogger(BaseExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
#Configuration
public static class Config {
#Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [Config]");
return new AccountDao();
}
}
#Configuration
public static class OverrideConfig {
#Bean
public Object accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
Output:
21:05 INFO | com.glassworks.mock.BaseExample | accountDao: Mock for AccountDao, hashCode: 666537607
[// ScanExample.java
package com.glassworks.mock;
import org.mockito.Mockito;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import com.google.common.base.Joiner;
public class ScanExample {
private static final Logger log = LoggerFactory.getLogger(ScanExample.class);
private static AnnotationConfigApplicationContext ctx;
public static void main(String args[]) {
ctx = new AnnotationConfigApplicationContext(Config.class, OverrideConfig.class);
String beans[] = ctx.getBeanDefinitionNames();
log.info("{} beans found: {}", beans.length, Joiner.on(",").join(beans));
for(String bean : beans) {
log.info("{}: {}", bean, ctx.getBean(bean));
}
}
#Configuration
#ComponentScan("com.glassworks.services")
public static class Config {
}
#Configuration
public static class OverrideConfig {
#Bean
public AccountDao accountDao() {
log.debug("Creating accountDao [OverrideConfig]");
return Mockito.mock(AccountDao.class);
}
}
}
Output:
21:08 INFO | com.glassworks.mock.ScanExample | accountDao: com.glassworks.services.AccountDao#48805ebb
// AccountDao.java
package com.glassworks.services;
import org.springframework.stereotype.Repository;
#Repository
public class AccountDao {
}
Note
Its worth noting on that with logging set to debug, Spring indicates that it is skipping over the definition. This appears to be a bug.
21:09 DEBUG | o.s.c.a.ConfigurationClassBeanDefinitionReader | Skipping loading bean definition for [BeanMethod:name=accountDao,declaringClass=com.glassworks.mock.ScanExample$OverrideConfig]: a definition for bean 'accountDao' already exists. This is likely due to an override in XML.

Related

In Spring Boot Test, how do I map a temporary folder to a configuration property?

I want to do a self-cleaning test
In my situation, I have one of the components depend on a directory
public class FileRepositoryManagerImpl implements ....
#Value("${acme.fileRepository.basePath}")
private File basePath;
}
The value is defined in the application.yml file, and in DEV it points to a directory under build.
This is not the worst idea, because gradle clean will eventually clean up the mess the tests create.
But, really, what I would like to achieve here, is to make sure that every test runs in an isolated temporary directory that is cleaned up after execution.
I know that JUnit has a tool for the temporary directories. But once I have defined that directory in the scope of JUnit 4, how do I tell Spring to use that temporary directory?
I tried the inner class unsuccessfully:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { SecurityBeanOverrideConfiguration.class, App.class })
#EnableConfigurationProperties
public abstract class AbstractFileRepositoryManagerIntTests {
private final static TemporaryFolder temporaryFolder = new TemporaryFolder();
#ClassRule
public static TemporaryFolder getTemporaryFolder()
{
return temporaryFolder;
}
#ConfigurationProperties(prefix = "acme")
static class Configuration
{
public FileRepository getFileRepository()
{
return new FileRepository();
}
static class FileRepository
{
public File basePath() throws Exception
{
return temporaryFolder.newFolder("fileRepositoryBaseDir");
}
}
}
}
I was thinking about tinkering with the Environment, but what should be the correct way to inject properties programmatically in a Spring Boot test?
I can think of at least four different approaches to your problem. All with their own advantages and disadvantages.
Approach 1: ReflectionTestUtils
You are using #Value annotation on a private instance property (please, don't to that anymore!). Hence, you can not change acme.fileRepository.basePath on the fly without reflection.
package demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.stereotype.Component;
import java.io.File;
#SpringBootApplication
public class FileRepositoryApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryApp.class, args);
}
#Component
public class FileRepository {
#Value("${acme.fileRepository.basePath}")
private File basePath;
public File getBasePath() {
return basePath;
}
}
}
Changing basePath after each test with ReflectionTestUtils.setField. Because we are using Spring's TestExecutionListener, that gets initialized before Junit rules are initialized, we are forced to manage the temporary folder in beforeTestExecution and afterTestMethod.
package demo;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.util.ReflectionTestUtils;
import java.io.IOException;
import static junit.framework.TestCase.assertEquals;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = FileRepositoryApp.class)
#TestExecutionListeners(listeners = FileRepositoryAppTest.SetBasePath.class, mergeMode = MERGE_WITH_DEFAULTS)
public class FileRepositoryAppTest {
private static TemporaryFolder temporaryFolder = new TemporaryFolder();
#Autowired
private FileRepositoryApp.FileRepository fileRepository;
#Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(fileRepository.getBasePath());
assertEquals(temporaryFolder.getRoot(), fileRepository.getBasePath());
}
#Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(fileRepository.getBasePath());
assertEquals(temporaryFolder.getRoot(), fileRepository.getBasePath());
}
static class SetBasePath implements TestExecutionListener {
#Override
public void beforeTestExecution(TestContext testContext) throws IOException {
temporaryFolder.create();
if (testContext.hasApplicationContext()) {
FileRepositoryApp.FileRepository bean = testContext.getApplicationContext().getBean(FileRepositoryApp.FileRepository.class);
ReflectionTestUtils.setField(bean, "basePath", temporaryFolder.getRoot());
}
}
#Override
public void afterTestMethod(TestContext testContext) {
temporaryFolder.delete();
}
}
}
Approach 2: Configuration properties
Introduce a configuration properties class for your application configuration. It gives you type safety for free and we don't rely on reflection anymore.
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
#SpringBootApplication
public class FileRepositoryWithPropertiesApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryWithPropertiesApp.class, args);
}
#Component
public class FileRepository {
private final FileRepositoryProperties fileRepositoryProperties;
public FileRepository(FileRepositoryProperties fileRepositoryProperties) {
this.fileRepositoryProperties = fileRepositoryProperties;
}
public File getBasePath() {
return fileRepositoryProperties.getBasePath();
}
}
#Component
#ConfigurationProperties(prefix = "acme.file-repository")
public class FileRepositoryProperties {
private File basePath;
public File getBasePath() {
return basePath;
}
public void setBasePath(File basePath) {
this.basePath = basePath;
}
}
}
Because we are using Spring's TestExecutionListener, that gets initialized before Junit rules are initialized, we are forced to manage the temporary folder in beforeTestExecution and afterTestMethod.
package demo;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.TestExecutionListener;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import static junit.framework.TestCase.assertEquals;
import static org.springframework.test.context.TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = FileRepositoryWithPropertiesApp.class)
#TestExecutionListeners(listeners = FileRepositoryWithPropertiesTest.SetBasePath.class, mergeMode = MERGE_WITH_DEFAULTS)
public class FileRepositoryWithPropertiesTest {
private static TemporaryFolder temporaryFolder = new TemporaryFolder();
#Autowired
private FileRepositoryWithPropertiesApp.FileRepository bean;
#Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
#Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
static class SetBasePath implements TestExecutionListener {
#Override
public void beforeTestExecution(TestContext testContext) throws IOException {
temporaryFolder.create();
if (testContext.hasApplicationContext()) {
FileRepositoryWithPropertiesApp.FileRepositoryProperties bean = testContext.getApplicationContext().getBean(FileRepositoryWithPropertiesApp.FileRepositoryProperties.class);
bean.setBasePath(temporaryFolder.getRoot());
}
}
#Override
public void afterTestMethod(TestContext testContext) {
temporaryFolder.delete();
}
}
}
Approach 3: Refactor your code (my favorite)
Extract basePath into its own class and hide it behind an api. Now you don't need to poke with your application properties and a temporary folder anymore.
package demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
import java.io.File;
#SpringBootApplication
public class FileRepositoryWithAbstractionApp {
public static void main(String[] args) {
SpringApplication.run(FileRepositoryWithAbstractionApp.class, args);
}
#Component
public class FileRepository {
private final FileRepositorySource fileRepositorySource;
public FileRepository(FileRepositorySource fileRepositorySource) {
this.fileRepositorySource = fileRepositorySource;
}
public File getBasePath() {
return fileRepositorySource.getBasePath();
}
}
#Component
public class FileRepositorySource {
private final FileRepositoryProperties fileRepositoryProperties;
public FileRepositorySource(FileRepositoryProperties fileRepositoryProperties) {
this.fileRepositoryProperties = fileRepositoryProperties;
}
// TODO for the sake of brevity no real api here
public File getBasePath() {
return fileRepositoryProperties.getBasePath();
}
}
#Component
#ConfigurationProperties(prefix = "acme.file-repository")
public class FileRepositoryProperties {
private File basePath;
public File getBasePath() {
return basePath;
}
public void setBasePath(File basePath) {
this.basePath = basePath;
}
}
}
We don't need any additional testing facility anymore and we can use #Rule on TemporaryFolder instead.
package demo;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import static junit.framework.TestCase.assertEquals;
import static org.mockito.Mockito.when;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = FileRepositoryWithAbstractionApp.class)
public class FileRepositoryWithAbstractionTest {
#Rule
public TemporaryFolder temporaryFolder = new TemporaryFolder();
#MockBean
private FileRepositoryWithAbstractionApp.FileRepositorySource fileRepositorySource;
#Autowired
private FileRepositoryWithAbstractionApp.FileRepository bean;
#Before
public void setUp() {
when(fileRepositorySource.getBasePath()).thenReturn(temporaryFolder.getRoot());
}
#Test
public void method() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
#Test
public void method1() {
System.out.println(temporaryFolder.getRoot().getAbsolutePath());
System.out.println(bean.getBasePath());
assertEquals(temporaryFolder.getRoot(), bean.getBasePath());
}
}
Approach 4: TestPropertySource
Use Spring's TestPropertySource annotation to override properties in a test selectively. Because a Java anntotation can not have a dynamic value, you need to decide beforehand where you want to create your directory and keep in mind your test is bound to a specific operating system due to the used os path separator.
package demo;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static demo.FileRepositoryTestPropertySourceTest.BASE_PATH;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = FileRepositoryApp.class)
#TestPropertySource(properties = "acme.fileRepository.basePath=" + BASE_PATH)
public class FileRepositoryTestPropertySourceTest {
static final String BASE_PATH = "/tmp/junit-base-path";
private Path basePath = Paths.get(BASE_PATH);;
#Autowired
private FileRepositoryApp.FileRepository fileRepository;
#Before
public void setUp() throws IOException {
Files.deleteIfExists(basePath);
Files.createDirectories(basePath);
}
#After
public void after() throws IOException {
Files.deleteIfExists(basePath);
}
#Test
public void method() {
System.out.println(fileRepository.getBasePath());
}
}
If you use JUnit 5.4+ then you can leverage their #TempDir that works just fine without manual lifecycle management of the directory. That is you don't need to create and delete it manually, in contrast to #TemporaryFolder from JUnit 4.
Here is a working example of how you can achieve your goal:
//Your bean into which you want to inject the property
#Component
public class FileRepositoryManager {
#Value("${acme.fileRepository.basePath}")
private File basePath;
public File getBasePath() {
return basePath;
}
}
//Test that uses ApplicationContextInitializer machinery to set the desired properties
#SpringBootTest
#ContextConfiguration(initializers = Initializer.class)
class FileRepositoryManagerTest {
#TempDir
static File tempDir;
#Autowired
FileRepositoryManager fileRepositoryManager;
#Test
void basePathIsSet() {
assertNotNull(fileRepositoryManager.getBasePath());
}
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
#Override
public void initialize(ConfigurableApplicationContext context) {
TestPropertyValues.of(
"acme.fileRepository.basePath=" + tempDir
).applyTo(context);
}
}
}

Spring #Autowired null problem on weblogic 12c

Spring's #Autowired value is null. My application is deployed on weblogic 12c.
I have pasted relevant files below along with explanations.
I tried changing to #Component
DriverServlet.java: Set in web.xml. Called by weblogic. Initializes
application context.
package com.clarksburg.services.configuration;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.AbstractApplicationContext;
public class DriverServlet extends HttpServlet {
public void init() throws ServletException {
// Call the AppConfig class to scan all packages
AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println("Driver Servlet initialized;");
}
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
}
public void destroy() {
// do nothing.
}
}
AppConfig.java: Does component scanning.
package com.clarksburg.services.configuration;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = { "com.clarksburg.services" })
public class AppConfig {
private static final Logger logger = LogManager.getLogger(AppConfig.class.getName());
public AppConfig() {
logger.debug("App Config called by driver");
// TODO Auto-generated constructor stub
}
// Put Other Application configuration here.
}
The class 'AsynchAQMessageSvcImpl' implements 'AsynchAQMessageSvc' which was generated using WSDL. It's wired to a OSB proxy on weblogic and get's called whenever the proxy gets a JMS message on a AQ queue.
It's annotated with #Service. I tried to autowire ProcessWsPayload in it but when i use it i get a null pointer exception. Printing it's value is also null.
package com.clarksburg.services.messaging;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebResult;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.clarksburg.services.wsimport.AsynchAQMessageRequestType;
import com.clarksburg.services.wsimport.AsynchAQMessageResponseType;
import com.clarksburg.services.wsimport.AsynchAQMessageSvc;
import com.clarksburg.services.services.ProcessWsPayload;
#Service
#WebService(name = "AsynchAQMessageSvc", targetNamespace = "http://www.clarksburg.com/AsynchAQMessageSvc")
#SOAPBinding(parameterStyle = SOAPBinding.ParameterStyle.BARE)
public class AsynchAQMessageSvcImpl implements AsynchAQMessageSvc {
#Autowired
ProcessWsPayload processWsPayload;
private static final Logger logger = LogManager.getLogger(AsynchAQMessageSvcImpl.class.getName());
#Override
#WebMethod(operationName = "GetAsynchAQMessage", action = "http://www.clarksburg.com/AsynchAQMessageSvc")
#WebResult(name = "AsynchAQResponse", targetNamespace = "http://www.clarksburg.com/AsynchAQMessageSvc", partName = "response")
public AsynchAQMessageResponseType getAsynchAQMessage(
#WebParam(name = "AsynchAQRequest", targetNamespace = "http://www.clarksburg.com/AsynchAQMessageSvc", partName = "request")
AsynchAQMessageRequestType request) {
logger.debug("processWsPayload is " +processWsPayload);
processWsPayload.processWsPayload(request);
// new ProcessWsPayload().processWsPayload(request);
AsynchAQMessageResponseType response = new AsynchAQMessageResponseType();
response.setServiceResponse("Success");
return response;
}
}
ProcessWsPayload.java: It's called (successfully) by 'AsynchAQMessageSvcImpl'. It's annotated as #Component.
package com.clarksburg.services.services;
import java.math.BigInteger;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.clarksburg.services.configuration.JDBCConfig;
#Component
public class ProcessWsPayload {
private static final Logger logger = LogManager.getLogger(ProcessWsPayload.class.getName());
#Autowired
JDBCConfig jdbcConfig;
/*
* This method is called by a JMS listener. It is being invoked successfully.
* However the value of 'jdbcConfig' is null
*/
public void processWsPayload(wsPayload request) {
BigInteger wsAuditId = request.getAuditId();
system.out.println("The value of JDBCConfig is " +jdbcConfig);
try {
String SQL = "UPDATE AUDIT_TBL SET ws_descriptions = ? WHERE ws_audit_id = ?";
JDBCConfig.jdbcTemplate().update(SQL, "Success with soap send", wsAuditId);
} catch (Exception e) {
logger.debug("Failed to update table with success");
logger.error(e);
}
}
I turned on Spring's TRACE logs and have pasted lines pertaining to Autowiring below.
Spring's relevant autowired logs during deployment:-
InjectionMetadata - Registered injected element on class [com.multiplan.services.messaging.AsynchAQMessageSvcImpl]: AutowiredFieldElement for com.multiplan.services.services.ProcessWsPayload com.multiplan.services.messaging.AsynchAQMessageSvcImpl.processWsPayload
InjectionMetadata - Processing injected element of bean 'AsynchAQMessageSvcImpl': AutowiredFieldElement for com.multiplan.services.services.ProcessWsPayload com.multiplan.services.messaging.AsynchAQMessageSvcImpl.processWsPayload
InjectionMetadata - Registered injected element on class [com.multiplan.services.services.ProcessWsPayload]: AutowiredFieldElement for com.multiplan.services.configuration.JDBCConfig com.multiplan.services.services.ProcessWsPayload.jdbcConfig
InjectionMetadata - Processing injected element of bean 'processWsPayload': AutowiredFieldElement for com.multiplan.services.configuration.JDBCConfig com.multiplan.services.services.ProcessWsPayload.jdbcConfig
AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'processWsPayload' to bean named 'JDBCConfig'
AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'AsynchAQMessageSvcImpl' to bean named 'processWsPayload'

How to create same Bean using Constructor Injection in Spring boot for different property values

I have to create multiple beans of same type for different property value which is to be injected using constructor.
Currently I have used Bean scope as Prototype & created multiple methods to read different properties to create new object. How to combine all the different methods into single method and to supply different values at run time to create new bean.
package com.demo.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
#Configuration
public class ConfigClass {
#Bean(name="RC")
public JavaClient getClient(#Autowired Environment env)
{
String sName=env.getProperty("RCSName");
String aUrl=env.getProperty("RCAUrl");
String dUrl=env.getProperty("RCDUrl");
return new JavaClient(sName,aUrl,dUrl);
}
#Bean(name="O")
public JavaClient getOClient(#Autowired Environment env)
{
String sName=env.getProperty("OSName");
String aUrl=env.getProperty("OAUrl");
String dUrl=env.getProperty("ODUrl");
return new JavaClient(sName,aUrl,dUrl);
}
}
Now it is creating 2 beans as per above code. Expectation: How to combine getClient & getOClient into single method, but the property to be supplied in a loop to create multiple beans of same type JavaClient
I have modified my ConfigClass as below & created ApplicationContextAware to inject beans by reading file properties.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.annotation.EnableScheduling;
#Configuration
#EnableScheduling
public class ConfigClass {
#Bean
#Scope("prototype")
public JavaClient getClient(String sName,String aUrl,String dUrl)
{
return new JavaClient(sName,aUrl,dUrl);
}
}
Then Have created ApplicationContextAware to create Beans.
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import javax.annotation.PostConstruct;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
#Component
public class ApplicationContextProvider implements ApplicationContextAware {
private ApplicationContext applicationContext;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public ApplicationContext getContext() {
return applicationContext;
}
#PostConstruct
public void init()
{
try {
String fileName = "Url.txt";
Resource resource = new ClassPathResource(fileName);
File file = resource.getFile();
List<String> lines = Files.readAllLines(file.toPath());
for (Iterator<String> iterator = lines.iterator(); iterator.hasNext();) {
String line = (String) iterator.next();
String[] s = line.split(",");
applicationContext.getBean(JavaClient.class,s[0], s[1], s[2]);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

Spring cloud #HystrixCommand doesn't proxy the method which is invoked in CompletableFuture.supplyAsync

I have one spring component bean which contains a method methodA defined by #HystrixCommand with fallbackMethod. The bean has another method methodB invokes methodA by CompletableFuture.supplyAsync(...). I expect Hystrix javanica will weave the aspect on methodA, but when I debug it, I didn't see hystrix aspect is weaved.
Here are some of the main sudo code,
TestApplication:
package com.my.own.test;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.my.own.test")
public class TestApplication {
public static void main(final String[] args) throws Exception {
SpringApplication.run(TestApplication.class, args);
}
}
ApplicationConfiguration:
package com.my.own.test;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy(exposeProxy = true)
#EnableConfigurationProperties
#EnableCircuitBreaker
public class ApplicationConfig {
}
AsyncConfig:
package com.my.own.test;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
#Configuration
#EnableAsync
public class AsyncConfig {
#Value("${spring.threadpool.executor.core:10}")
private int corePoolSize;
#Value("${spring.threadpool.executor.max:20}")
private int maxPoolSize;
#Value("${spring.threadpool.executor.queue:1000}")
private int queueCapacity;
#Value("${spring.threadpool.executor.timeout:true}")
private boolean coreThreadTimeOut;
#Value("${spring.threadpool.executor.keepalive:30000}")
private int keepAlive;
#Value("${spring.threadpool.executor.prefix:ThreadPoolTaskExecutor}")
private String threadNamePrefix;
#Bean("taskExecutor")
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setAllowCoreThreadTimeOut(coreThreadTimeOut);
executor.setKeepAliveSeconds(keepAlive);
executor.setThreadNamePrefix(threadNamePrefix + "-");
executor.initialize();
return executor;
}
}
TestController:
package com.my.own.test.controller;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import com.my.own.test.core.TestProcessor;
#RestController
public class TestController {
private static Logger logger = LoggerFactory.getLogger(TestController.class);
#Autowired
TestProcessor tester;
#RequestMapping(value = "/test", method = { RequestMethod.POST })
public void test() {
tester.methodB();
}
}
TestProcessor:
package com.my.own.test.core;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
#Component
public class TestProcessor {
#Autowired
private ThreadPoolTaskExecutor executor;
public void methodB() {
final List<CompletableFuture<String>> a = new ArrayList<>();
a.add(CompletableFuture.supplyAsync(() -> methodA(), executor));
CompletableFuture.allOf(a.toArray(new CompletableFuture[a.size()])).join();
}
#HystrixCommand(fallbackMethod = "deferPushdown")
public String methodA() {
if (true) {
throw new RuntimeException();
} else {
return "methodA";
}
}
public String deferMethodA() {
return "deferMethodA";
}
}
Run output
Jan 03, 2018 2:55:55 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.util.concurrent.CompletionException: java.lang.RuntimeException] with root cause
java.lang.RuntimeException
at com.my.own.test.core.TestProcessor.methodA(TestProcessor.java:40)
at com.my.own.test.core.TestProcessor.lambda$0(TestProcessor.java:33)
at java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1590)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
P.S.
From the output, methodA is not invoked from hystrix aspect, but by lambda directly. Is this an issue on Hystrix or javanica? Please share if you know a solution. I appreciate it.
Unless you’re using aspectj weaving (which requires special handling of compile steps, I think), spring defaults to using interface/cglib weaving, which only applies to the first method called from outside the class, as described in https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-understanding-aop-proxies.
In summary, if you call methodB, no aspect applies and the call from methodB to methodA is not eligible for aspect interception.
To activate the aspect you have to call methodB directly from outside the TestProcessor class.

How to get Neo4jTemplate in SDN4

I am using Spring Data Neo4j 4(SDN4) from the example project SDN4-northwind(https://github.com/amorgner/sdn4-northwind),when I try to get the Neo4jTemplate bean,I get the error message like this.
Does anyone know how to get the Neo4jTemplate bean from SDN4?
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.data.neo4j.template.Neo4jTemplate] is defined
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:371)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:331)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:968)
at org.neo4j.example.northwind.Run.main(Run.java:34)
Here is the Configuration file
package org.neo4j.example.northwind;
import org.neo4j.ogm.session.Session;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.config.Neo4jConfiguration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.server.Neo4jServer;
import org.springframework.data.neo4j.server.RemoteServer;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableNeo4jRepositories("org.neo4j.example.northwind.repository")
#EnableTransactionManagement
public class AppContext extends Neo4jConfiguration {
public static final String NEO4J_HOST = "http://localhost:";
public static final int NEO4J_PORT = 7474;
#Override
public SessionFactory getSessionFactory() {
System.setProperty("username", "neo4j");
System.setProperty("password", System.getProperty("password","osp"));
return new SessionFactory("org.neo4j.example.northwind.model");
}
#Bean
#Override
public Neo4jServer neo4jServer() {
return new RemoteServer(NEO4J_HOST + NEO4J_PORT);
}
#Bean
#Override
//#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
}
Assdd
#Bean
public Neo4jOperations getNeo4jTemplate() throws Exception {
return new Neo4jTemplate(getSession());
}
to AppContext and then #Autowired Neo4jOperations neo4jTemplate;

Resources