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).
Related
The project artifact structure is:
test.ear
--lib - has all log4j2 jars core, web, bridge and JCL
--META-INF -- has app.xml, JBoss specific deployment XML, and manifest file
--a.war -- each war has web.xml with log4jConfiguration and Log4jServletContextListener specified.
--b.war
--c.war
The war creates context with log4jConfiguration, which is "classpath:test.xml". I can see each war creating the context with log4jContextName I have provided in web.xml.I have a few crons, too, which run based on configured time intervals. When crons run, and many of the JMS process run, I observed the log files are not populated with logs(From project-specific classes). While going through the log4j2 code, I understood that log4j2 creates a context for each classloader. And in my case, it creates a context for "test.ear", which is defaulted to error(DefaultContext) as it is not able to find a default log4j2.xml since I have a custom named(test.xml) on in the classpath. The Log4jServletContextListener does not catch the "test.ear" event.
How to inject my "classpath:test.xml" while log4j2 creates a context for the ear file? Since my project can be both deployed to WebSphere and JBoss, I am looking forward to suggestions that are not server-specific. Or is there a way to create a single context for all the war and ear somehow? I have different apps outside this ear in same server so I cannot give an environmental config of -Dlog4j.configurationFile as other apps have there own log4j2 xml's.
One approach that I can think of is to place your log4j config file test.xml in a shared library and configure your application to use the shared library.
The following link describes how to configure a shared library for a server or an enterprise application on WebSphere.
https://www.ibm.com/support/pages/how-create-shared-library-and-associate-it-application-server-or-enterprise-application-websphere-application-server
The shared library should be a generic function for modern Java app servers, so JBoss should also be able to configure this.
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)
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
My maven top level project refers to a common-db project. In this project I have a spring file which defines the DB parameters.
However, I want the top-level project to define the DB parameters through the profile and inject these into the spring config file in /src/main/resources.
The top-level project only does the filtering on its own /src/main/resources files and ignores those located in the JAR dependencies.
How can I do this?
So you want to depend on common-db but then modify its contents to change the parameters in the config file? Ok, if you really want to do that, you could do something convoluted where you use dependency:unpack to expand the common-db jar, then overwrite / filter its contents, and then use a custom jar:jar execution to re-jar up the dependency and ship it with your application.
But, wow - why would you jump through all these hoops? Like #hoaz suggested, just place your application-specific config in the same classpath location so that it is loaded before common-db's default configuration. This is the convention followed by many, many Java libraries.
I have some questions derived from a problem that I have already solved through this other question. However, I am still wondering about the root cause. My questions are as follows:
What is the purpose of spring.handlers and spring.schemas?
As I understand it's a way of telling the Spring Framework where to locate the xsd so that everything is wired and loaded correctly. But...
Under what circumstances should I have those two files under the META-INF folder?
In my other question linked above, does anybody know why I had to add the maven-shade-plugin to create those two files (based on all my dependencies) under META-INF? In other words, what was the ROOT CAUSE that made me have to use the maven shade plugin?
What is the purpose of spring.handlers and spring.schemas?
well you more or less found it out by yourself, let's add some more details:
some spring libraries contain a spring.schemas and a spring.handlers file inside a META-INF directory
META-INF/spring.schemas
re-maps(*) schemalocation to a xsd inside the library
(abstract) only re-mapped versions are supported by this library
META-INF/spring.handlers
provides namespace handler classes for specific namespaces
the namespace handler class provides the parser logic to parse spring-batch beans, like job,
step, etc.
(*) the actual re-mapping happens during the build of the spring application context
Under what circumstances should I have those two files under the
META-INF folder?
normally the files are inside the spring library jars you use, but you can use the mechanism to implement own namespace bean parsing, then you would have own files
In my other question linked above, does anybody know why I had to add
the maven-shade-plugin to create those two files (based on all my
dependencies) under META-INF? In other words, what was the ROOT CAUSE
that made me have to use the maven shade plugin?
if you use a spring namespace in your spring configuration, you need the appropriate files
the problem arises when you want to run a java application:
with a main class either
the spring libraries need to be on the classpath
or all is merged into one jar, which has to be on the classpath (*)
as war/ear server application, the spring libaries need to be on the classpath, normally inside the war
i guess you did not start the mainclass with the complete classpath and i updated my answer for your first question too
(*) if you merge all into one jar, you have to make sure, that the contents of all spring.schemas/spring.handlers files are merged into one spring.schemas and one spring.handlers file, see this answer for a configuration with maven to create an all-in-one.jar