I am trying to use AOP for logging all method calls while entering and leaving a method, using the below bean and aop declaration in my beans xml,
<aop:config>
<aop:pointcut id="componentAnnotatedClass" expression="execution(* com.test.*.*.*(..))" />
<aop:advisor advice-ref="loggingAdvisor" pointcut-ref="componentAnnotatedClass" />
</aop:config>
<bean id="loggingAdvisor" class="org.springframework.aop.interceptor.CustomizableTraceInterceptor">
<property name="enterMessage" value="Entering $[targetClassShortName].$[methodName]($[arguments])" />
<property name="exitMessage" value="Leaving $[targetClassShortName].$[methodName](): $[returnValue], Total Execution Time : $[invocationTime]ms" />
</bean>
But when i use this i am getting an exception like below,
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class [class com.test.dao.BCommonDataAccessor]: Common causes of this problem include using a final class or a non-visible class; nested exception is java.lang.IllegalArgumentException: Superclass has no null constructors but no arguments were given
When i browsed through this, i got to know that cglib requries a default consructor, but in my app i have a parametrized constructor to inject my data source. I cannot have a default constructor in my data accessor. Is there any workaroud to over come this issue.
Note: I am using spring3.2 vesion
Related
I have project with Spring configuration in XML file. I added below aspect with pointcut.
<aop:aspectj-autoproxy/>
<aop:config proxy-target-class="true">
<aop:aspect id="customAuditAspect" ref="customAudit">
<aop:pointcut id="customAuditPointcut"
expression="#target(lombok.NoArgsConstructor)"/>
<aop:before pointcut-ref="customAuditPointcut" method="customAuditUpdate"/>
</aop:aspect>
</aop:config>
And this is a bean, which abovementioned pointcut refers to:
<bean id="customAudit" class="com.socha.modules.inspektr.aspect.AuditCustomUpdateAspect"/>
This is class:
#Slf4j
#NoArgsConstructor
public class AuditCustomUpdateAspect {
#Autowired
JdbcTemplate jdbcTemplate;*
public void customAuditUpdate() {
log.warn("here I am");
}
}
When i deploy Web app with this feature, it complains in following way:
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'dataSourceAudit'
defined in ServletContext resource [/WEB-INF/spring-context/portlet-application-context.xml]:
Unsatisfied dependency expressed through constructor parameter 0:
Could not convert argument value of type [com.sun.proxy.$Proxy1719]
to required type [com.zaxxer.hikari.HikariConfig]:
Failed to convert value of type 'com.sun.proxy.$Proxy1719
implementing org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised,org.springframework.cglib.proxy.Factory,com.zaxxer.hikari.HikariConfigMXBean,org.springframework.core.DecoratingProxy'
to required type 'com.zaxxer.hikari.HikariConfig';
nested exception is java.lang.IllegalStateException:
Cannot convert value of type 'com.sun.proxy.$Proxy1719
implementing org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised,org.springframework.cglib.proxy.Factory,com.zaxxer.hikari.HikariConfigMXBean,org.springframework.core.DecoratingProxy'
to required type 'com.zaxxer.hikari.HikariConfig':
no matching editors or conversion strategy found
Below I am attaching this bean with all its dependent beans:
<bean id="inspektrTransactionTemplate"
class="org.springframework.transaction.support.TransactionTemplate"
p:transactionManager-ref="txManagerAudit" p:isolationLevelName="ISOLATION_READ_COMMITTED"
p:propagationBehaviorName="PROPAGATION_REQUIRED"/>
<bean id="auditHikariCPConfig" class="com.zaxxer.hikari.HikariConfig">
<property name="poolName" value="auditHikariCP"/>
</bean>
<bean id="dataSourceAudit" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<constructor-arg ref="auditHikariCPConfig"/>
</bean>
I understand more or less how is AOP in Spring working. Class HikariDataSource of bean dataSourceAudit implements some interfaces and by default it applies JDK proxying. In above snippet I am trying to apply proxy-target-class=true, but it still fails. I see when I add this setting, that implemented interfaces changes a bit - org.springframework.cglib.proxy.Factory appears, but content of error is still the same. Maybe I am eventually failing to apply this setting on HikariDataSource bean and that's why it is not working?
Thank you in advance for any hints
This particular problem was solved by narrowing scope of the classes to be adviced , as R.G and Kriegaex suggested. Errors stopped occuring
Thank you
Problem:
I'm integrating with a library written by the other team. This library provides a set of classes that I'm using in my Spring-driven application. Every bean in my application is in singleton scope.
Also 99% of the classes from that library uses constructor injection.
I'm using XML and my configuration looks very similar to the following:
<bean id="lib.service1" class="lib.Service1"/>
<bean id="lib.service2" class="lib.Service2">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service3" class="lib.Service3">
<constructor-arg ref="lib.service1"/>
</bean>
<bean id="lib.service4" class="lib.Service3">
<constructor-arg ref="lib.service2"/>
<constructor-arg ref="lib.service3"/>
</bean>
<!-- other bean definitions -->
<bean id="lib.serviceN" class="lib.ServiceN">
<constructor-arg ref="lib.serviceX"/>
<constructor-arg ref="lib.serviceY"/>
<constructor-arg ref="lib.serviceZ"/>
<constructor-arg ref="lib.serviceK"/>
</bean>
<!-- other bean definitions -->
What I want:
I want to simplify my configuration to not to use bean IDs and ask spring to do constructor injection for me based on the type of arguments in the bean constructors. I can also ask library implementers to add #Inject annotation to the class constructors (the 99% of the classes have just one public constructor), but this is all that I can ask wrt refactoring of their library.
And eventually I want to have just following in my spring config (doesn't work, but illustrates the idea):
<bean class="lib.Service1"/>
<bean class="lib.Service2"/>
<bean class="lib.Service3"/>
<!-- ... -->
<bean class="lib.ServiceN"/>
Here I'm expecting Spring to figure out that I want to use constructor injection for all those beans and infer bean instances based on the constructor argument types.
Note that I cannot use component scan - they have one package (lib. in the example given above) and some classes in that package are useless for my application and too expensive to be needlessly created. Plus some classes that I'm not using are experimental and can be changed/renamed/removed without prior notice.
Add autowire="constructor", assuming these bean types only have one constructor and that the corresponding parameters match single beans.
<bean class="lib.Service1" autowire="constructor"/>
From the documentation
"constructor"
Analogous to "byType" for constructor arguments. If there
is not exactly one bean of the constructor argument type in the
bean factory, a fatal error is raised. Note that explicit
dependencies, i.e. "property" and "constructor-arg" elements, always
override autowiring. Note: This attribute will not be inherited by
child bean definitions. Hence, it needs to be specified per concrete
bean definition.
I'm trying to create the following bean bean
<bean id="couchBaseExecutor" class="java.util.concurrent.ScheduledThreadPoolExecutor">
<constructor-arg name="corePoolSize" value="10"></constructor-arg>
</bean>
but it fails with the exception
13:48:24.206 [main] DEBUG o.s.c.LocalVariableTableParameterNameDiscoverer - ASM ClassReader failed to parse class file [class java.util.concurrent.ScheduledThreadPoolExecutor], probably due to a new Java class file version that isn't supported yet - unable to determine constructors/methods parameter names
java.lang.IllegalArgumentException: null
Does anyone have any idea why?
The core Java classes don't have debug symbols/parameter metadata, so Spring can't determine the name of the constructor arguments. In that case, you have to use the index attribute with value 0 for the first constructor-arg, like this:
<constructor-arg index="0" value="10"/>
I am trying to bind to an existing legacy webservice that is running in a test environment hosted on a JBoss server but Spring cannot create the bean due to the below exception:
Caused by: javax.xml.ws.WebServiceException: Unable to create JAXBContext
at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:153)
at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.postProcess(AbstractSEIModelImpl.java:83)
at com.sun.xml.internal.ws.model.RuntimeModeler.buildRuntimeModel(RuntimeModeler.java:244)
at com.sun.xml.internal.ws.client.WSServiceDelegate.createSEIPortInfo(WSServiceDelegate.java:687)
at com.sun.xml.internal.ws.client.WSServiceDelegate.addSEI(WSServiceDelegate.java:675)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:330)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:313)
at com.sun.xml.internal.ws.client.WSServiceDelegate.getPort(WSServiceDelegate.java:295)
at javax.xml.ws.Service.getPort(Service.java:92)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.getPortStub(JaxWsPortClientInterceptor.java:413)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.prepare(JaxWsPortClientInterceptor.java:337)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.afterPropertiesSet(JaxWsPortClientInterceptor.java:316)
at org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean.afterPropertiesSet(JaxWsPortProxyFactoryBean.java:42)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1514)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1452)
... 38 more
Caused by: java.security.PrivilegedActionException: com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions
java.lang.StackTraceElement does not have a no-arg default constructor.
this problem is related to the following location:
at java.lang.StackTraceElement
at public java.lang.StackTraceElement[] java.lang.Throwable.getStackTrace()
at java.lang.Throwable
at java.lang.Exception
at uk.co.example.UserException
at public javax.xml.bind.JAXBElement uk.co.example.ObjectFactory.createUserException(uk.co.example.UserException)
at uk.co.example.ObjectFactory
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.xml.internal.ws.model.AbstractSEIModelImpl.createJAXBContext(AbstractSEIModelImpl.java:140)
I'm binding to the webservice via Spring xml:
<bean id="auth1" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean">
<property name="serviceInterface" value="uk.co.example.Authentication" />
<property name="wsdlDocumentUrl" value="http://testdomain.co.uk:30001/services/authentication?wsdl" />
<property name="namespaceUri" value="http://example.co.uk/" />
<property name="serviceName" value="AuthenticationWebServiceService" />
<property name="portName" value="AuthenticationPort" />
<property name="maintainSession" value="true" />
</bean>
My UserException simply extends java.lang.Exception:
public class UserException extends Exception
{
}
So my webservice contains a method that declares a throw of UserException. UserException extends java.lang.Exception which contains a reference to StackTraceElement which doesn't have a no-arg constructor. Hence the JAXB exception. That much I've picked up from some other posts on this matter.
What I don't understand is: why this particular webservice and this particular exception class? And of course, how I correct this?
My webservice throws numerous other exceptions (most appear to extend java.lang.Exception). Another webservice (which doesn't throw UserException, but throws other bespoke Exception subclasses) that runs in the same JBoss server works fine. The 2 different sets of webservices have different client jars.
I've simply created an Eclipse Maven project, pulled in the Spring test and web jars (v3.1.0), junit and log4j. The 2 client jars (pre-existing as part of the legacy build) are also on the classpath. I've created a Junit that autowires the proxy bean as the Authentication interface and call an authenticateUser() method on it.
Because it's legacy code, I can't change anything in the running webservice, or the generated client jars.
Any suggestions?
I'll admit to been a bit of a novice when it comes to webservices and also how our legacy code is built and deployed, so don't be afraid to ask anything that you think might be too obvious!
If you can't change the client or the server code, you could still write your own client that extends the existing client and try to add a custom JAXB binding there.
In my java class I have:
#Autowired
#Qualifier("customerProviderExec")
private DefaultCustomerProvider customerProvider;
And in my context configuration XML
<bean id="customerProviderExec" class="my.package.DefaultCustomerProviderExecutor">
<property name="defaultCustomerProviderService" ref="customerProviderImpl" />
</bean>
<bean id="testCustomerProviderImpl" class="my.package.DefaultCustomerProviderTest">
<property name="customerProviderImpl" ref="customerProviderImpl" />
</bean>
<bean id="customerProviderImpl" class="my.package.DefaultCustomerProviderImpl">
...
</bean>
Important: The class DefaultCustomerProviderImpl implements DefaultCustomerProvider
When I try to execute in my Java class:
DefaultCustomerProviderExecutor executor = (DefaultCustomerProviderExecutor)this.getCustomerProvider();
return (DefaultCustomerProviderImpl) executor.getDefaultCustomerProviderService();
I get the error:
Caused by: java.lang.ClassCastException: $Proxy17 cannot be cast to my.package.DefaultCustomerProviderImpl
Has someone been throug this?
return (DefaultCustomerProvider) executor.getDefaultCustomerProviderService();
Casting to the implementation is defying the meaning of having an interface defined.
Why do you cast interface to its implementation? Interfaces are to prevent this. You should normally use only interface.
Since by default Spring does not generate proxy for classes, only Java proxies, the bean you get from context is implementing all the bean's interface, but does not extend the bean itself (original bean is only wrapped by the proxy).