How can I add a custom loader to the executable jar?
At the moment i'm doing it manually by opening the jar file, pasting the class and re-signing it the end. -.-
Thanks in advance,
Pedro Silva
Executable jar restrictions
There are 2 types of restrictions that you need to consider when working with a Spring Boot Loader packaged application.
Zip entry compression
The ZipEntry for a nested jar must be saved using the ZipEntry.STORED method. This is required so that we can seek directly to individual content within the nested jar. The content of the nested jar file itself can still be compressed, as can any other entries in the outer jar.
System ClassLoader
Launched applications should use Thread.getContextClassLoader() when loading classes (most libraries and frameworks will do this by default). Trying to load nested jar classes via ClassLoader.getSystemClassLoader() will fail. Please be aware that java.util.Logging always uses the system classloader, for this reason you should consider a different logging implementation.
Alternative single jar solutions
If the above restrictions mean that you cannot use Spring Boot Loader the following alternatives could be considered:
Maven Shade Plugin
JarClassLoader
OneJar
Resource Link:
The executable jar format
spring boot loading jars (application dependencies and external file system jars)
Related
I'm trying to set a custom log Handler in my Spring Boot (version 2.6.3) application. The result is a ClassNotFound as described in this other question
Can't override java.util.logging.LogManager in a Spring Boot web application: Getting java.lang.ClassNotFoundException on already loaded class
Based on the answer to that question, it seems I need my Handler and all its dependencies to be placed into the root of the executable jar.
Is there a direct way to accomplish this during the Maven build, i.e. not by extracting and repackaging the jar myself post-build?
This issue is a result of BOOT-INF fat jar structure introduced by Spring Boot 1.4.
There is currently no straightforward solution, and it appears some of the Spring Boot maintainers do not agree there is a problem, so it could be a long time before the situation changes:
Issue #6626: Make it easier to package certain content in the root of a fat jar
Issue #12659: Starting executable war with -Djava.util.logging.manager=org.apache.logging.log4j.jul.LogManager produces a ClassNotFoundException
WORKAROUND #1
I had to do two things to get my application working again with a custom log handler. 1) use Maven Shade to package up the log handler with all its dependencies, and 2) launch the app with using the PropertiesLauncher class in the command line instead of using java -jar:
java -cp executable.jar:logger-shaded.jar -Dloader.main=mypackage.myapp org.springframework.boot.loader.PropertiesLauncher
The executable.jar, logger-shaded.jar, and mypackage.myapp are placeholders specific to my project, so adjust accordingly.
WORKAROUND #2
If the handler is loaded from code in a config class or from main() instead of being specified in the file loaded via java.util.logging.config.file, as discussed in the comments to the answer in this other question, then everything works as expected. I actually prefer this over Workaround #1 as it results in a smaller deployment, but it does require writing a few more lines of code.
I have multimodule maven application. Inside the main directory where parent pom resides I have some modules. I am in the process of adding another one on the same level as the other modules. The application is packages as EAR and deployed into Websphere, this EAR is configured to use shared library which basically is a directory containing jars. In this shared library Apache Crimson resides which is used by some of the modules. I am adding the new module based on Spring Framework 4.0.0.RELEASE. The whole project is a legacy and uses Java 6 and Hibernate 2.1 :( . The problem is that this Crimson make my new module crashing during deployment:
BeanDefinitionStoreException: Parser configuration exception parsing
XML from ServletContext resource [/WEB-INF/spring/root-context.xml];
nested exception is javax.xml.parsers.ParserConfigurationException:
Unable to validate using XSD: Your JAXP provider
[org.apache.crimson.jaxp.DocumentBuilderFactoryImpl#706b4db8] does not
support XML Schema. Are you running on Java 1.4 with Apache Crimson?
Upgrade to Apache Xerces (or Java 1.5) for full XSD support.
So I added to my new module this:
`<dependency>
<groupId>xerces</groupId>
<artifactId>xercesImpl</artifactId>
<version>2.6.2</version>
</dependency`
But application still takes crimson.jar from the shared library. I tried to exclude this crimson.jar from parent pom but still it is taken from shared library.
Crimson is required by some other modules being packaged as jars hence the crimson is present ar EAR/lib level.
Another try was to change classloader order to take jars from the new module first and then go upward in the hierarchy (my new war module then parent ear). But this gave me some other exception.
The question is - how being in WAR module inside EAR module not take the jar from the ear level (it was also included in EAR/lib) and take from the WAR level only?
If parent-last class loading in the WAR doesn't work (perhaps due to something else in the WAR that can't be safely run with that delegation pattern), then I'd recommend an isolated shared library associated with the WAR. In the WAS administrative console, create a shared library with a class path containing the parser jar(s) you need, select the "use an isolated class loader for this shared library" option, then associated that shared library with the web module that needs it.
Isolated shared libraries utilize parent-last class loading, but only for the jars in the library rather than for the entire EAR or WAR. Because of that, you can target it to only the jars that you really NEED to use that type of delegation. The isolated library should allow you to use Xerces in the WAR while still using Crimson in the EAR.
Can anyone explain how Spring decides where to look for resources when one uses the ResourceLoader.getResource(...) method?
I am having a problem with a multi-module maven application built using Spring Boot whereby in my integration tests my code is able to find resources using resourceLoader.getResource("templates/") or even resourceLoader.getResource("classpath:templates/"). So far so good...
However, when the module is eventually packaged into the executable JAR and run with embedded Tomcat the resources can no longer be resolved. I also tried resourceLoader.getResource("classpath*:templates/") with no success.
What I find concerning is that when I add a logging statement to output the URL being used in the search i get a path to one of the other modules in the project (not the one that actually contains the resource in question). E.g: jar:file:/Users/david/exmaple/target/spring-boot-0.0.1-SNAPSHOT.jar!/lib/module1-0.0.1-SNAPSHOT.jar!/templates/ whereas I believe the resource is in jar:file:/Users/david/exmaple/target/spring-boot-0.0.1-SNAPSHOT.jar!/lib/module2-0.0.1-SNAPSHOT.jar!/templates/
The resource loader was obtained from an Autowired constructor param.
Thanks in advance for any hints.
Edit
Just in case it isn't clear or is of importance, my integration tests for the module in question aren't aware of the other module. I have module1, module2 and a spring-boot module which has dependencies on module1 & module2. Essentially, when I run the integration tests for module 2 the classpath isn't aware of module1 - so I suspect that this has something to do with why it works in the tests.
When you use classpath: or classpath*: prefix, internally, this essentially happens via a ClassLoader.getResources(…) call in spring.
The wildcard classpath relies on the getResources() method of the underlying classloader. As most application servers nowadays supply their own classloader implementation, the behavior might differ especially when dealing with jar files. A simple test to check if classpath* works is to use the classloader to load a file from within a jar on the classpath: getClass().getClassLoader().getResources("<someFileInsideTheJar>"). Try this test with files that have the same name but are placed inside two different locations. In case an inappropriate result is returned, check the application server documentation for settings that might affect the classloader behavior.
Do not use classpath: form as you have multiple classloader locations of templates/ .
Refer to: resources-classpath-wildcards
I am using activejdbc 1.4.9. I created one jar (using maven) which has two ActiveJDBC Model Classes. I added the jar to the application. Application has three more model classes. When I compile and try to run the application (gradle based), activejdbc is instrumenting only 3 classes which are in application but not instrumenting the classes which are in jar. When I try to write the data into the two models which are in jar, It is throwing exception as
org.javalite.activejdbc.DBException: Failed to retrieve metadata from DB. Are you sure table exists in DB ?
Now I have certain doubts. Please help me to resolve and understand few things.
How instrumentation happens ?
When we create a jar, will it include instrumented classes ?
Why it is throwing this error ?
It is throwing this error in case classes have not been instrumented. This means that before placing your model classes into a jar file, you need to instrument them. Does not matter which build method you use though. This http://javalite.io/instrumentation explains what is instrumentation and how to do it. Instrumentation does not create jars, it merely adds some byte code into your classes. In all scenarios you need:
Write code :)
Compile
Instrument
after this, you can do any of the following:
run app using class files in the file system
package class files into jar file and use that on your classpath
package jar file into a larger app (WAR, EAR, RAR, etc.) and deploy your app
making sense?
I dont understand the information provided in the README file for spring-boot-loader
https://github.com/spring-projects/spring-boot/tree/master/spring-boot-tools/spring-boot-loader
what exactly is the purpose of spring boot loader other than it being used internally by spring boot to create the embedded server jar files. Can we tap into this process and load additional jars from filepath to be incldued in the classpath
There are 3 launcher classes (JarLauncher, WarLauncher and PropertiesLauncher). Their purpose is to load resources (.class files etc.) from nested JAR files or JAR files in directories (as opposed to explicitly on the classpath). So, yes, you can tap into that mechanism. In the case of the [Jar|War]Launcher the nested paths are fixed (lib/.jar and lib-provided/.jar for the WAR case) so you just add extra JARs in those locations if you want more. The PropertiesLauncher looks in lib/ by default, but you can add additional locations by setting an environment variable LOADER_PATHor loader.path in application.properties (colon-separated list of directories).