cft
Become a CreatorSign inGet Started

How to build your Spring Boot as .deb package in maven

when you need to run your application as a service in Windows or Linux server it better wrap it in a package where you can control it as a system applications and services


user

Ismaeil Shajar

4 months ago | 9 min read
Follow

build-spring-boot-deb-package-maven-nfr09

a Sprint boot application

so let's start developing our application service and let it be RestAPI application so no front-end.

init

first will create a spring boot application using any method and add a spring web starter

add controller class

now add new class HomeController and add new end-point to return String message when call path "/".

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RestController;

@RestControllerpublic class HomeController {

@GetMapping

public String getHomeWelcome(){

return "Welcome Home Debian";

}

}

now either start an application now or edit the server port, for me I will set server port in application.yaml and then start.

server:

port: 8088

service test

now to test this application will use curl

curl http://localhost:8088

and will see "Welcome Home Debian" in terminal

Test maven build

*Note: this project use java 11 and you can use java 8 but need change gradle-maven-plugin version.(until now there is no gradle-maven-plugin version work with java 17) if you have multiple versions of JDK you need to test if maven see what you run so run in terminal mvn -v and check if the not same version you need to update JAVA_HOME env . (for me because I use java 17 and also use lombok "because I will need it next" I need to check maven version is at least 3.8.3 and lombok version 1.18.22 )

now run maven build

mvn clean install -DskipTests

clean to make sure all old build was clean and -DskipTests to stop run tests . not we have a new folder called target and we have ${app.name}.jar file in it we can test run it

java -jar target/${app.name}.jar

and then test curl again curl http://localhost:8088 and now if I see output everything is work

Build configuration

now we need to add build configuration that let our application build using Gradle plugin so add inside build tag

<pluginManagement>

<plugins>

<plugin>

<groupId>org.thingsboard</groupId>

<artifactId>gradle-maven-plugin</artifactId>

<version>1.0.11</version>

</plugin>

</plugins>

</pluginManagement>

after that, we can follow these steps 1- add properties to describe all pkg name and the user will be run and also log location in the system and install location to use them in build next

<properties>

<java.version>1.8</java.version>

<pkg.name>deb_home</pkg.name>

<pkg.user>debuser</pkg.user>

<pkg.unixLogFolder>/var/log/${pkg.name}</pkg.unixLogFolder>

<pkg.installFolder>/usr/share/${pkg.name}</pkg.installFolder>

</properties>

2- add output deb Name using finalName tag in build and use resources tag to import all resources

<finalName>${pkg.name}-${project.version}</finalName>

<resources>

<resource>

<directory>${project.basedir}/src/main/resources</directory>

</resource>

</resources>

3- then use gradle-maven-plugin to build as Gradle so need to add a plugin in <build> and then in <plugins> to told maven to do that

<plugin>

<groupId>org.thingsboard</groupId>

<artifactId>gradle-maven-plugin</artifactId>

<configuration>

<tasks>

<task>build</task>

<task>buildDeb</task>

</tasks>

<args>

<arg>-PprojectBuildDir=${project.build.directory}</arg>

<arg>-PprojectVersion=${project.version}</arg>

<arg>-PmainJar=${project.build.directory}/${project.build.finalName}-boot.${project.packaging}</arg>

<arg>-PpkgName=${pkg.name}</arg>

<arg>-PpkgUser=${pkg.user}</arg>

<arg>-PpkgInstallFolder=${pkg.installFolder}</arg>

<arg>-PpkgLogFolder=${pkg.unixLogFolder}</arg>

</args>

</configuration>

<executions>

<execution>

<phase>package</phase>

<goals>

<goal>invoke</goal>

</goals>

</execution>

</executions>

</plugin>

now run mvn clean install -DskipTests. after waiting until download and build finish we have this error

FAILURE: Build failed with an exception.

* What went wrong:

Task 'buildDeb' not found in root project 'deb'.

so what is happened? the answer is there are basic things we don't do here,. This is Gradle project so we need build.gradle file that gradle will use it. but if we need build.gradle too what is a function of gradle-maven-plugin? the function of gradle-maven-plugin is not to replace gradle but to run gradle tasks. so let complete gradle needs.

4- create build.gradle and add this

import org.apache.tools.ant.filters.ReplaceTokens

buildscript {

repositories {

mavenCentral()

mavenLocal()

}

dependencies {

classpath("com.netflix.nebula:gradle-ospackage-plugin:9.0.0")

}

}

apply plugin: "nebula.ospackage"

buildDir = projectBuildDir

version = projectVersion

distsDirName = "./"

// OS Package plugin configuration

ospackage {

packageName = pkgName

version = "${project.version}"

release = 1

os = LINUX

type = BINARY

into pkgInstallFolder

user pkgUser

permissionGroup pkgUser

// Copy the actual .jar file

from(mainJar) {

// Strip the version from the jar filename

rename { String fileName ->

"${pkgName}.jar"

}

fileMode 0500

into "bin"

}

// Copy the config files

from("target/conf") {

exclude "application.conf"

fileType CONFIG | NOREPLACE

fileMode 0754

into "conf"

}

}

// Same as the buildRpm task

buildDeb {

arch = "all"

archiveName = "${pkgName}.deb"

requires("openjdk-11-jre").or("java11-runtime").or("oracle-java11-installer").or("openjdk-11-jre-headless")

from("target/conf") {

include "application.conf"

filter(ReplaceTokens, tokens: ['pkg.platform': 'deb'])

fileType CONFIG | NOREPLACE

fileMode 0754

into "${pkgInstallFolder}/conf"

}

configurationFile("${pkgInstallFolder}/conf/application.conf")

configurationFile("${pkgInstallFolder}/conf/application.yaml")

// configurationFile("${pkgInstallFolder}/conf/logback.xml") // you can add logback

preInstall file("${buildDir}/control/deb/preinst")

postInstall file("${buildDir}/control/deb/postinst")

preUninstall file("${buildDir}/control/deb/prerm")

postUninstall file("${buildDir}/control/deb/postrm")

user pkgUser

permissionGroup pkgUser

directory(pkgLogFolder, 0755)

link("/etc/init.d/${pkgName}", "${pkgInstallFolder}/bin/${pkgName}.jar")

link("${pkgInstallFolder}/bin/${pkgName}.yml", "${pkgInstallFolder}/conf/${pkgName}.yml")

link("/etc/${pkgName}/conf", "${pkgInstallFolder}/conf")

}

5- as you can see we need to set some configurations and installation scripts as follows

application.conf
used to set JAVA_OPTS and some env variable

export JAVA_OPTS="$JAVA_OPTS -Dplatform=@pkg.platform@"

export LOG_FILENAME=debhome.out

export LOADER_PATH=/usr/share/debhome/conf

application.yaml service setting and we alwardy have
preinst Script will be run before install deb used to set users and group

#!/bin/sh

if ! getent group debuser >/dev/null; then

addgroup --system debuser

fi

if ! getent passwd debuser >/dev/null; then

adduser --quiet \

--system \

--ingroup debuser \

--quiet \

--disabled-login \

--disabled-password \

--home /usr/share/debhome\

--no-create-home \

debuser

fi

postinst script will be run after install deb

#!/bin/sh

chown -R debuser: /var/log/debhome

chown -R debuser: /usr/share/debhome/conf

update-rc.d debhome defaults

prerm script run when removing the application from the system before start remove operation

#!/bin/sh

if [ -e /var/run/debhome/debhome.pid ]; then

service debhome stop

fi

postrm script run when removing application after remove done

#!/bin/sh

update-rc.d -f debhome remove

unix.properties properties file set to use in prepare build in pom.xml

we will add those files in src/main folder as following

  • src/main/packaging/conf/application.conf
  • src/main/packaging/filter/unix.properties
  • src/main/packaging/script/control/deb/preinst
  • src/main/packaging/script/control/deb/postinst
  • src/main/packaging/script/control/deb/prerm
  • src/main/packaging/script/control/deb/postrm

6- add configuration plugin in pom.xml to copy this scripts and configuration files from resource and packaging folder in src to target

<plugin>

<groupId>org.apache.maven.plugins</groupId>

<artifactId>maven-resources-plugin</artifactId>

<executions>

<execution>

<id>copy-service-conf</id>

<phase>process-resources</phase>

<goals>

<goal>copy-resources</goal>

</goals>

<configuration>

<outputDirectory>${project.build.directory}/conf</outputDirectory>

<resources>

<resource>

<directory>src/main/resources</directory>

<filtering>false</filtering>

</resource>

<resource>

<directory>src/main/packaging/conf</directory>

<filtering>true</filtering>

</resource>

</resources>

<filters>

<filter>src/main/packaging/filters/unix.properties</filter>

</filters>

</configuration>

</execution>

<execution>

<id>copy-control</id>

<phase>process-resources</phase>

<goals>

<goal>copy-resources</goal>

</goals>

<configuration>

<outputDirectory>${project.build.directory}/control</outputDirectory>

<resources>

<resource>

<directory>src/main/packaging/scripts/control</directory>

<filtering>true</filtering>

</resource>

</resources>

<filters>

<filter>src/main/packaging/filters/unix.properties</filter>

</filters>

</configuration>

</execution>

</executions>

</plugin>

7- now run mvn clean install -DskipTests and we get output deb in the target. go to target folder and copy ${app-name}.deb file and now can do sudo dpkg -i ${app-name}.deb

you can find a project on GitHub

Upvote


user
Created by

Ismaeil Shajar

Follow

Software Developer

Software developer over 5 year experience in microservices and serverless in web ,IoT and in demand delivery software


people
Post

Upvote

Downvote

Comment

Bookmark

Share


Related Articles