4
votes

I'm a new who are getting started learning Spring Boot. I'm feeling that It's really helpful and great tool to develop Java application easily.

On the other hand, I'm considering developing a Daemon Service which collects data/message(s) from Apache Kafka via Kafka Consumer API and do some process about the retrieved data. This whole processes, of course, are done periodically.

So I've been developing the application as a Daemon by using Apache Commons Daemon. I now, however, want to use Spring Boot instead of it.

Is it possible implementing such service application via Spring Boot? If possible, please let me know how it could be implemented. Thanks in advance!

1

1 Answers

3
votes

I found this somewhere so apologies to the original owner but I created a project to which I added spring-boot-loader dependency

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-loader</artifactId>
    <scope>provided</scope>
</dependency>

because of the need to extend the JarLauncher class. Spring boot provides a special launcher that changes the java behaviour class loader. The class org.springframework.boot.loader.JarLauncher creates a special class loader and boostraps the application.

Since I wanted to launch the application as a window service, I chose Procrun as service manager. Procrun needs two start and stop methods or one method with string array parameter (see procrun project for more details). Therefore I created a Bootsrap class that extended JarLauncher and implemented the methods that Procrun needs.

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

In the spring boot application class I changed the main method with:

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(TestProcrunApplication.class);
        applicationContext = app.run(args);
        if (logger.isDebugEnabled()) {
            logger.debug("PID:" + ManagementFactory.getRuntimeMXBean().getName() + 
                         " Application started context:" + applicationContext);
        }
    }
}

Then, I installed the service with prunsrv.exe:

prunsrv.exe //IS//test-procrun --DisplayName="test-procrun" --Description="test-procrun" --Startup=auto --Install=%CD%\prunsrv.exe --Jvm=auto --Classpath=%CD%..\target\test-procrun-0.0.1-SNAPSHOT.jar --StartMode=jvm --StartClass=it.test.procrun.Bootstrap --StartMethod=start --StartParams=start --StopMode=jvm --StopClass=it.test.procrun.Bootstrap --StopMethod=stop --StopParams=stop --StdOutput=auto --StdError=auto --LogPath=%CD% --LogLevel=Debug