I have application-sample.YAML file where I have data. After loading the data, based on certain fields. I want to decide which few components to load or not to load. I can see this below condition class loads first, as a result, I am getting the data null because this condition class is loading first before my Data loaded.
#Configuration
public class AwsCondition implements Condition {
#Autowired
MyTestData data;
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
log.info("inside...........condition");
if(data.getListeners().size()>1){
return true;
}
return false;
}
}
MyTestData.java
#Slf4j
#Data
#ConfigurationProperties
#Component
public class MyTestData implements InitializingBean {
List<Listener> listeners = new ArrayList<>();
#Data
public static class Listener {
private String type;
private String name;
}
#Override
public void afterPropertiesSet() throws Exception {
if (this.getListeners().isEmpty()) {
log.info("Nothing configured. Please verify application-test.yml is configured properly.");
System.exit(1);
}
}
}
ConditionTest class
#Component
#Conditional(AwsCondition.class)
public class TestS3ListenerRouter extends RouteBuilder {
//My all logic lying here
}
Try the following:
#Component
public class AwsCondition implements Condition {
#Autowired
MyTestData data;
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO Auto-generated method stub
log.info("inside...........condition");
if(data.getListeners().size()>1){
return true;
}
return false;
}
}
Related
I am following this article to implement a database read/write separation feature by calling different methods. However, I got the error:
Missing method call for verify(mock) here: verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY);
when doing the testing.
My test case is trying to verify DatabaseEnvironment.READONLY has been set once when using TransactionReadonlyAspect AOP annotation:
// TransactionReadonlyAspectTest.java
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = {LoadServiceImpl.class, TransactionReadonlyAspect.class})
public class TransactionReadonlyAspectTest {
#Autowired
private TransactionReadonlyAspect transactionReadonlyAspect;
#MockBean
private LoadServiceImpl loadService;
#Test
public void testReadOnlyTransaction() throws Throwable {
ProceedingJoinPoint mockProceedingJoinPoint = mock(ProceedingJoinPoint.class);
Transactional mockTransactional = mock(Transactional.class);
DatabaseContextHolder spyDatabaseContextHolder = mock(DatabaseContextHolder.class);
when(mockTransactional.readOnly()).thenReturn(true);
when(loadService.findById(16)).thenReturn(null);
when(mockProceedingJoinPoint.proceed()).thenAnswer(invocation -> loadService.findById(16));
transactionReadonlyAspect.proceed(mockProceedingJoinPoint, mockTransactional);
verify(spyDatabaseContextHolder, times(1)).set(DatabaseEnvironment.READONLY); // got the error: Missing method call for verify(mock)
verify(loadService, times(1)).findById(16);
assertEquals(DatabaseContextHolder.getEnvironment(), DatabaseEnvironment.UPDATABLE);
}
}
//TransactionReadonlyAspect.java
#Aspect
#Component
#Order(0)
#Slf4j
public class TransactionReadonlyAspect {
#Around("#annotation(transactional)")
public Object proceed(ProceedingJoinPoint proceedingJoinPoint,
org.springframework.transaction.annotation.Transactional transactional) throws Throwable {
try {
if (transactional.readOnly()) {
log.info("Inside method " + proceedingJoinPoint.getSignature());
DatabaseContextHolder.set(DatabaseEnvironment.READONLY);
}
return proceedingJoinPoint.proceed();
} finally {
DatabaseContextHolder.reset();
}
}
}
// DatabaseContextHolder.java
public class DatabaseContextHolder {
private static final ThreadLocal<DatabaseEnvironment> CONTEXT = new ThreadLocal<>();
public static void set(DatabaseEnvironment databaseEnvironment) {
CONTEXT.set(databaseEnvironment);
}
public static DatabaseEnvironment getEnvironment() {
DatabaseEnvironment context = CONTEXT.get();
System.out.println("context: " + context);
return CONTEXT.get();
}
public static void reset() {
CONTEXT.set(DatabaseEnvironment.UPDATABLE);
}
}
//DatabaseEnvironment.java
public enum DatabaseEnvironment {
UPDATABLE,READONLY
}
// LoadServiceImpl.java
#Service
public class LoadServiceImpl implements LoadService {
#Override
#Transactional(readOnly = true)
public LoadEntity findById(Integer Id) {
return this.loadDAO.findById(Id);
}
...
}
I just want to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) has been used once then in the TransactionReadonlyAspect finally block it will be reset to DatabaseEnvironment.UPDATABLE which make sense.
However, how to test DatabaseContextHolder.set(DatabaseEnvironment.READONLY) gets called once? Why does this error occur? Is there a better way to test TransactionReadonlyAspect?
I ma using #SpringBootTest for my IT and what I want to do is load a new ApplicationContext for every IT. For this I am overriding the SpringBootTest annotation dynamically and I am providing the same value for classes. But its throwing an exception saying could not load ApplicationContext. Is there an issue if we override the impl for SpringBootTest?
#RunWith(SpringRunner.class)
#ActiveProfiles("test")
#Slf4j
public abstract class OrchestratorBootIT extends BaseIT {
private static final String ANNOTATIONS = "annotations";
public static final String ANNOTATION_DATA = "annotationData";
public static Class<? extends OrchestratorBootIT> runningTestClassType;
public OrchestratorBootIT() {
if(runningTestClassType != null) return;
StepsConfigDAOCacheIT.class.toString());
try {
SpringBootTest annotation = StepsConfigDAOCacheIT.class.getAnnotation(SpringBootTest.class);
//In JDK8 Class has a private method called annotationData().
//We first need to invoke it to obtain a reference to AnnotationData class which is a private class
Method method = Class.class.getDeclaredMethod(ANNOTATION_DATA, null);
method.setAccessible(true);
//Since AnnotationData is a private class we cannot create a direct reference to it. We will have to
//manage with just Object
Object annotationData = method.invoke(StepsConfigDAOCacheIT.class);
//We now look for the map called "annotations" within AnnotationData object.
Field annotations = annotationData.getClass().getDeclaredField(ANNOTATIONS);
annotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> map =
(Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);
map.put(SpringBootTest.class, new SpringBootTestImpl());
SpringBootTest annotation1 = StepsConfigDAOCacheIT.class.getAnnotation(SpringBootTest.class);
log.info("test");
} catch (Exception e) {
e.printStackTrace();
}
}
#BeforeClass
public static void beforeClass() {
BaseIT.beforeClass();
}
#AfterClass
public static void afterClass() {
runningTestClassType = null;
}
#After
public void cleanup() {
super.cleanup();
}
private boolean setDynamicSpringBootTestAnnotation(final Object annotationData) throws Exception {
Field annotations = annotationData.getClass().getDeclaredField("annotations");
annotations.setAccessible(true);
Map<Class<? extends Annotation>, Annotation> map = (Map<Class<? extends Annotation>, Annotation>) annotations.get(annotationData);
map.put(SpringBootTest.class, getSpringBootTest());
return true;
}
protected SpringBootTest getSpringBootTest() {
return new SpringBootTestImpl();
}
static class SpringBootTestImpl implements SpringBootTest {
#Override
public String[] value() {
return new String[0];
}
#Override
public String[] properties() {
return new String[0];
}
#Override
public Class<?>[] classes() {
return new Class[] {CacheConfig.class};
}
#Override
public WebEnvironment webEnvironment() {
return WebEnvironment.MOCK;
}
#Override
public Class<? extends Annotation> annotationType() {
return SpringBootTestImpl.class;
}
}
I'm trying to create a BeanFactory called TaskBeanFactory that I can Autowire into another prototype class that's running on a thread. I want a different instance of a bean returned by the Factory based on a taskName that i want to pass into it but when i start the application i get a null pointer exception because the taskName is null. I had a look at this article but i'm confused about how I should configure the Factory and then pass in the taskName.
The Factory:
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.stereotype.Component;
#Data
#Component
#NoArgsConstructor
public class TaskBeanFactory extends AbstractFactoryBean<GenericTask>{
private TaskNameEnum taskName;
public TaskBeanFactory(TaskNameEnum taskName) {
setSingleton(false);
}
#Override
public Class<?> getObjectType() {
return GenericTask.class;
}
#Override
protected GenericTask createInstance() throws Exception {
switch (taskName) {
case FILE_OPERATION:
return new FileTask();
case DATA_OPERATION:
return new DataTask();
default:
return new GenericTask();
}
}
}
The classes used by the Factory:
#Data
public class GenericTask {
private String idTask;
public void executeTask(Work work) {};
}
#Component
#Scope(value="prototype")
public class FileTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
#Component
#Scope(value="prototype")
public class DataTask extends GenericTask {
#Override
public void executeTask(Work work) {
//some processing
}
}
and the thread that's calling the Factory:
#Slf4j
#Data
#Scope("prototype")
#Component
public class WorkerThread implements Runnable {
#Autowired
private TaskBeanFactory taskBeanFactory;
#Autowired
private DataService dataService;
#Override
public void run() {
//iterate a Map of taskIds from the dataService
taskBeanFactory.setTaskName(TaskNameEnum.valueOf(taskEntry.getKey()));
GenericTask genericTask = taskBeanFactory.getObject();
//expecting genericTask to be of Type FileTask if called with one Key
//or of Type DataTask if called with another
}
}
}
I am caching data using spring cache. Now i want to encrypt few data before writing into cache and decrypt data while reading. So is there any way i can write custom interceptor/aop for #cachable annotation
Instead of using AOP you can simply use a decorator for your Cache and CacheResolver.
public class EncodingCacheResolver implements CacheResolver {
private final CacheResolver delegate;
public EncodingCacheResolver(CacheResolver delegate) {
this.delegate=delegate;
}
#Override
public Collection<? extends Cache> resolveCaches(CacheOperationInvocationContext<?> context) {
Collection<Cache> result = delegate.resolveCaches(context);
return result.stream().map(EncodingCache::new).collect(Collectors.toLlist());
}
}
The cache implementation
public class EncodingCache implements Cache {
private final Cache delegate;
public EncodingCache(Cache delegate) {
this.delegate=delegate;
}
public String getName() {
return delegate.getName();
}
public Object getNativeCache() {
return delegate.getNativeCache();
}
public void evict(Object key) {
delegate.evict(key)
}
public void put(Object key, Object value) {
Object encodedValue = encode(value);
this.delegate.put(key, encodedValue);
}
public <T> T get(Object key, Class<T> type) {
Object encodedValue = delegate.get(key, type);
return decode(encodedValue);
}
// Other Cache methods omitted but the pattern is the same
private Object encode(Object value) {
// encoding logic here
}
private Object decode(Object value) {
// decoding logic here
}
}
Then some configuration
#Configuration
#EnableCache
public void CacheConfiguration {
#Bean
public CacheResolver cacheResolver(CacheManager cacheManager) {
return new EncodingCacheResolver(SimpleCache.of(cacheManager));
}
}
Haven't tested the implementation, typed it from the top of my head. But this should more or less be what you need. You don't really need AOP for this.
I would like to have a Bean and a SubBean like this:
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Component
public class SubBean implements ApplicationContextAware{
private Object parent;
public void setApplicationContext(ApplicationContext ctx){
this.parent = doSomeMagicToGetMyParent(ctx);
}
public Object getParent(){
return parent;
}
}
#Component
public class SomeBean implements InitializingBean{
#Resource
private SubBean sub;
public void afterPropertiesSet(){
Assert.isTrue(this == sub.getParent());
}
}
The trick I want to achieve is, that the SubBean automagically gets a reference to the Bean it got injected into. Because the scope of the subbean is prototype, it will get injected as a new instance in every parent that wants it to get injected.
My big idea is to exploit this pattern to write a LoggerBean which can be injected into normal beans. The subbean should work just like a SLF4J Logger.
So does anyone know the magic to make this work? :)
EDIT: I've found a solution to do this with a custom BeanPostProcessor:
#Component
public class DependencyInjectionAwareBeanPostProcessor implements BeanPostProcessor {
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
for (Field f : bean.getClass().getFields()) {
if (f.getType().isInstance(IDependencyInjectionAware.class)) {
ReflectionUtils.makeAccessible(f);
try {
IDependencyInjectionAware diAware = (IDependencyInjectionAware) f.get(bean);
diAware.injectedInto(bean);
} catch (IllegalArgumentException e) {
ReflectionUtils.handleReflectionException(e);
} catch (IllegalAccessException e) {
ReflectionUtils.handleReflectionException(e);
}
}
}
return bean;
}
}
Here is the Interface:
public interface IDependencyInjectionAware {
void injectedInto(Object parent);
}
And here a Bean using it:
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Component
public class SomeAwareBean implements IDependencyInjectionAware {
private Object parent;
public void injectedInto(Object parent){
this.parent = parent;
}
public Object getParent(){
return parent;
}
}
Here a test with a normal Bean which works perfectly:
#Component
public class UsingBean implements InitializingBean {
#Resource
private SomeAwareBean b;
public void afterPropertiesSet(){
Assert.notNull(b); //works
Assert.isTrue(b.getParent() == this); //works
}
}
Though, when using the same with a normal class which gets the depedencies injected via #Configurable, the test fails:
#Configurable
public class UsingPlainClass implements InitializingBean {
#Resource
private SomeAwareBean b;
public void afterPropertiesSet(){
Assert.notNull(b); //works
Assert.isTrue(b.getParent() == this); //fails because null is returned
}
}
So this seems to have gotten me to another question: Why won't my custom BeanPostProcessor run on a #Configurable classes? Maybe I have to resort to AspectJ afterall...
EDIT: Just to update the status. I did not implement this afterall because this is overengineering...
I find this simpler:
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Component
public class SubBean implements ApplicationContextAware{
private Object parent;
public void setApplicationContext(ApplicationContext ctx){
...
}
public Object getParent(){
return parent;
}
//ADDED CODE
public void setParent(Object parent) {
this.parent = parent;
}
//END ADDED CODE
}
#Component
public class SomeBean implements InitializingBean{
private SubBean sub;
//ADDED CODE
#Resource
public void setSub(SubBean sub) {
this.sub = sub;
sub.setParent(this);
}
//END ADDED CODE
public void afterPropertiesSet(){
Assert.isTrue(this == sub.getParent());
}
}
Fixed several bugs with the solution given by the original poster:
import java.lang.reflect.Field;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.ReflectionUtils;
public interface DependencyInjectionAware {
void injectedInto(final Object bean, final String beanName);
public static class DependencyInjectionAwareBeanPostProcessor implements
BeanPostProcessor {
private static final Logger logger = Logger.getLogger(DependencyInjectionAwareBeanPostProcessor.class);
#Override
public Object postProcessBeforeInitialization(final Object bean,
final String beanName) {
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean,
final String beanName) {
for (final Field f : bean.getClass().getDeclaredFields()) {
logger.info("scanning field " + f.getName() + " of bean " + beanName + " (class= " + bean.getClass() + ")");
if (DependencyInjectionAware.class.isAssignableFrom(f.getType())) {
ReflectionUtils.makeAccessible(f);
try {
final DependencyInjectionAware diAware = (DependencyInjectionAware) f.get(bean);
diAware.injectedInto(bean, beanName);
} catch (final IllegalArgumentException e) {
ReflectionUtils.handleReflectionException(e);
} catch (final IllegalAccessException e) {
ReflectionUtils.handleReflectionException(e);
}
}
}
return bean;
}
}
}