Spring boot JAR as windows service

試著忘記壹切 提交于 2020-05-09 19:33:26

问题


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).


回答1:


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);
            }
        }
    }
}



回答2:


This is now possible since Spring Boot 1.3 using winsw.

The documentation directs you to a reference implementation which shows how to setup a service.




回答3:


As of springboot v1.2.2 there is no clean way to shutdown a Spring Boot application, packaged as an uber jar, using procrun. Be sure to follow these issues as this is something others are asking about as well:

  • https://github.com/spring-projects/spring-boot/issues/519
  • https://github.com/spring-projects/spring-boot/issues/644

It's not clear if/how springboot maintainers are going to handle it. In the meantime, consider unzipping the uber jar and ignoring Spring Boot's JarLauncher.

My original answer to this question (viewable in history) proposed a way that should work (and I thought did), but does not due to how the classloader is handled in JarLauncher.




回答4:


Just ran into this and wanted to share, I fixed this issue a while back and issued a pull request. https://github.com/spring-projects/spring-boot/pull/2520

You can use my forked version until it gets merged to start/stop using procrun.




回答5:


Stay away of winsw, It's made with .NET, and I experience a lot of problems about my customers environment about out-of-date Windows.

I recommend NSSM, it's made using pure C, and I used it on all my out-of-date Windows without problems. It has the same features and more...

Here is a batch script (.bat) sample how to use it:

rem Register the service
nssm install my-java-service "C:\Program Files\Java\jre1.8.0_152\bin\java.exe" "-jar" "snapshot.jar"
rem Set the service working dir
nssm set my-java-service AppDirectory "c:\path\to\jar-diretory"
rem Redirect sysout to file
nssm set my-java-service AppStdout "c:\path\to\jar-diretory\my-java-service.out"
rem Redirect syserr to file
nssm set my-java-service AppStderr "c:\path\to\jar-diretory\my-java-service.err"
rem Enable redirection files rotation
nssm set my-java-service AppRotateFiles 1
rem Rotate files while service is running
nssm set my-java-service AppRotateOnline 1
rem Rotate files when they reach 10MB
nssm set my-java-service AppRotateBytes 10485760
rem Stop service when my-java-service exits/stop
nssm set my-java-service AppExit Default Exit
rem Restart service when my-java-service exits with code 2 (self-update)
nssm set my-java-service AppExit 2 Restart
rem Set the display name for the service
nssm set my-java-service DisplayName "My JAVA Service"
rem Set the description for the service
nssm set my-java-service Description "Your Corp"
rem Remove old rotated files (older than 30 days)
nssm set my-java-service AppEvents Rotate/Pre "cmd /c forfiles /p \"c:\path\to\jar-diretory\" /s /m \"my-java-service-*.*\" /d -30 /c \"cmd /c del /q /f @path\""
rem Make a copy of my-java-service.jar to snapshot.jar to leave the original JAR unlocked (for self-update purposes)
nssm set my-java-service AppEvents Start/Pre "cmd /c copy /y \"c:\path\to\jar-diretory\my-java-service.jar\" \"c:\path\to\jar-diretory\snapshot.jar\""


来源:https://stackoverflow.com/questions/24124062/spring-boot-jar-as-windows-service

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!