Spring AOP with custom annotation on #Bean method - spring

I'm trying to use AOP with annotation triggering as you can see in this pointcut
package mypackage.aop;
// ...
#Aspect
#Component
public class ErrorHandlerAspect {
private final static Logger LOGGER = LoggerFactory.getLogger(ErrorHandlerAspect.class);
#Pointcut("within(mypackage.config.steps..*) && #annotation(mypackage.aop.SaveAndErrors)")
private void pointcut(){ }
#Around("pointcut()")
private Object errorHandler(ProceedingJoinPoint pjp) throws Throwable{
try{
return pjp.proceed();
} catch (Throwable e){
LOGGER.error("Handling error");
throw e;
}
}
}
Here are declaration of annotation :
package mypackage.aop;
// ...
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface SaveAndErrors { }
... and usage in #Configuration class (for spring batch step configuration) :
package mypackage.config.steps;
// ...
#Configuration
public class StepConfiguration {
public final static String STEP_NAME = "xStep";
// ...
#SaveAndErrors
#Bean(name="xFileReader")
#StepScope
public ItemStreamReader<Object> xFileReader(#Value("#{stepExecutionContext['fileName']}") String resourceName // Inside a partitionner) throws xException {
try {
// ...
// return ...
} catch (yException e) {
throw new xException("new Exception :", e);
}
}
// ...
}
And my Application class :
#SpringBootApplication
public class Application { //...
}
Unfortunately, this is not working.
Removing && #annotation(mypackage.aop.SaveAndErrors) from pointcut, my aop proxy is working.
Where is the mistake?

Related

Class field in Netty ChannelInboundHandler could not autowired

I try to autowire some service class in channelInboundhandler class field.
but when handler use repository field it always null.
Here is my Netty Configuration
my.netty:
server:
bind: 9000
#SpringBootApplication
public class NettyApplication {
public static void main(String[] args) {
SpringApplication.run(NettyApplication.class, args);
}
#Bean
NettyServerConfig serverConfig(#Autowired ServerHandler serverHandler) {
return NettyServerConfig.builder()
.propertiesPrefix("my.netty.server")
.channelInitializer(pipelineOf(loggingChannelHandler(), serverHandler))
.build();
}
#Bean
ChannelHandler loggingChannelHandler() {
return new LoggingHandler(INFO);
}
}
And channelinboundhandler, Service(Repository)
#Slf4j
#Component
#Sharable
public class ServerHandler extends ChannelInboundHandlerAdapter {
#Autowired ServerRepository repository; // always null
#Override
public void channelRead(ChannelHandlerContext ctx, Object object) throws Exception {
log.info("ServerHandler.channelRead()");
String data = repository.findOne();
log.info("data={}", data);
ctx.writeAndFlush(object);
}
}
#Repository
public class ServerRepository {
public String findOne() {
sleep(1000); // data-access time
return "data";
}
private void sleep(int millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Is There any Problem?

Bean not getting overridden in Spring boot

I am trying to write and test an application that used spring-cloud with azure functions following this tutorial.
https://github.com/markusgulden/aws-tutorials/tree/master/spring-cloud-function/spring-cloud-function-azure/src/main/java/de/margul/awstutorials/springcloudfunction/azure
I am tryign to write a testcase and override the bean.
Here is the application class having function and handler Bean function.
#SpringBootApplication
#ComponentScan(basePackages = { "com.package" })
public class DataFunctions extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, Data> {
#FunctionName("addData")
public HttpResponseMessage addDataRun(
#HttpTrigger(name = "add", methods = {
HttpMethod.POST }, authLevel = AuthorizationLevel.FUNCTION) HttpRequestMessage<Optional<String>> request,
final ExecutionContext context) throws JsonParseException, JsonMappingException, IOException {
context.getLogger().info("Java HTTP trigger processed a POST request.");
try {
handleRequest(new GenericMessage<Optional<String>>(request.getBody()), context);
} catch (ServiceException ex) {
ErrorMessage em = new ErrorMessage();
return request.createResponseBuilder(handleException(ex, em)).body(em).build();
}
return request.createResponseBuilder(HttpStatus.CREATED).build();
}
#Autowired
MyService mService;
#Bean
public Consumer<GenericMessage<Optional<String>>> addData() {
ObjectMapper mapper = new ObjectMapper();
return req -> {
SomeModel fp = null;
try {
fp = mapper.readValue(req.getPayload().get(), SomeModel.class);
} catch (Exception e) {
throw new ServiceException(e);
}
mService.addData(fp);
};
}
}
I want to test by overriding the above bean.
Cosmosdb spring configuration
#Configuration
#EnableDocumentDbRepositories
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
#Value("${cosmosdb.collection.endpoint}")
private String uri;
#Value("${cosmosdb.collection.key}")
private String key;
#Value("${cosmosdb.collection.dbname}")
private String dbName;
#Value("${cosmosdb.connect.directly}")
private Boolean connectDirectly;
#Override
public DocumentDBConfig getConfig() {
ConnectionPolicy cp = ConnectionPolicy.GetDefault();
if (connectDirectly) {
cp.setConnectionMode(ConnectionMode.DirectHttps);
} else {
cp.setConnectionMode(ConnectionMode.Gateway);
}
return DocumentDBConfig.builder(uri, key, dbName).connectionPolicy(cp).build();
}
}
Here is the configuration
#TestConfiguration
#PropertySource(value = "classpath:application.properties", encoding = "UTF-8")
#Profile("test")
#Import({DataFunctions.class})
public class TestConfig {
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
#Bean
#Primary
public DocumentDBConfig getConfig() {
return Mockito.mock(DocumentDBConfig.class);
}
}
Finally the test class
#RunWith(SpringRunner.class)
//#SpringBootTest //Enabling this gives initialization error.
#ActiveProfiles("test")
public class TempTest {
#InjectMocks
DataFunctions func;
#Mock
MyService mService;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
private Optional<String> createRequestString(final String res) throws IOException {
InputStream iStream = TempTest.class.getResourceAsStream(res);
String charset="UTF-8";
try (BufferedReader br = new BufferedReader(new InputStreamReader(iStream, charset))) {
return Optional.of(br.lines().collect(Collectors.joining(System.lineSeparator())));
}
}
#Test
public void testHttpPostTriggerJava() throws Exception {
#SuppressWarnings("unchecked")
final HttpRequestMessage<Optional<String>> req = mock(HttpRequestMessage.class);
final Optional<String> queryBody = createRequestString("/test-data.json");
doNothing().when(mService).addData(Mockito.any(SomeModel.class));
doReturn(queryBody).when(req).getBody();
doAnswer(new Answer<HttpResponseMessage.Builder>() {
#Override
public HttpResponseMessage.Builder answer(InvocationOnMock invocation) {
HttpStatus status = (HttpStatus) invocation.getArguments()[0];
return new HttpResponseMessageMock.HttpResponseMessageBuilderMock().status(status);
}
}).when(req).createResponseBuilder(any(HttpStatus.class));
final ExecutionContext context = mock(ExecutionContext.class);
doReturn(Logger.getGlobal()).when(context).getLogger();
doReturn("addData").when(context).getFunctionName();
// Invoke
final HttpResponseMessage ret = func.addDataRun(req, context);
// Verify
assertEquals(ret.getStatus(), HttpStatus.CREATED);
}
}
For this case instead of test configuration addData the actual bean is called from DataFunctions class. Also the database connection is also created when it should use the mocked bean from my test configuration. Can somebody please point out what is wrong in my test configuration?
I was able to resolve the first part of cosmos db config loading by marking it with
#Configuration
#EnableDocumentDbRepositories
#Profile("!test")
public class CosmosDBConfig extends AbstractDocumentDbConfiguration {
...
}
Also had to mark the repository bean as optional in the service.
public class MyService {
#Autowired(required = false)
private MyRepository myRepo;
}
Didn't use any spring boot configuration other than this.
#ActiveProfiles("test")
public class FunctionTest {
...
}
For the second part of providing mock version of Mock handlers, I simply made the test config file as spring application as below.
#SpringBootApplication
#ComponentScan(basePackages = { "com.boeing.da.helix.utm.traffic" })
#Profile("test")
public class TestConfiguration {
public static void main(final String[] args) {
SpringApplication.run(TestConfiguration.class, args);
}
#Bean(name="addData")
#Primary
public Consumer<GenericMessage<Optional<String>>> addData() {
return req -> {
System.out.println("data mock");
};
}
}
and made use of this constructor from azure functions library in spring cloud in my constructor
public class AppFunctions
extends AzureSpringBootRequestHandler<GenericMessage<Optional<String>>, List<Data>> {
public AppFunctions(Class<?> configurationClass) {
super(configurationClass);
}
}
public AzureSpringBootRequestHandler(Class<?> configurationClass) {
super(configurationClass);
}
Hope it helps someone.

Spring aop: advice never reached

I've create this pointcut:
#Pointcut(value = "execution(* net.space.service.RepositoryService.createDocumentFromBytes(..))")
public void groupBytesMethod() {
}
and this advice:
#Around("groupBytesMethod()")
public Object groupMetrics(ProceedingJoinPoint point) throws Throwable {
Object result = point.proceed();
}
I've set a breakpoint, but it's never reached.
package net.space.service;
#Service
public class RepositoryService {
private Reference createDocumentFromBytes(String id, byte[] content) throws IOException {...}
public Reference groupDocuments(#NotNull RepositoryGroupForm groupForm) {
return this.createDocumentFromBytes("id", new byte[10]);
}
}
There are many ways to make this work, I would suggest use an annotation,something like :
#Documented
#Target(ElementType.METHOD)
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface GroupBytesMethod {
}
Then
#Aspect
#Component
public class MyAspect {
#Around("#annotation(GroupBytesMethod)") // use full package name of annotation
public Object groupMetrics(ProceedingJoinPoint point) throws Throwable {
// do something before
Object result = null;
try {
result = point.proceed();
}
catch (Throwable t) {
// handle an exception
}
// do something after
return result;
}
}
Example here

Spring AOP Cannot work for #Around

I am currently working on AOP but some how the AOP cannot working, The #around is not invoking after and before the execution on specific controller.Folloeing is my code.
// my controller
package com.rest.controllers;
class ManCon{
#PostMapping(EndPointReferrer.UPDATE_DEALER_SCHEME_MAPPING_DETAILS)
public ResponseEntity<BaseResponse> updateDealerSchemeMappingDetails(
#RequestBody #NotNull #Valid Myclass obj){
return null;
}
}
//configuration
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
#ComponentScan(basePackages = {"com.aop"})
public class AOPConfiguration {
#Bean(autowire = Autowire.BY_TYPE)
public SchemeMappingPortalAspect schemeMappingPortalAspect(){ return new SchemeMappingPortalAspect(); }
}
//Aspect
#Aspect
public class SchemeMappingPortalAspect {
private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SchemeMappingPortalAspect.class);
#Pointcut("execution(* com.rest.controllers.ManCon.updateDealerSchemeMappingDetails(..))")
public void schemeMappingPointcut(){
}
/*
* This is used for scheme mapping portal loging
* */
#Around(value = "schemeMappingPointcut()")
public Object loggingSchemePortalDetail(ProceedingJoinPoint joinPoint) throws Throwable{
logger.debug("-------------------------");
logger.debug("In before calling");
logger.debug("-------------------------");
Object o=null;
try {
o= joinPoint.proceed();
System.out.println("JSON::"+ new Gson().toJson(o));
}catch (Exception e){
e.printStackTrace();
}
logger.debug("-------------------------");
logger.debug("In After Calling");
logger.debug("-------------------------");
return o;
}
}

Get parent bean in prototype bean that gets injected

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;
}
}
}

Resources