Spring does not find WebApplicationInitializer when packaged in a fat Jetty jar - spring

I am trying to create a single "FAT" jar of embedded Jetty and my code. Unfortunatly if I run from the jar I get this error: No Spring WebApplicationInitializer types detected on classpath
If I unpack the jar it works just fine.
The question is can I run the app from the jar or does it have to be unpacked?
Here is a sample project and the steps to reproduce the issue:
git clone https://github.com/steveliles/jetty-embedded-spring-mvc-noxml.git
cd jetty-embedded-spring-mvc-noxml
mvn clean install
cd target
java -jar jetty-noxml-1.0-SNAPSHOT.jar
You will get this output "...No Spring WebApplicationInitializer types detected on classpath...".
but if you do this:
mkdir temp
cd temp
unzip jetty-noxml-1.0-SNAPSHOT.jar
java -cp . com.sjl.Main
This will work just fine.
So what can be done to make this work directly from the jar without unpacking it first?
== Update ==
The author of the project resolved the issue. The following code was needed to pickup webapps from a shaded jar:
parser = new AnnotationParser() {
#Override
public void parse(Resource aDir, ClassNameResolver aResolver) throws Exception {
if (aDir.isDirectory()) {
super.parse(aDir, aResolver);
} else {
super.parse(aDir.getURI(), aResolver);
}
}
};
See: https://github.com/steveliles/jetty-embedded-spring-mvc-noxml/commit/789663310b2fa2bdc0b101658275758a26cec229

Related

Is it possible to send a application.properties file in arguments through a Jar file?

I have a Springboot project and I know I can send a application.properties file as a argument, but is this possible using a jar file?
I built my jar file using maven and in my application I have this piece of code that runs the programm if the user sent the argument run.
Is there any method that allows me to set the application properties if I receive it through argument? Or does the override of the file happens automatically as it does when I use the command
mvn spring-boot:run -Dspring.config.location=your.properties
if (args[0].equals("run")) {
ConfigurableApplicationContext ctx = SpringApplication.run(MigrationsApplication.class, args);
int exitCode = SpringApplication.exit(ctx, () -> 0);
System.exit(exitCode);
}
For jar you can either pass one of the properties or the complete or its location as beow.
we can configure the location directly in the command line:
java -jar app.jar --spring.config.location=file:///Users/home/config/jdbc.properties
We can also pass a folder location where the application will search for the file:
java -jar app.jar --spring.config.name=application,jdbc --spring.config.location=file:///Users/home/config
And, an alternative approach is running the Spring Boot application through the Maven plugin. There, we can use a -D parameter:
mvn spring-boot:run -Dspring.config.location="file:///Users/home/jdbc.properties"

Dependencies put twice into Spring Boot (>2.x) jar when using Gradle

adding Cloud SDK like this for CF:
compile "com.sap.cloud.s4hana:s4hana-all:${cloudSDKVersion}"
compile ("com.sap.cloud.s4hana.cloudplatform:scp-cf:${cloudSDKVersion}")
leads to duplicate jars in spring boot jar which is deployed to CF.
examples:
core-2.3.1.jar
connectivity-2.3.1.jar
This leads to :
ClassNotFoundExceptions during runtime
prevents cf push commands with error:
Comparing local files to remote cache...
Aborting push: File BOOT-INF/lib/core-2.3.1.jar has been modified since the start of push. Validate the correct state of the file and try again.
FAILED
gradle skips the component name when building the boot package.
After some googling around this was the solution:
https://github.com/spring-projects/spring-boot/issues/10778
bootJar {
rootSpec.filesMatching('**/*.jar', { jar ->
String groupId = jar.file.parentFile.parentFile.parentFile.parentFile.name
jar.name = "$groupId-${jar.name}"
})
}

How to pass maven variable to springboot application

I want to pass variable from maven command line to spring boot main class,but i have no idea,here is my maven pom file:
<properties>
<maven.tomcat.home>Here should add argument from command line</maven.tomcat.home>
</properties>
Here is my spring boot main class:
#PropertySource(value ={"file:#maven.tomcat.home#/em/easymobile-application.properties"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
So when i run maven by command line as below ,the application will deploy as war file to tomcat
mvn clean install -Dmaven.tomcat.home=/usr/local/apache-tomcat-8.0.33/webapps
Then i start up tomcat,the error says easymobile-application.properties is not found,so do you know how to load the easymobile-application.properties from maven command line using #PropertySource or something else.Thanks!!
I don't think that's the right approach, assuming at some point you get that to work, what happens if you need / have to deploy the webapp to /usr/local/apache-tomcat-8.0.xx? Would you need to compile the artifact again? That's not good. A better approach is to only produce 1 binary / artifact that could be deployed / installed in every environment: staging / prod / etc..
Like mentioned before, if the app needs files path configuration, I would suggest to use a config properties file instead of a Maven property.
Thanks! I have solved the problem using a simple way ,i just add the path of the folder which contains application.properties in tomcat\bin\catalina.sh
CLASSPATH=:/usr/local/apache-tomcat-8.0.33
the #PropertySource points to "classpath:em/easymobile-application.properties"

Unable to run repacked spring boot jar caused by "Unable to open nested entry"

I am setting up a build pipeline for a spring boot project.
It has three stages so far:
build: compile-->unit test-->archive the jar
deploy acceptance test: repack the jar for acc environment (replacing datasource.properties etc)
deploy uat test: repack the jar for uat environment (replacing datasource.properties etc)
I don't want to build the jar from scratch for different environments as it wastes time and potentially has risk of building inconsistent artifacts.
For traditional war project, I just extract the war, replace the config files and repack. But this time with spring boot, somehow it does not work. When I run the repacked jar, it reports
java.lang.IllegalStateException: Unable to open nested entry 'lib/antlr-2.7.7.jar'. It has been compressed and nested jar files must be stored without compression. Please check the mechanism used to create your executable jar file
at org.springframework.boot.loader.jar.JarFile.createJarFileFromFileEntry(JarFile.java:378)
at org.springframework.boot.loader.jar.JarFile.createJarFileFromEntry(JarFile.java:355)
at org.springframework.boot.loader.jar.JarFile.getNestedJarFile(JarFile.java:341)
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchive(JarFileArchive.java:108)
at org.springframework.boot.loader.archive.JarFileArchive.getNestedArchives(JarFileArchive.java:92)
at org.springframework.boot.loader.ExecutableArchiveLauncher.getClassPathArchives(ExecutableArchiveLauncher.java:68)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:60)
at org.springframework.boot.loader.JarLauncher.main(JarLauncher.java:45)
I extracted the origin jar and the repacked jar and don't find differences with lib folder.
task extractArtifact() {
doLast {
def outputDirName = "${buildDir}/tmp/under_config"
def outputDir = file(outputDirName)
assert outputDir.deleteDir() // cleanup workspace
def zipFile = file("${buildDir}/libs/${getArtifactName()}")
copy {
from zipTree(zipFile)
into outputDir
}
copy {
from file("${buildDir}/env")
into file("${buildDir}/tmp/under_config")
}
}
}
task repackConfiguredArtifact(type: Zip, dependsOn: extractArtifact) {
archiveName = "${getArtifactName()}"
destinationDir = file("${buildDir}/libs/${getEnv()}")
from file("${buildDir}/tmp/under_config")
}
Does anyone have an idea?
Or how do you guys config the jar for different environment (without re-compile the binary).
You shoud add -0 to store only; use no ZIP compression
$jar -cvf0m yourproject.jar META-INF/MANIFEST.MF .
There is another soulution:
Set the active Spring profiles
$java -jar -Dspring.profiles.active=production demo-0.0.1-SNAPSHOT.jar
You can use application-${profile}.properties to specify profile-specific values.
I have a solution after looking up the spring-boot reference.
turn default spring boot repackage off since I need to repack it anyway.
extract the traditional jar and copy the config files
Use jar type task to repack it
Use BootRepackage type task to assemble a spring-boot jar.
here is the code:
bootRepackage {
enabled = false
}
task extractArtifact() {
doLast {
def outputDirName = "${buildDir}/tmp/under_config"
def outputDir = file(outputDirName)
assert outputDir.deleteDir() // cleanup workspace
def zipFile = file("${buildDir}/libs/${getArtifactName()}")
copy {
from zipTree(zipFile)
into outputDir
}
copy {
from file("${buildDir}/env")
into file("${buildDir}/tmp/under_config")
}
assert zipFile.delete()
}
}
task clientJar(type: Jar, dependsOn: extractArtifact) {
archiveName = "${getArtifactName()}"
from file("${buildDir}/tmp/under_config")
}
task repackConfiguredArtifact(type: BootRepackage, dependsOn: clientJar) {
withJarTask = clientJar
}
I found that if you had a directory called 'resources/lib' the spring-boot executable JAR would assume the contents were zipped and throw the above exception. Renaming to 'resources/static' worked for me.

Resource injection in Spring Boot not working with gradle

I have a Configuration bean.
#Component
public class Config {
#Value("classpath:${app.file.name.srgbicc}")
public Resource icc;
#PostConstruct
void init(){
try {
tempdir = Files.createTempDir();
newIcc = copyIccFileToTemp();
}catch (IOException e){
throw new RuntimeException(e);
}
}
private File copyIccFileToTemp() throws IOException {
File tempIcc = new File(tempdir, icc.getFilename());
FileUtils.copyFile(icc.getFile(),tempIcc);
return tempIcc;
}
}
On icc.getFile() there is a FileNotFoundException
application.properties
app.file.name.srgbicc = sRGB.icc
I looked in my classpath and found the following situation:
build/classes/main (no icc file)
build/resources/main (icc file, application.properties)
When printed out my classpath during application start I only found ...myApp/build/classes/main.
No ../build/resources/main or ../src/main/resources entries there.
So I was wondering why is the resources not on the classpath?
according to http://docs.spring.io/spring-boot/docs/1.1.5.RELEASE/reference/htmlsingle/#build-tool-plugins-gradle-running-applications
this should be.
Of course if I put the icc file in build/classes/main its all working as expected, but it is not supposed to be there. right?
I tried to run the application with gradle run or gradle bootRun in intellij I use static main. The good thing is that the application fails consistently independent of how I start it.
My Project layout
myApp
src
main
java
pkg
Config.java
resources
sRGB.icc
application.properties
For the reference:
IntelliJ 13
spring-boot 1.1.5
Fgradle 2.0
Update
Here is a stripped down example of my code
https://gist.github.com/Vad1mo/bb5d23ca251341236fbd
I removed #PostConstruct in the config.init and run the application with gradle build all test are success when i rund from intellij the tests fail. this is strange that i know have different behavior
I solved the problem now.
What helped me was just to do a simple clean build and rebuild project in intellij. I was using to much eclipse and maven so I expected it to happen automagically.

Resources