I am trying to wrap a spring boot "uber JAR" with procrun.
Running the following works as expected:
java -jar my.jar
I need my spring boot jar to automatically start on windows boot. The nicest solution for this would be to run the jar as a service (same as a standalone tomcat).
When I try to run this I am getting "Commons Daemon procrun failed with exit value: 3"
Looking at the spring-boot source it looks as if it uses a custom classloader:
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-tools/spring-boot-loader/src/main/java/org/springframework/boot/loader/JarLauncher.java
I also get a "ClassNotFoundException" when trying to run my main method directly.
java -cp my.jar my.MainClass
Is there a method I can use to run my main method in a spring boot jar (not via JarLauncher)?
Has anyone successfully integrated spring-boot with procrun?
I am aware of http://wrapper.tanukisoftware.com/. However due to their licence I can't use it.
UPDATE
I have now managed to start the service using procrun.
set SERVICE_NAME=MyService
set BASE_DIR=C:\MyService\Path
set PR_INSTALL=%BASE_DIR%prunsrv.exe
REM Service log configuration
set PR_LOGPREFIX=%SERVICE_NAME%
set PR_LOGPATH=%BASE_DIR%
set PR_STDOUTPUT=%BASE_DIR%stdout.txt
set PR_STDERROR=%BASE_DIR%stderr.txt
set PR_LOGLEVEL=Error
REM Path to java installation
set PR_JVM=auto
set PR_CLASSPATH=%BASE_DIR%%SERVICE_NAME%.jar
REM Startup configuration
set PR_STARTUP=auto
set PR_STARTIMAGE=c:\Program Files\Java\jre7\bin\java.exe
set PR_STARTMODE=exe
set PR_STARTPARAMS=-jar#%PR_CLASSPATH%
REM Shutdown configuration
set PR_STOPMODE=java
set PR_STOPCLASS=TODO
set PR_STOPMETHOD=stop
REM JVM configuration
set PR_JVMMS=64
set PR_JVMMX=256
REM Install service
%PR_INSTALL% //IS//%SERVICE_NAME%
I now just need to workout how to stop the service. I am thinking of doing someting with the spring-boot actuator shutdown JMX Bean.
What happens when I stop the service at the moment is; windows fails to stop the service (but marks it as stopped), the service is still running (I can browse to localhost), There is no mention of the process in task manager (Not very good! unless I am being blind).
I ran into similar issues but found someone else (Francesco Zanutto) was gracious enough to write a blog post about their efforts. Their solution worked for me. I take no credit for the time they put into realizing this code.
http://zazos79.blogspot.com/2015/02/spring-boot-12-run-as-windows-service.html
He's using the jvm start and stop mode, compared to the exe mode I see in your example. With this, he's able to extend Spring Boot's JarLauncher to handle both "start" and "stop" commands from Windows services' which I believe you're looking to do for a graceful shutdown.
As with his examples you'll be adding multiple main methods, depending on your implementation, you will need to indicate which should now be invoked by the launcher. I'm using Gradle and simply had to add the following to my build.gradle:
springBoot{
mainClass = 'mydomain.app.MyApplication'
}
My Procrun installation script:
D:\app\prunsrv.exe //IS//MyServiceName ^
--DisplayName="MyServiceDisplayName" ^
--Description="A Java app" ^
--Startup=auto ^
--Install=%CD%\prunsrv.exe ^
--Jvm=%JAVA_HOME%\jre\bin\server\jvm.dll ^
--Classpath=%CD%\SpringBootApp-1.1.0-SNAPSHOT.jar; ^
--StartMode=jvm ^
--StartClass=mydomain.app.Bootstrap ^
--StartMethod=start ^
--StartParams=start ^
--StopMode=jvm ^
--StopClass=mydomain.app.Bootstrap ^
--StopMethod=stop ^
--StopParams=stop ^
--StdOutput=auto ^
--StdError=auto ^
--LogPath=%CD% ^
--LogLevel=Debug
JarLauncher extension class:
package mydomain.app;
import org.springframework.boot.loader.JarLauncher;
import org.springframework.boot.loader.jar.JarFile;
public class Bootstrap extends JarLauncher {
private static ClassLoader classLoader = null;
private static Bootstrap bootstrap = null;
protected void launch(String[] args, String mainClass, ClassLoader classLoader, boolean wait)
throws Exception {
Runnable runner = createMainMethodRunner(mainClass, args, classLoader);
Thread runnerThread = new Thread(runner);
runnerThread.setContextClassLoader(classLoader);
runnerThread.setName(Thread.currentThread().getName());
runnerThread.start();
if (wait == true) {
runnerThread.join();
}
}
public static void start (String []args) {
bootstrap = new Bootstrap ();
try {
JarFile.registerUrlProtocolHandler();
classLoader = bootstrap.createClassLoader(bootstrap.getClassPathArchives());
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void stop (String []args) {
try {
if (bootstrap != null) {
bootstrap.launch(args, bootstrap.getMainClass(), classLoader, true);
bootstrap = null;
classLoader = null;
}
}
catch (Exception ex) {
ex.printStackTrace();
System.exit(1);
}
}
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if ("start".equals(mode)) {
Bootstrap.start(args);
}
else if ("stop".equals(mode)) {
Bootstrap.stop(args);
}
}
}
My main Spring application class:
package mydomain.app;
import java.lang.management.ManagementFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@ComponentScan
@EnableAutoConfiguration
public class MyApplication {
private static final Logger logger = LoggerFactory.getLogger(MyApplication.class);
private static ApplicationContext applicationContext = null;
public static void main(String[] args) {
String mode = args != null && args.length > 0 ? args[0] : null;
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application mode:" + mode + " context:" + applicationContext);
}
if (applicationContext != null && mode != null && "stop".equals(mode)) {
System.exit(SpringApplication.exit(applicationContext, new ExitCodeGenerator() {
@Override
public int getExitCode() {
return 0;
}
}));
}
else {
SpringApplication app = new SpringApplication(MyApplication.class);
applicationContext = app.run(args);
if (logger.isDebugEnabled()) {
logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + " Application started context:" + applicationContext);
}
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With