Start spring-boot application with classLoader - java

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

Related

ClassNotFoundException in Java Agent Instrumentation premain

I read https://www.baeldung.com/java-instrumentation and I'm trying to instrument the doService method in org.springframework.web.servlet.DispatcherServlet, which is part of the Spring Boot framework, i.e. spring-webmvc.jar.
This is the way I start my spring boot application:
java -javaagent:agent.jar -jar myapp.jar
I understand my agent's pom.xml should package/include the javassist dependency but I don't expect the agent should package/include the jar spring-webmvc.jar containing the class org.springframework.web.servlet.DispatcherServlet, as it is already packaged and contained in myapp.jar
In the premain of the agent, when I try to instrument and load the class org.springframework.web.servlet.DispatcherServlet by using forName, it throws the ClassNotFoundException.
How should I instruct the agent to load it from the myapp.jar ?
Thank you!
try {
targetCls = Class.forName(className);
targetClassLoader = targetCls.getClassLoader();
LOGGER.log(Level.INFO, "Successfully obtain target class {0} and its classloader using 'forName' with class loader {1}", new Object[] {className, targetClassLoader.toString()});
} catch (Exception ex) {
LOGGER.log(Level.SEVERE, "Class {0} not found with Class.forName. {1}", new Object[] {className, ex.getClass().getCanonicalName()});
}
If you are new to Java instrumentation and following this post, note that manually invoking forName is not necessary in the example. What you can do is to register the transformer in premain and then in transform method, check for your target class name and start the instrumentation as instructed in the link. Happy coding!

Splitting resources.groovy (Grails 2.5) to make it modular

resources.groovy of my Grails project is growing, so I am thinking of splitting it in some logical files that will be easier to mantain. After reading some questions, blogs, i got to know about importBeans or loadBeans. This works well only when application is run using grails run-app. However we I create a war and deploy it on JBoss, the custom bean files are not accessible.
Also tried below with no luck - mentioned here -
Load spring beans from custom groovy files in grails app
grails.war.resources = { stagingDir, args ->
copy(todir: "${stagingDir}/WEB-INF/classes/spring") {
fileset(dir:"grails-app/conf/spring") {
include(name: "datasourceDefinitions.groovy")
exclude(name: "resources.groovy")
}
}
}
I have added datasourceDefinitions.groovy in grails-app/conf/spring.
Please help.
The problem is due to the Spring bean configuration files are moved into the folder "WEB-INF/classes/spring/"(make sure the file is packaged in the.war) inside the WAR file. As what I did was locate the resource path in resources.groovy.
def loadFromFile = { name ->
importBeans("file:grails-app/conf/spring/"+name)
}
def loadFromWar = { name ->
def resource = application.parentContext.getResource("WEB-INF/classes/spring/"+name)
loadBeans(resource)
}
def loadResource = application.isWarDeployed() ? loadFromWar : loadFromFile
loadResource "datasourceDefinitions.groovy"
loadResource "anotherBean.groovy"

How can I confirm that ClassPathXmlApplicationContext finds a file?

We have an app that does not run in a web container so I am trying to start Spring up. In the "main" function we have this:
public static void main(String[] args) throws InterruptedException, IOException, AlreadyAliveException, InvalidTopologyException, AuthorizationException {
// starting up spring...
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:/applicationContext.xml");
DataSourceTransactionManager dstm = applicationContext.getBean("markiscool", DataSourceTransactionManager.class);
dstm.toString();
}
And my applicationContext.xml contains:
<bean id="markiscool" class="blah.entities.LocationEntity" />
The app, on startup, logs this:
[INFO] ClassPathXmlApplicationContext - Refreshing
org.springframework.context.support.ClassPathXmlApplicationContext#295cd6e5:
startup date
[Thu Dec 17 10:28:28 CST 2015]; root of context hierarchy
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'markiscool' is defined
I have tried putting garbage in the xml file but it doesn't fail so it must not be finding the file. Also, the file is on the classpath:
I have also stuck the file in about every place I can think of. It does not load. Help!
Try to use FileSystemXmlApplicationContext:
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("/conf/applicationContext.xml");
Instead of:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:/applicationContext.xml");
Try this:
Right click -> properties -> Deployment Assembly
Put an entry: conf -> /
try
new ClassPathXmlApplicationContext("applicationContext.xml");
1) Put applicationContext.xml into the config folder
2) Tell Maven to include applicationContext.xml in the jar it creates
3) Use the function ClassPathXmlApplicationContext to pull in the file
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath*:conf/applicationContext.xml");
Note: FileSystemXmlApplicationContext will not work if you are trying to access a file inside of a jar. For this reason I recommend using ClassPathXmlApplicationContext because it works whether you are running the project in eclipse or via the jar

Spring: How can I debug a BeanCreationNotAllowedException?

I am trying to add a new Spring bean to one of my projects. The bean is defined and created in another package like so:
#Configuration
public class UtilityBeans {
public static String MY_BEAN_NAME = "my.bean.name";
#Bean(name = MY_BEAN_NAME)
public MyUtilBeanClass getMyBeanClass() {
return new MyUtilBeanClass();
}
}
I use it in my other package like this:
#Configuration
#Import({
UtilityBeans.class
)}
...
#Resource(name = UtilityBeans.MY_BEAN_NAME)
private MyUtilBeanClass myUtilBeans;
During runtime I get:
ERROR
Caused by: org.springframework.beans.factory.BeanCreationNotAllowedException: Error creating bean with name 'my.bean.name': Singleton bean creation not allowed while the singletons of this factory are in destruction (Do not request a bean from a BeanFactory in a destroy method implementation!)
...
The logs do not give me any useful information as the stack trace is all in Spring library. How can I find out what failed? Is it incorrect usage of resource or is it that the bean creation itself is wrong?
I am using Spring-4 with JDK8.
The issue here was that the bean was being created in 2 different points in my spring configuration due to some refactoring and the fix was to remove duplicate code. I had the same bean creation code:
#Bean(name = MY_BEAN_NAME)
public MyUtilBeanClass getMyBeanClass() {
return new MyUtilBeanClass();
}
... in another class I had half way refactored.
In this case my mistake was that I did not grep across all the log files being generated. The exceptions were being split and then buried into 2 different logs, one for a server start up and one for application runtime. The above exception was being posted to the application log. The other logs contained the relevant exception which stated that duplicate bean creation failed and the fix was to remove duplicate code.

AspectJExpressionPointcut uses wrong classLoader

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.

Categories