Spring AOP #Around access the value of #annotation - spring

I have a custom annotation as,
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
public #interface XAudit {
AuditActionType action();
}
I am using this annotation on some method as following,
#XAudit(action = "REGISTRATION")
public RegistrationDTO register(UserDetail detail) {
//Logic
return dto;
}
I am capturing event in aspect as following,
#Around(value = "#annotation(XAudit)")
public Object postAuditEvent(ProceedingJoinPoint joinPoint, XAudit xAudit) throws Throwable {
String action = xAudit.action(); //Accessing Action for logic decision making
Object result = joinPoint.proceed();
//audit processing logic
return result;
}
The #Around advice works well with only ProceedingJoinPoint argument. If passing XAudit as 2nd argument, it throws following error,
Error creating bean with name 'metaDataSourceAdvisor': Cannot resolve reference to bean 'methodSecurityMetadataSource' while setting constructor argument; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration': Unsatisfied dependency expressed through method 'setObjectPostProcessor' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.security.config.annotation.configuration.ObjectPostProcessorConfiguration': Initialization of bean failed; nested exception is java.lang.IllegalArgumentException:
error at ::0 formal unbound in pointcut
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:498)
at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:204)
at org.springframework.context.support.PostProcessorRegistrationDelegate.registerBeanPostProcessors(PostProcessorRegistrationDelegate.java:238)
at org.springframework.context.support.AbstractApplicationContext.registerBeanPostProcessors(AbstractApplicationContext.java:710)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:535)
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140)
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:759)
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:395)
at org.springframework.boot.SpringApplication.run(SpringApplication.java:327)
I need to use xAudit inside aspect in order to access action of XAudit. Kindly share some pointers on how can I access the value of #XAudit inside #Around Aspect.

There is a typo in the #Around(value = "#annotation(XAudit)") , it should be xAudit ( x is lower case)
Try : #Around(value = "#annotation(xAudit)")
Also , to get the action value from annotation you need to use xAudit.action() and not xAudit.getAction().
Complete code would be as follows
#Component
#Aspect
public class XAuditAspect {
#Around(value = "#annotation(xAudit)")
public Object postAuditEvent(ProceedingJoinPoint joinPoint, XAudit xAudit) throws Throwable {
String action = xAudit.action(); //Accessing Action for logic decision making
Object result = joinPoint.proceed();
//audit processing logic
System.out.println(xAudit.action());
return result;
}
}

Related

DelegatingDataSource Spring boot

I am trying to implement Spring Boot AOP for data-source pointcut - where before running any query I need to set client context in DB connection.
I was trying this approach of using DelegatingDataSource. But I am getting below error during server startup
org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'dataSource': Requested bean is currently in creation: Is there an unresolvable circular reference?
Please let me know DeletegatingDatasource for JNDI based DB lookup.
Edit 1: AOP - I tried to add pointcut execution(public * javax.sql.DataSource+.getConnection(..)). This works only when Spring datasource is used with username/password. Once i deploy in Jboss with JNDI I am getting WildFlyDataSource Proxy error. So, instead of AOP approach I thought of using DelegatingDatasource
// AOP Example
#Pointcut("execution(public * javax.sql.DataSource+.getConnection(..))")
void prepareConnectionPointcut() {
logger.debug("prepareConnectionPointcut");
}
#AfterReturning(pointcut = "prepareConnectionPointcut()", returning = "connection")
void afterPrepareConnection(Connection connection) {
// Set context in Connection - return same connection for query execution
}
But when i deploy this code in JBoss - I am getting WildFlyDataSource datasource bean creation error.
Error creating bean with name
'org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaConfiguration':
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'dataSource' defined in class path resource
[org/springframework/boot/autoconfigure/jdbc/JndiDataSourceAutoConfiguration.class]:
Initialization of bean failed; nested exception is
org.springframework.aop.framework.AopConfigException: Could not
generate CGLIB subclass of class
org.jboss.as.connector.subsystems.datasources.WildFlyDataSource:
Common causes of this problem include using a final class or a
non-visible class; nested exception is
org.springframework.cglib.core.CodeGenerationException:
java.lang.NoClassDefFoundError-->org/jboss/as/connector/subsystems/datasources/WildFlyDataSource
I have also added proxyTargetClass flag during initialization
#EnableAspectJAutoProxy(proxyTargetClass = true)
Thanks #M.Deinum for recommendation of using BeanPostProcessor & Implement DelegatingDatasource for setting client info. Please find snippet below which i have implemented to accomplish this in Spring Boot which works well with JBoos JNDI based connection or Spring Boot URL Datasource connection.
#Component
public class MyBeanPostProcessor implements BeanPostProcessor {
private static Logger logger = LoggerFactory.getLogger(MyBeanPostProcessor.class);
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource) {
// Check DataSource bean initialization & enclose it with DelegatingDataSource
logger.debug("MyBeanPostProcessor:: postProcessAfterInitialization:: DataSource");
DataSource beanDs = (DataSource) bean;
return new MyDelegateDS(beanDs);
}
return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof DataSource) {
logger.debug("MyBeanPostProcessor:: postProcessBeforeInitialization:: DataSource");
}
logger.debug("MyBeanPostProcessor:: postProcessBeforeInitialization:: " + beanName);
return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
}
My implementation of DelegatingDataSource to handle each user request to set client context in DB connection session
public class MyDelegateDS extends DelegatingDataSource {
private static Logger logger = LoggerFactory.getLogger(MyDelegateDS.class);
public MyDelegateDS(DataSource delegate) {
super(delegate);
logger.debug("MyDelegateDS:: constructor");
}
#Override
public Connection getConnection() throws SQLException {
logger.debug("MyDelegateDS:: getConnection");
// To do this context only for user Request - to avoid this during Server initialization
if (RequestContextHolder.getRequestAttributes() != null
&& ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest() != null) {
logger.debug("MyDelegateDS:: getConnection: valid user request");
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes())
.getRequest();
// Checking each user request & calling SP to set client context before invoking actual native query/SP
}
logger.debug("MyDelegateDS:: getConnection: Not User Request");
return super.getConnection();
}
}
Hope this is helpful for someone facing same problem

Bean instantiation via factory method failed

I am getting below error:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'fileStorageServiceBean' defined in com.primesolutions.fileupload.settings.FileStorageProperties: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.primesolutions.fileupload.service.FileStorageService]: Factory method 'fileStorageServiceBean' threw exception; nested exception is java.lang.StackOverflowError
#ConfigurationProperties(prefix = "file")
public class FileStorageProperties {
private String uploadDir;
#Bean
public FileStorageService fileStorageServiceBean() throws Exception{
return new FileOnDiskStorageService(); // some random name; don't exist
}
public String getUploadDir() {
return uploadDir;
}
public void setUploadDir(String uploadDir) {
this.uploadDir = uploadDir;
}
}
Your method:
public FileStorageService fileStorageServiceBean() throws Exception{
return fileStorageServiceBean();
}
Is a recursive function. Which causes the stackOverflowError.
You get a java.lang.StackOverflowError because the method fileStorageServiceBean() is calling itself and that will result in an infinite loop. That code part can never end in this situation.
It could/should be something like:
#Bean
public FileStorageService fileStorageServiceBean() throws Exception{
return new FileOnDiskStorageService(); // some random name; don't exist
}
If you use a good IDE it will warn you about this. It's a good advice to always use a good IDE for Java and read the warnings. Also installing the plugins like SonarQube, Findbugs is really helpful to prevent for code issues.
Also the errors stacktrace will give a line number and some info where in the code the error occurs.

Cucumber Spring and class configuration

I'm struggling with Cucumber and Spring configuration.
I'm writing selenium framework using Page Object Pattern, with BrowserFactory.
When I use #ComponentScan, #Component and #Autowire annotations everything works fine, but when I want to create a bit more complicated bean with #Bean annotation (BrowserFactory which registers few browser drivers) in #Configuration class it does not work, during debug I'm getting nulls on every single variable I'm trying to Autowire.
I'm using Spring 4.2.4, all cucumber dependencies in version 1.2.4.
Config:
#Configuration
public class AppConfig {
#Bean
#Scope("cucumber-glue")
public BrowserFactory browserFactory() {
BrowserFactory browserFactory = new BrowserFactory();
browserFactory.registerBrowser(new ChromeBrowser());
browserFactory.registerBrowser(new FirefoxBrowser());
return browserFactory;
}
#Bean(name = "loginPage")
#Scope("cucumber-glue")
public LoginPage loginPage() throws Exception {
return new LoginPage();
}
#Bean(name = "login")
#Scope("cucumber-glue")
public Login login() {
return new Login();
}
}
POP:
public class LoginPage extends Page {
public LoginPage() throws Exception {
super();
}
...
}
Page:
public class Page {
#Autowired
private BrowserFactory browserFactory;
public Page() throws Exception{
...
}
}
Login:
public class Login {
#Autowired
private LoginPage loginPage;
public Login(){}
...
}
Steps:
#ContextConfiguration(classes = {AppConfig.class})
public class LoginSteps {
#Autowired
Login login;
public LoginSteps(){
}
#Given("^an? (admin|user) logs in$")
public void adminLogsIn(Login.User user) throws Exception {
World.currentScenario().write("Logging in as " + user + "\n");
login.as(user);
}
}
Error:
cucumber.runtime.CucumberException: Error creating bean with name 'LoginSteps': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: Login LoginSteps.login; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'login': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private LoginPage Login.loginPage; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginPage' defined in AppConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [LoginPage]: Factory method 'loginPage' threw exception; nested exception is java.lang.NullPointerException
And now for the fun part...
BrowserFactory in World class is properly Autowired!!
World:
public class World {
#Autowired
private BrowserFactory browserFactory;
...
}
So I'll answer my own question:)
Issue was that I was calling BrowserFactory inside of Page constructor.
Looks like this bean was not yet created and was causing NPEs.
In order to fix that I:
Added #Lazy annotation to configuration (all elements that use this configuration, both defined in that class and those which will be found by Scan will be created as Lazy)
Moved call to Browser Factory to #PostConstruct method
Two more things to increase readability of Spring config:
Added #ComponentScan to configuration
Classes with no constructor parameters are annotated with #Component and #Scope("cucumber-glue") annotation so bean creation can be removed from AppConfig.class

How to create a Spring bean for apache logging Log class?

I'd like to create an autowired bean in a Dao class in order to do logging opperations. My way was hitherto static final statement like this:
private static final Log log = LogFactory.getLog(LoggedClass.class);
But now I'm trying to use IoC to turn classes decoupled.
If just add configuration in pom.xml and try to do sth like
#Autowired
Log log;
I receive an error message:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'funciDaoImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: org.apache.commons.logging.Log br.com.bb.dirco.dao.impl.FunciDaoImpl.log; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'log' defined in class path resource [com/company/project/util/PersistenceConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.lang.Class]: : No qualifying bean of type [java.lang.Class] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [java.lang.Class] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {}
In order to get a logger, I had to provide a class to getLog method on LogFactory class and attribute it to Log instance. There's a way to do it using #Autowired Spring IoC? Thanks!
You can inject only those objects which are managed/created by Spring container. You have to register your bean (or factory method creating the bean) with container (with annotations like #Component/#Singleton/... or directly in xml)
In your case it's not very applicable since you have to have many different types (for every class) of logger objects provided by Spring and then when you inject they would have to be identified by different name/type for every class.
P.S. I don't see any problem using it the way you use it now
Where I work we have implemented support for #Autowired SLF4J Loggers using Springs BeanPostProcessor.
First you need to define an Logger placeholder bean in your application context. This bean is going to be injected by Spring into all bean with a #Autowired Logger field.
#Configuration
public class LoggerConfig {
#Bean
public Logger placeHolderLogger() {
return PlaceHolder.LOGGER;
}
#Bean
public AutowiredLoggerBeanPostProcessor loggerPostProcessor() {
return new AutowiredLoggerBeanPostProcessor();
}
}
Then you an AutowiredLoggerBeanPostProcessor which inspects all beans, indetify bean that contain Logger fields annotated with #Autowired (at this point should contain a reference to the Logger placeholder bean), create a new Logger for the partilcar bean an assigned it to the fields.
#Component
public class AutowiredLoggerBeanPostProcessor implements BeanPostProcessor, PriorityOrdered {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
attachLogger(bean);
return bean;
}
#Override
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
attachLogger(bean);
return bean;
}
private void attachLogger(final Object bean) {
ReflectionUtils.doWithFields(bean.getClass(), new FieldCallback() {
public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
if (Logger.class.isAssignableFrom(field.getType()) &&
(field.isAnnotationPresent(Autowired.class) ||
field.isAnnotationPresent(Inject.class))) {
ReflectionUtils.makeAccessible(field);
if (field.get(bean) == PlaceHolder.LOGGER) {
field.set(bean, LoggerFactory.getLogger(bean.getClass()));
}
}
}
});
}
#Override
public int getOrder() {
return HIGHEST_PRECEDENCE;
}
}

Spring bean definition - get bean class

I'm trying the get Bean class name without initialize the bean.
I need to know the class , I could get the bean from applicationContext and to check the class name from the bean instance, But I want to know the class with out actually creating/init the bean..
Is it possible ?
Object bean = applicationContext.getBean("beanName");
bean.getClass();
You can't do this after creating the ApplicationContext. Most ApplicationContext implementations will refresh() themselves and force instantiation of the beans.
What you can do is create a BeanFactoryPostProcessor in which you get the target bean definition and check the bean class.
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
String className = beanFactory.getBeanDefinition("").getBeanClassName();
}
But note, as the javadoc for getBeanClassName() states
Hence, do not consider this to be the definitive bean type at runtime
but rather only use it for parsing purposes at the individual bean
definition level.
So use it with a grain of salt.
If you give us more details as to what you are trying to accomplish, there might be alternatives.
The code provided by Sotirious will not work for the beans that have parent beans and for the beans that are defined using Java Config or using #Component annotation (and similar annotatios like #Service, #Repository, #Component).
Just an extension which checks if it is AnnotatedBeanDefinition or if the bean has a parent:
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
for (String beanDefinitionName : beanFactory.getBeanDefinitionNames()) {
String beanClassName = getBeanClassName(beanDefinitionName, beanFactory);
}
}
private String getBeanClassName(String beanName, ConfigurableListableBeanFactory beanFactory) {
String beanClassName;
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
if (beanDefinition instanceof AnnotatedBeanDefinition) {
AnnotationMetadata metadata = ((AnnotatedBeanDefinition) beanDefinition).getMetadata();
beanClassName = metadata.getClassName();
} else {
beanClassName = beanDefinition.getBeanClassName();
while (beanClassName == null) {
BeanDefinition parentBeanDefinition = beanFactory.getBeanDefinition(beanDefinition.getParentName());
beanClassName = parentBeanDefinition.getBeanClassName();
beanDefinition = parentBeanDefinition;
}
}
return beanClassName;
}
Note, this approach will not work in the case factory method is used. As Java Doc says:
Also, this may just be the class that a factory method is called on, or it may even be empty in case of a factory bean reference that a method is called on. Hence, do not consider this to be the definitive bean type at runtime but rather only use it for parsing purposes at the individual bean definition level.

Resources