AspectJExpressionPointcut uses wrong classLoader - java

I have a Java EE application consisting of multiple OSGi bundles running within Apache Felix container. One of these bundles is responsible for loading Spring application context.
I'm using Spring AOP in my application and the following error arised in my bundle:
java.lang.IllegalArgumentException: warning no match for this type name: com.somepackage.SomeClass [Xlint:invalidAbsoluteTypeName]
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:301)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:206)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:192)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:169)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:208)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:262)
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:294)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:118)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:88)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:69)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:330)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:293)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:422)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1573)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:540)
The cause of this problem is that class(com.somepackage.SomeClass) used in pointcat of my aspect was loaded by bundle ClassLoader but AspectJExpressionPointcut passes default ClassLoader to buildPointcutExpression() method:
private void checkReadyToMatch() {
if (getExpression() == null) {
throw new IllegalStateException("Must set property 'expression' before attempting to match");
}
if (this.pointcutExpression == null) {
this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
ClassUtils.getDefaultClassLoader());
this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
}
}
which knows nothing about this class (com.somepackage.SomeClass).

The problem was solved by changing ClassLoader in thread initializing Spring Application Context:
Thread.currentThread().setContextClassLoader(bundleClassLoader);
So method ClassUtils.getDefaultClassLoader() returns bundleClassLoader which can load class mentioned in pointcut.

Related

Start spring-boot application with classLoader

I'm searched a lot for a solution for my case, basically i will start/run my spring boot application in other spring boot application.
How i tried do:
String[] args = { "-Dspring.profiles.active=development",
"-Dspring.config.location=config/example-db-development.properties,config/example-custom-development.properties",
"-Dloader.path=test/lib" };
File fatJar = new File("D:/spring-boot-example/test/spring-boot-example-api.jar");
ClassLoader classLoader = new URLClassLoader(new URL[] { fatJar.toURI().toURL() });
Class<?> mainClass = classLoader.loadClass(getMainClassName(fatJar));
Method mainMethod = mainClass.getMethod("main", String[].class);
mainMethod.invoke(null, new Object[] { args });
private static String getMainClassName(File fatJar) throws IOException {
try (JarFile jarFile = new JarFile(fatJar)) {
return jarFile.getManifest().getMainAttributes().getValue(Attributes.Name.MAIN_CLASS);
}
}
This started the example project, however, I need to add some dependencies of the example project to the project that should start the same.
And I'm still having problems for springboot to be able to create beans, for example:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'vendaPDVControllerImpl' defined in URL [jar:file:/D:/dsv-git/dsv-java/pdv-prototipos/spring-boot-integrador-testes/test/spring-boot-integrador-api.jar!/BOOT-INF/classes!/br/com/sysmo/integrador/api/impl/VendaPDVControllerImpl.class]: Post-processing of merged bean definition failed; nested exception is java.lang.IllegalStateException: Failed to introspect Class [br.com.sysmo.integrador.api.impl.VendaPDVControllerImpl] from ClassLoader [org.springframework.boot.loader.LaunchedURLClassLoader#31f924f5]
I do not want to have to add dependencies of the sample project on the project that should start it, and I already looked in the stack overflow for solutions, but found none that solves my problem.
I'd like something like this, but from the classLoader, or another solution that makes it possible to start my application without adding its dependencies to my project:
java -jar -Dspring.profiles.active=development -Dspring.config.location=config/example-db-development.properties,config/example-custom-development.properties -Dloader.path=lib example-api-1.00.00.000.jar

JCA Connector Classloading

in my Scenario, i try to use JCA Adapters to connect to an external storage - just to try this feature of J2EE.
I use JBoss EAP 7 and its packed implementation ironjacamar.
i deploy an adapter.rar, which contains an adapter.jar (this contains the Connection and ConnectionFactory Interfaces and all implementations) and META-INF/ironjacamar.xml.
I then deploy a app.war file, containing a Bean with an annotated field:
#RequestScoped
public class Bean {
...
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactory connectionFactory;
}
The war also contains the adapter.jar as library - as it needs to know of all the classes at runtime (NoClassDefFound etc.)
To my amazement, the Connector itself seems to work - as is get the Exception:
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
and on ommitting the interfaces even:
#Resource(lookup = "java:/eis/StorageConnectionFactory")
private StorageConnectionFactoryImpl connectionFactory;
still
java.lang.IllegalArgumentException: Can not set conn.StorageConnectionFactoryImpl field Bean.connectionFactory to conn.HsmConnectionFactoryImp
I see that the Problem is, that the adapter.rar does nto share the same classloader as the app.war and both contain the corresponding classes, leading to a sort of ClassCastException - how do i solve this issue correctly?
It seems you haven't configure resource adapter properly.
See the below guide, it will help you to configure:
https://access.redhat.com/documentation/en/red-hat-jboss-enterprise-application-platform/version-7.0/configuration-guide/#configuring_jca_subsystem

No bean class specified on bean definition

I am sorry that this is probably too vague and generel of a question. I am trying to bring up a huge legacy project with tons of spring wirings, and I am getting this error:
The bizzare thing is this error does not tell me on which xml, which bean id, the error occurs.
I certainly don't expect anybody to point me to where to fix.. My question is, how do spring experts even debug this kind of uninformative error message?
Yes, I understand it must be one of my xml bean was set wrongly. But shouldn't the error message make it easier for me to find out which bean?
Any advice would be extremely appreciated.
00:09:00.640 [main] ERROR com.xxxx.yyyy.zzzzzz.AppMain - Failed to initialize BLAH BLAH BLAH
java.lang.IllegalStateException: No bean class specified on bean definition
at org.springframework.beans.factory.support.AbstractBeanDefinition.getBeanClass(AbstractBeanDefinition.java:381) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(ConstructorResolver.java:154) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireConstructor(AbstractAutowireCapableBeanFactory.java:1035) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:939) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:485) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456) ~[spring-beans-3.1.2.RELEASE.jar:3.1.2.RELEASE]
It's thrown by following piece of code. The init() methods takes in the spring xml files and do its wiring.
try {
AppMain.init(args_);
} catch (Exception e) {
_LOGGER.error("Failed to initialize BLAH BLAH BLAH", e);
System.exit(-1);
}
That's a piece of the sources where it fails
You can try to set a breakpoint there in the Exception throwing. Or add the spring code rather than jar file and try to debug there.
public Class<?> getBeanClass() throws IllegalStateException {
Object beanClassObject = this.beanClass;
if (beanClassObject == null) {
throw new IllegalStateException("No bean class specified on bean definition");
}
if (!(beanClassObject instanceof Class)) {
throw new IllegalStateException(
"Bean class name [" + beanClassObject + "] has not been resolved into an actual Class");
}
return (Class) beanClassObject;
}
My question is, how do spring experts even debug this kind of
uninformative error message?
I'd argue that the error message is very informative. It states that something has tried to put your application in an illegal state. In this particular example, your context was configured in a way where some bean's class is not specified. This is typically caused by forgetting to put a class attribute in a <bean> declaration.
Spring creates BeanDefinition objects for each bean in your context. These can be from your <bean> declarations or other XML elements or Java configuration elements (from #Configuration, #Component, etc.). Almost everything involved in a Spring ApplicationContext is a bean.
You can be pretty sure that Spring's infrastructure beans aren't to blame for this. Spring is an established framework with tons of work put into it and testing done before every release. The immediately obvious alternative is that somewhere in your code you forgot to specify a class. Did you, for example, try to make an abstract <bean> but forget to add the abstract="true"? Did you register a BeanDefinitionParser and create BeanDefinitions without a class? Did you create BeanDefinitionPostProcessor and remove BeanDefinition class values?
Those are the things I would start with.

ClassCastException thrown on Component.getInstance(GateAction.class, true)

I am new in Seam. I am using Rest services and in one of my rest service I am trying to get Instance by Component like,
GateAction gateAction = (GateAction) Component.getInstance(GateAction.class, true);
So with this I got error java.lang.IllegalStateException: No application context active
then to resolve this I call Lifecycle.beginCall() and Lifecycle.endCall() methods as below,
Lifecycle.beginCall();
GateActionIntf gateAction = (GateActionIntf) Component.getInstance(GateActionImpl.class, true);
Lifecycle.endCall();
where GateActionIntf is an Interface and GateActionImpl is seam component which implements GateActionIntf. But now I am getting ClassCastException.
Note : In my web service project I don't have any component.xml and seams based configuration, I have just added jboss-seam jar
Is that possible to get instance like that, without any component.xml and seams configuration? Again just highlighting I am getting the Instance but while type casting throws ClassCastException. how to resolve this?
Give the following a try:
GateActionIntf gateAction = (GateActionImpl) Component.getInstance(GateActionImpl.class, true);

How to bootstrap weld-osgi version 2 in SE application

It's really not funny. There is no information in internet how to run weld-osgi second version (2.1.2.final) in se app. Instructions for ver 1 don't work.
Let the developers be ashamed that they didn't provide necessary samples. I wrote them here.
So, I have and OSGi activator and I want to get beans from it. In GF4 I used this:
private BeanManager getBeanManager() throws NamingException
{
try{
InitialContext initialContext = new InitialContext();
return (BeanManager) initialContext.lookup("java:comp/BeanManager");
}
catch (NamingException e) {
System.out.println("Couldn't get BeanManager through JNDI");
return null;
}
}
But in SE application I can't get it through JNDI.
Also I tried:
Weld weld=new Weld();
BeanManager beanManager=weld.getBeanManager();
But at the second line I get
Caused by: java.lang.IllegalStateException: Singleton is not set. Is
your Thread.currentThread().getContextClassLoader() set correctly?
How can I use CDI starting from activator? What is my mistake?
EDIT:
What I did - I found two source code of two programs that use it, but it's really no so easy to write on their base (at least for me). The first is here and the second is here
The weld-osgi subproject is not supported anymore with Weld 2. Instead, integration with OSGi is provided using the Pax CDI project.
Pax CDI documentation can be found here: https://ops4j1.jira.com/wiki/display/PAXCDI/Pax+CDI
Additional information can be found at:
- http://karaf.apache.org/manual/latest/users-guide/cdi.html
- https://github.com/weld/core/blob/master/examples/osgi/README.md

Categories