Spring Boot fastest way to deploy instead of build Jar? - spring-boot

I am new to Spring Boot from php world. In Php development, it is simple to make changes on the file, upload and run.
But on Spring boot, my development relies on remote ubuntu server, every time I make change in *.java, I have to build the Fat Jar, upload the Jar, kill the current java process on ubuntu, and run the java -jar my.jar again, which spend much time on the upload because the Jar is about 60 mb.
Is there any way I can work like php, just upload the changed file, so the spring boot just compile the class and run?
Does change to build *.war help to faster deployment?

There are a few option to mitigate the roundtrip of building the jar file and upload it.
Hot-swap: For minor changes, you can hot-swap changes automatially when you have a remote-debugger attached. I use Intellij as Ide, which provide this out of the box after a file is recompiled, see more at this link how to enable it.
Reloading tool: use a tool that are designed to reload Java classes, such as JRebel which extends the classloader and updates a class if a change has been detected. However, they are often only available in a paid version.
Spring Boot dev-tools: this tool also monitors changes and restart the application with the new changes (so no need to rebuild the jar file). It is possible to use on a remote application. See this link for more info.
Using a war file is different concept since a war file is executed inside an application container (e.g a Wildfly server). You can dynamically upload a war file to a running application server, which will only restart the war file. But I'm not sure if this will lead to faster deployment, however it is a different approach how the application is run.

Related

Can you replace a Spring Boot jar while the application is running?

In our Linux environment applications are restarted periodically (the reasons aren't important here). It would be convenient for us to deploy new versions of an application by copying the application Spring Boot jar on top of the existing (old) jar thereby overwriting it and then simply wait for the application to restart (that is, the JVM running the application to restart).
However, this seems to not work. We get different kinds of errors - sometimes the app just hangs, sometimes we get a ClassNotFoundException. It's as if Spring Boot (or something inside Spring Boot) reopens the jar and expects it to be the same one it was when the application was originally started.
We had a look through Spring's common application properties, but didn't see anything appropriate. Is there a way to make this work? When we were using WAR files we configured the servlet container to unpack the WAR file and run from the unpacked version. Can we do something similar with Spring Boot?
First of all the errors you are experiencing can come from multiple sources. Usually replacing the file from a process is not a big problem as the whole file is loaded into memory before execution. Java is a little bit different, because the actual process that is running is the JVM and it only loads the jar file from disk. The JVM loads classes only on demand, this means if there was any class that was not loaded before it will try to load it and most likely fail, if the jar file is different. In the case of spring boot there are also other resources (such as HTML files) inside the jar file that are dynamically loaded.
You mentioned you are using a linux environment. If you can just replace your startup with a script you can just copy the jar and start it from the copied location:
#!/bin/bash
JAR_NAME="spring-boot.jar"
NEW_JAR_NAME=".$JAR_NAME" # Use an appropriate name here
cp $JAR_NAME $NEW_JAR_NAME
java -jar $NEW_JAR_NAME
rm $NEW_JAR_NAME
Now every time you start the application a copy is being made and started from there. You can replace the original jar and on the next restart the new application will load.
You coud also use rsync instead of cp to avoid copying the same jar twice, if the application is restarted multiple times without changing the jar.
It would be convenient for us to deploy new versions of an application
by copying the Spring Boot jar on top of the existing (old) jar and
then simply wait for the application to restart
Why would you do such a thing to yourself? You are trying solve a usecase that's against best practices, sound like asking for trouble just to avoid an app restart. When you are doing a deployment, you need to make sure the deployment went through, otherwise how will you troubleshoot if something goes wrong in your application, you will have one more variable in hand when you troubleshoot, i,e the uncertainty of current version of the code.
If you are having downtime while deploying (I am assuming thats why you want to limit the restarts), why don't you bring up another instance with the newer version of code and once its healthy shutdown the old one

How to load a WAR module into Spring's built-in Tomcat running in a standalone?

I am having three modules in my Maven project:
parent
rest-api # Spring REST API
web-client # AngularJS web client
application # Project to bundle it all up for a standalone
I am not sure if I have here an "elegant" solution so please hit me with a stick if that is complete garbage, but this is how it works - or how it's supposed to work:
rest-api
Module rest-api does simply offer the REST API and other core functionality - basically it is pure server code. It is a jar artifact.
web-client
To separate client and server code I am having the module web-client. It is a war project that hold the client webapp.
application
This module is supposed to glue it all up. It depends on rest-api and web-client. It does two important things:
It's pom.xml uses the spring-boot-maven-plugin to build a standalone runnable jar - my ultimate goal
It provides the main(String args[]) method that starts the #SpringBootApplication with SpringApplication.run(EasyModelAccessServer.class, args);
What I am currently able to is tell Eclipse to run this in a servlet container. The server boots up and I my two resources, the rest-api and the web-client working. Everything is fine so far.
The issue
What is not fully working is the standalone. If I package up the whole thing and run the server:
$ path/to/application: mvn clean package
$ path/to/application: java -jar target/application.jar
Only the REST API will work. The reason is because the web-client is not added or introduced as a web app to the Spring built-in Tomcat.
The question
is how I can make this work. There are two options which come to my mind:
Somehow sneak in the web-client.war file into the application.jar such that it is available as a resource and programmatically call tomcat.addWebapp("/web-client", "path/to/web-client.war") (or something like that) to load the additional service
Hope that the spring-boot-maven-plugin or another Spring Maven plug-in can do that for me and find somebody that links me to an example.
I've tried it with 1. but I didn't succeed to move web-client.war into application.jar but I am also not sure if I should actually do that.
FAQ
Q: "Why do you separate everything instead of merge all those modules into a server module where the Spring Maven plug-in would do everything for you out of the box?"
A: I really want to separate the client code from the server code. I could however merge web-client into application but last time I tried that I had 10 other issues why this did not work out so I decided to keep it that way and that it actually shouldn't be so hard to load an additional server resource.
Q: "Can I take a look at the project?"
A: Yes, you can. Just take a look: https://github.com/silentsnooc/easy-model-access Please forgive me that I am currently using tabs instead of whitespaces - I am going to change that as soon as I got everything up and running.

How can I include a resource file (logback.xml) in a project when building JAR but not when embedded in a WAR?

I'm working on a project that uses two maven projects (named core and webapp); core is built with JAR packaging and used for two different purposes: as a stand-alone app (essentially an executable JAR), and also embedded into webapp.
For its purpose as a stand-alone app, core needs to have its own logback configuration (a logback.xml file) that needs to be included on the classpath. Normal Maven convention would have me put it in src/main/resources/logback.xml. That works fine, but causes a problem when the core JAR is included in webapp. webapp needs to have its own logback configuration, but the container (tc Server or Jetty) is picking up the one from core.jar first.
I realize that logback can be told about a custom config location via a system property (-D on the command line) but that's not viable in a app container like Tomcat or Jetty.
I've read some other people asking about this situation, but none of the solutions I've seen sits well with me. One solution involved setting up a context listener that runs early in the webapp initialization and explicitly configures logback based on a <context-param>. That's a bit brutish in my opinion, and probably a hard sell to my fellow dev team when log4j "just works" in this situation.
I'm far from a Maven expert, so I'm hoping there is some elegant way to get Maven to help me here. Or perhaps some logback extension or add-on that makes it more web-app friendly. Or even a clever idea that I haven't thought of.
There are a number of possible solutions, but the easiest is to put the file in its own module and mark the dependency as provided. The, conspire to have it on the classpath when running the standalone version of the app.
The solution that we ended up using was to leave only the common "non-app" pieces (code and configuration) in core and then extract the other "app" pieces into a new module (batch-app).
The logging configuration only lives in the 2 app projects (webapp and batch-app) that depend on core. core has a logback-test.xml configuration in it, but that's excluded from the JAR that maven builds (since it's in the src/test/resources folder).

How to run web applications using jersey in an easy way?

I created a web application using Jersey through this maven code:
mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-webapp \
-DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false \
-DgroupId=com.example -DartifactId=simple-service-webapp -Dpackage=com.example \
-DarchetypeVersion=2.4.1
And I am using Tomcat v7 as my Java server. When I finish writing some code, I use mvn's package command to generate a .war file, copy this file to the /webapps folder and then start tomcat to run my application and test it on browser. But I think I waste lots of time doing these things. So I want to ask if there is an easier way test my code on browsers. How do you guys run your web applications, especially Jersey app, on your server?
And I am using Intellij Idea, does it have some features that help me build and run Jersey apps, or other J2EE apps? how to use them?
In IntelliJ IDEA you can create a Tomcat Run/Debug configuration. In that configuration you can specify "before launch" tasks/options, including running a maven goal. So by running the Tomcat configuration, IDEA will run the maven goal, deploy your code to Tomcat, start the tomcat server, and (if desired) open you web browser to a specified page.
JetBrains has a Getting Started with Spring MVC, Hibernate and JSON tutorial. What you want to do is very similar. The main difference is you will need to remove the default "make" option in the "Before Launch" section at the bottom of the run/debug configuration and instead have it run your maven task.
There is also the Creating a simple Web application and deploying it to Tomcat tutorial. It's a little older and some of the options on the run/debug dialog have changed. But at the core, its still valid. Combined with the above, you should be in good shape.
Finally take a look at the Run/Debug Configuration: Tomcat page in the help guide (also available in the online webhelp).

java.lang.NoClassDefFoundError: org/aspectj/weaver/reflect/ReflectionWorld$ReflectionWorldException

I am using aspectJ in my project and added dependencies in my POM file. When i am running my application on Websphere Application Server Liberty Profile, in the library folder aspectj.jar is not getting added/created. I am very new to using spring and never used server's to run a application. When i am trying to run the application on the server i am getting the following exception:
Caused by: java.lang.ClassNotFoundException: org.aspectj.weaver.reflect.ReflectionWorld$ReflectionWorldException
Can anyone please team whats going on wrong with the application ?
Thanks!
You'll need to make sure that aspectj is available to your application at runtime. The two basic approaches for this are to package it up with your application zip, or to make it available as a shared library in the server. The first approach has the advantage that you don't need to do any extra config, and no matter where you run your application, the dependency will be there. However, it has the disadvantage of bloating your application. If you end up running multiple applications on the server, it could also cause the apps to use more memory than they would if they were just using a shared copy.
For the first approach, if your dependency has the default scope in the pom, maven should automatically copy it to WEB-INF/lib (assuming your application is a war).
For the second approach, you can configure it in Liberty as a global library (available to all applications) by copying it to a wlp/usr/shared/config/lib/global folder in your Liberty install.

Resources