How to convert a .war file into a JAR file? - java

I did a project based on J2EE framework. Using spring/ hibernate technologies. And used Tomcat as my servlet container. I want to create a JAR file and make my web project executable. This question is asked before but answers are ambiguous.

Have a look at Spring Boot, this will help you create a jar that can be started with
java -jar <your-jar-file>
It would not need to get deployed into a servlet container, instead it will run with an embedded tomcat. You'd need to install a java runtime wherever you want to run it, though.
See this link for more information.

To convert war file to jar in the spring boot, you need to do 3 steps.
convert war to jar in the pom.xml
<packaging>jar</packaging>
extends SpringBootServletInitializer
override configure
public class Your_Application_Name extends SpringBootServletInitializer{
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder springApplicationBuilder){
return springApplicationBuilder.sources(Your_Application_Name.class);
}
public static void main(String[] args) {
SpringApplication.run(Your_Application_Name.class, args);
}

Related

How to add external JARs to Spring application without restarting JVM?

I have a Spring Boot application which copies external JAR files to a folder, depending on certain conditions. These JARs can contain many Spring components (i.e. classes annotated or meta-annotated with #Component) and the Spring application should be able scan and instantiate for these beans. Is it possible, based on certain conditions, to dynamically load the contents of the JAR files and make them available to the Spring application context? I am fully aware of the security implications this has.
I have read about the different types of Launchers which Spring provides for its executable JAR format, such as JarLauncher and PropertiesLauncher, but it looks like that these launchers do not detect changes to the classpath, but instead only scan the directories once for JAR files.
The following simple application demonstrates the problem:
// .../Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
SpringApplication.run(Application.class, args);
}
}
Replace the default JarLauncher with PropertiesLauncher:
// build.gradle
tasks.named('bootJar') {
manifest {
attributes 'Main-Class': 'org.springframework.boot.loader.PropertiesLauncher',
'Start-Class': 'com.example.customlauncher.Application'
}
}
Specify the location to the external JARs in the properties file of the PropertiesLauncher:
# .../resources/loader.properties
loader.path=file:/path/to/dir
The application is a Spring Initializer Gradle application and packaged by running the bootJar task: ./gradlew bootJar.
It is then started with the following command:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
This works if the JAR file is already present at the specified location (/path/to/dir), but it does not work if the java command is executed while the directory is empty and the JAR file is then copied while the app waits for the user to copy the files and press Enter ↲.
There are a couple of related questions, but it looks like they all assume that the JAR files already exist at the time of starting the JVM:
How to put a directory first on the classpath with Spring Boot?
Spring Boot Executable Jar with Classpath
SpringBoot external jar not load
Is there a way to achieve this without too many awkard hacks? Or is recommended to utilize something like OSGi? Am I looking at this completely wrong and there is a better way to have JARs on the classpath that do not need always need loading (if the JAR is "disabled", it should not be loaded/compiled by the JVM, should not be picked up by Spring, etc.)?
It looks like this is possible if the JAR files are copied before starting the Spring application. It feels hackish, but it works. Use at your own risk!
You need two classes, one for bootstrapping the external JARs, which will then start the second via a manually created PropertiesLauncher. The bootstrapping class can be a plain old regular Java class (but it can be a Spring Boot Application too) and only the second class needs to be a SpringBootApplication.
// BootstrapApplication.java
public class BootstrapApplication {
public static void main(String[] args) {
System.out.println("Please copy JAR files and press Enter ...");
System.in.read();
PropertiesLauncher.main(args);
}
}
// Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
In the gradle file, we can switch back to the default JarLauncher, by removing the bootJar task manifest configuration and applying settings via the springBoot configuration block. mainClass will end up as Start-Class in the MANIFEST.MF file.
// build.gradle
springBoot {
mainClass = 'com.example.customlauncher.BootstrapApplication'
}
In the properties file for the loader, a new property needs to be set, which points to the real application class. The settings in this file are only picked up by PropertiesLauncher and ignored by JarLauncher. In other words: JarLauncher delegates to Start-Class from the manifest file and PropertiesLauncher delegates to loader.main from its properties file.
# .../resources/loader.properties
loader.path=file:/path/to/dir
loader.main=com.example.customlauncher.Application
Spring (Boot) will first call the main method of BootstrapApplication, as specified in the MANIFEST.MF file (controlled via springBoot configuration block in the build.gradle file). In the implementation of this main method, a new PropertiesLauncher is created with the main class set to the "real" application (i.e. Application).
Executing the application is still done via the same invocation:
java -jar build/libs/customlauncher-0.0.1-SNAPSHOT.jar
Any JAR files added to /path/to/dir after the JVM has started, but before calling PropertiesLauncher#main in BootstrapApplication are then available in the classpath and application context as seen from Application.

How I can deploy existing web applications (wars) in the Spring Boot embedded server (tomcat)

I have an application which allows to dynamically generate web applications (wars) and I would like to deploy these applications in a server to test them and I think of putting them in the same embedded server of spring, here is how I solved the problem with a simple main java.
public class Main {
private final static Logger logger = LoggerFactory.getLogger(Main.class);
private final static File catalinaHome = new File(
"C:\\Users\\Dev\\Desktop\\demo\\userstory-2\\compiler\\patternHost");
private static Tomcat tomcat = null;
public static void main(String[] args) {
tomcat = new Tomcat();
tomcat.setPort(8080);
tomcat.setBaseDir(catalinaHome.getAbsolutePath());
tomcat.getHost().setAutoDeploy(true);
tomcat.getHost().setDeployOnStartup(true);
tomcat.getServer().addLifecycleListener(new VersionLoggerListener());
tomcat.getHost().addLifecycleListener(new HostConfig());
try {
tomcat.start();
} catch (LifecycleException e) {
logger.error("Tomcat could not be started.");
e.printStackTrace();
}
logger.info("Tomcat started on " + tomcat.getHost());
tomcat.getServer().await();
}
}
How can I do the same with spring boot. ?
I have converted a non-spring app to spring boot in this way and it worked for me. I was able to run it with spring boot embedded tomcat. Hope this helps.
Spring boot is all about speed, it comes with embedded-tomcat server(provided you use spring-boot-starter-web dependency) and now all you need is java to run your standalone spring boot application. It reduces the manual steps of copying war file to tomcat's webapp folder and then starting it.
Try the approach which suits your app.
If your old app is spring based :
Create a new spring boot starter web project and copy your old source code to this new project. Modify application.properties, resources folder, add all required dependencies in pom.xml file and change package as war.
Do a mvn clean install it will generate a war file (with embedded
tomcat) in target folder of your project's root directory. Now to run it all you
need to do is, in your target folder open terminal and run java -jar your_warFileName.war it will start the application.
If your old app is not a spring based:
Again start with new spring boot starter web project and copy your source code but then to use your old code with spring-boot, first you have to do clean-up stuff like adding #RestController to controller classes, declaring beans by marking classes with #Service or #Component and autowiring beans in the appropriate places. Once your code compiles fine then to run it your can use step 2 as above.

Spring boot - Using the implementations defined in the other project

I have 2 Projects.
All the API contract (interfaces) defines in the demo-parent project (spring boot application)
The implementation for those defined in the demo-child project (spring boot application)
demo-parent is a dependency for demo-child, defined in the pom.xml of demo-child
In demo-parent :
AccessAPI.java
public interface AccessAPI {
void call();
}
SpringDemoParentApplication.java
#SpringBootApplication
public class SpringDemoParentApplication {
#Autowired
private AccessAPI accessAPI;
public static void main(String[] args) {
SpringApplication.run(SpringDemoParentApplication.class, args);
}
#PostConstruct
void exec() {
accessAPI.call();
}
}
In demo-child I have the implementation for the service :
AccessAPIImpl.java
#Service
public class AccessAPIImpl implements AccessAPI {
#Override
public void call() {
System.out.println("Executing from AccessAPIImpl");
}
}
Goal trying to achieve :
I must be able to build these projects independently, and pass the demo-child project jar via classpath to pick the implementation and inject all the implementation when running of demo-parent .
Such as :
>java -jar demo-parent.jar -cp demo-child.jar
I expected that the implementation would be picked up from the demo-child and autoinjected but It is not working as expected.
Note : I dont want to add demo-child dependency on demo-parent, the implementations/dependency must be picked up at runtime.
Please check the Git repository :
https://github.com/anthonyvk/spring-demo-child
https://github.com/anthonyvk/spring-demo-parent
TL; TR:
java -cp demo-parent.jar -Dloader.path=demo-child.jar org.springframework.boot.loader.PropertiesLauncher
if the demo-child.jar is a Spring Boot application, i.e. if the jar is repacked by the Spring Boot plugin, you need to change the loader.path:
java -cp demo-parent.jar -Dloader.path='demo-child.jar!/BOOT-INF/classes' org.springframework.boot.loader.PropertiesLauncher
Explanation:
By default, Spring Boot uses JarLauncher, which looks only in BOOT-INF/lib/ inside the jar archive, -cp argument has no effect. The commands above switch to PropertiesLauncher, which honors loader.path parameter (-cp equivalent). For details see The Executable Jar Format
By the way, the project structure seems to be a bit problematic:
adding spring boot application (demo-parent.jar) as a dependency to demo-child.jar is tricky, as demo-parent.jar is repacked by the Spring Boot plugin and maven cannot find AccessAPI class
there are runtime circular dependencies, even if not explicitly specified in the POM
I don't know detailed requirements, but if possible, I would suggest:
extracting interfaces to a separate project, let's say demo-api
making demo-parent and demo-child depended on demo-api, not on each other
making demo-api and demo-child regular jar's, not Spring Boot applications (removing Spring Boot plugin)

Load classes in Spring before component scan

I have a situation a work where we have several jar filed in npm packages. If I add all the paths to the classpath before running my spring mvc app, it works fine.
But we want a "modular" way of doing this. IE, I don't know of all the packages that may or may not be there at runtime. I have a base folder, and I can easaly do a recusive file scan for all jar files, but how do I then load them before the component scan so everything is wired in correctly? I have this standard in my base Application class:
#Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
I assume I have to do something in my main() before I call run(), but im not sure what to do.
EDIT I have been trying to get this to work, and forgot to mention that this is a .war file in a tomcat container, and I don't think the main() method is being called.

Launch Spring Boot WicketWebApplication inside another java Application without main

i want to use Spring Boot with Spring Security and Apache Wicket 8.0 as web view.
It`s easy to do it with SpringBoot and just simply run jar file, but i want to do something like this:
Minecraft Server (Spigot, Bukkit, etc..) Scanning /plugins/ folder, and looking for .jar files.
All files must contain plugin.yml in root of .jar archive. This plugin.yml file contains a path to class file, for example - BukkitMain and this class MUST extend JavaPlugin.
Then MinecraftServer core is executing onEnable() method of BukkitMain.class (so, if we override this method - server code will execute all code inside this method)
For Example:
public class BukkitMain extends JavaPlugin {
#Override
public void onEnable(){
//SpringApplicationBuilder.run(....);
}
}
So, i can use Spring-boot-plugin in build part of maven .pom file, but it's packing all class files and resources to BOOT-INF inside jar and it's requiring main(String args[]) method to run SpringApplication. It`s bad variant, because i can't access my BukkitMain to run it.
When i use Maven Compiler Plugin and packing it to jar - it's all ok, but there is new problem. My BukkitMain trying to run SpringApplicationBuilder, but i'm getting ClassNotFoundException, cause there is no SpringApplicationBuilder inside jar.

Categories