├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── exclude-pmd.properties ├── do-site-release ├── .gitignore ├── do-release ├── src └── site │ ├── markdown │ ├── startup-timeout.md │ ├── native-library.md │ ├── conditionals.md │ ├── tomcat-status.md │ ├── index.md │ ├── startup-progress.md │ ├── howto.md.vm │ └── status-providers.md │ └── site.xml ├── README.md ├── jsystemd-spring-boot-starter ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ ├── spring │ │ │ │ └── org.springframework.boot.autoconfigure.AutoConfiguration.imports │ │ │ │ └── spring.factories │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── jpmsilva │ │ │ └── jsystemd │ │ │ ├── SystemdServletContextListener.java │ │ │ ├── ConditionalOnSystemd.java │ │ │ ├── SystemdLifecycle.java │ │ │ ├── OnSystemdCondition.java │ │ │ ├── PendingHealthProvider.java │ │ │ ├── SystemdActuatorHealthProvider.java │ │ │ ├── SystemdTomcatStatusProvider.java │ │ │ ├── SystemdApplicationContextStatusProvider.java │ │ │ ├── SystemdHealthProviderProperties.java │ │ │ ├── SystemdSpringApplicationRunListener.java │ │ │ ├── SystemdApplicationRunStatusProvider.java │ │ │ └── SystemdAutoConfiguration.java │ └── site │ │ └── site.xml └── pom.xml ├── jsystemd-spring-boot-sample ├── src │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── github │ │ │ └── jpmsilva │ │ │ └── jsystemd │ │ │ └── SampleApplicationTests.java │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── jpmsilva │ │ └── jsystemd │ │ └── SampleApplication.java └── pom.xml ├── jsystemd-core ├── src │ ├── site │ │ └── site.xml │ └── main │ │ └── java │ │ └── com │ │ └── github │ │ └── jpmsilva │ │ └── jsystemd │ │ ├── SystemdStatusProvider.java │ │ ├── SystemdClassLoaderStatusProvider.java │ │ ├── SystemdHeapStatusProvider.java │ │ ├── SystemdNonHeapStatusProvider.java │ │ ├── HealthProvider.java │ │ ├── SystemdUtilities.java │ │ ├── SystemdNotify.java │ │ └── Systemd.java └── pom.xml ├── mvnw.cmd ├── LICENSE ├── mvnw ├── google_checkstyle.xml ├── pom.xml └── codestyle └── intellij-java-google-style.xml /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jpmsilva/jsystemd/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /exclude-pmd.properties: -------------------------------------------------------------------------------- 1 | com.github.jpmsilva.jsystemd.Systemd=TooManyStaticImports 2 | com.github.jpmsilva.jsystemd.SystemdNotify=TooManyStaticImports 3 | -------------------------------------------------------------------------------- /do-site-release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT=$(readlink -f "$0") 4 | SCRIPTPATH=$(dirname "$SCRIPT") 5 | 6 | set -e 7 | 8 | "${SCRIPTPATH}"/mvnw clean package site site:site -P !sample -am -pl :jsystemd-core,:jsystemd-spring-boot-starter --debug 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ -------------------------------------------------------------------------------- /do-release: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT=$(readlink -f "$0") 4 | SCRIPTPATH=$(dirname "$SCRIPT") 5 | 6 | set -e 7 | 8 | git checkout master && \ 9 | git pull && \ 10 | git checkout development && \ 11 | git pull && \ 12 | "${SCRIPTPATH}"/mvnw jgitflow:release-start -P !sample -am -pl :jsystemd-core,:jsystemd-spring-boot-starter && \ 13 | "${SCRIPTPATH}"/mvnw jgitflow:release-finish -P !sample -am -pl :jsystemd-core,:jsystemd-spring-boot-starter 14 | -------------------------------------------------------------------------------- /src/site/markdown/startup-timeout.md: -------------------------------------------------------------------------------- 1 | # Startup timeout 2 | 3 | During startup, as status updates are sent to systemd to notify on progress, the timeout is extended. 4 | This means that as long as the application context is making progress in creating beans, the startup will not 5 | timeout. The default timeout of 30 seconds that systemd implements should be sufficient to ensure the 6 | service starts up correctly. 7 | 8 | Note, though, that timeout extension has only been implemented starting with version 236 of systemd. 9 | Check your version of systemd using `systemctl --version`. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # jsystemd 2 | 3 | This project contains modules to integrate Java services with systemd. 4 | 5 | It aims primarily to provide decent integration of services developed with Spring Boot, but 6 | may contain interesting and reusable components for other projects. 7 | 8 | ## How to use 9 | 10 | When using Spring Boot, simply import the `com.github.jpmsilva.jsystemd:systemd-spring-boot-starter` 11 | dependency into your own project. 12 | 13 | Refer to the [documentation site](https://jpmsilva.github.io/jsystemd-site/) 14 | for more information, and how to use outside of Spring Boot. 15 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Joao Silva 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | com.github.jpmsilva.jsystemd.SystemdAutoConfiguration 15 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/resources/META-INF/spring.factories: -------------------------------------------------------------------------------- 1 | # Copyright 2018 Joao Silva 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | org.springframework.boot.SpringApplicationRunListener=\ 15 | com.github.jpmsilva.jsystemd.SystemdSpringApplicationRunListener 16 | -------------------------------------------------------------------------------- /src/site/markdown/native-library.md: -------------------------------------------------------------------------------- 1 | # Native library 2 | 3 | The integration uses [JNA](https://github.com/java-native-access/jna) (`net.java.dev.jna:jna`) to interface 4 | with [libsystemd](https://github.com/systemd/systemd/tree/master/src/libsystemd) natively. 5 | 6 | ## Running without JNA 7 | 8 | It used to be possible to switch the implementation from JNA with an executable call to the 9 | [systemd-notify](https://www.freedesktop.org/software/systemd/man/systemd-notify.html) program that is bundled with systemd. 10 | 11 | However, that method is no longer supported. 12 | 13 | For the time being, JNA is the only reliable way to interface with systemd via libsystemd, as the JVM does not 14 | yet [implement proper support for Datagram Channels (SOCK_DGRAM) over Unix Domain Sockets (AF_UNIX)](https://bugs.openjdk.org/browse/JDK-8297837). 15 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-sample/src/test/java/com/github/jpmsilva/jsystemd/SampleApplicationTests.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import org.junit.jupiter.api.Test; 20 | import org.springframework.boot.test.context.SpringBootTest; 21 | 22 | @SpringBootTest 23 | class SampleApplicationTests { 24 | 25 | @Test 26 | void contextLoads() { 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.2/apache-maven-3.9.2-bin.zip 18 | wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar 19 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-sample/src/main/java/com/github/jpmsilva/jsystemd/SampleApplication.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import org.springframework.boot.SpringApplication; 20 | import org.springframework.boot.autoconfigure.SpringBootApplication; 21 | 22 | @SpringBootApplication 23 | public class SampleApplication { 24 | 25 | public static void main(String[] args) { 26 | SpringApplication.run(SampleApplication.class, args); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/site/markdown/conditionals.md: -------------------------------------------------------------------------------- 1 | # Conditionals 2 | 3 | _Note: this page is only relevant for Spring Boot applications._ 4 | 5 | The autoconfiguration class ot the [starter module](jsystemd-spring-boot-starter/index.html) 6 | is guarded with [ConditionalOnSystemd](apidocs/com/github/jpmsilva/jsystemd/ConditionalOnSystemd.html). 7 | 8 | This conditional checks if running under Linux, and searches for the presence of the environment 9 | property [NOTIFY_SOCKET](https://www.freedesktop.org/software/systemd/man/sd_notify.html#%24NOTIFY_SOCKET). 10 | 11 | Should there be a need to provide custom jsystemd beans, such 12 | as custom [SystemdNotifyStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdNotifyStatusProvider.html) implementations, it is preferable to 13 | use this conditional on the configuration class providing them, as it preferable to not waste time building beans that will never be needed or used. 14 | 15 | If you autowire the current [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html) to use it directly, then 16 | make sure to use the provided `@ConditionalOnSystemd`, or a similar `@ConditionalOnBean(Systemd.class)`. 17 | -------------------------------------------------------------------------------- /src/site/markdown/tomcat-status.md: -------------------------------------------------------------------------------- 1 | # Tomcat status 2 | 3 | _Note: this page is only relevant for Spring Boot applications._ 4 | 5 | Additionally, when using the Tomcat starter, the Catalina mbeans will be used to show connector usage on the service status. 6 | 7 | The connector status corresponds to the current usage of the thread pool, in the form of `busy threads/total available threads`. 8 | So, `http-nio-8080: 5/10` means that 5 out of the 10 available threads of the `http-nio-8080` connector are actively serving requests. 9 | 10 | This information is shown when requesting the service status: 11 | 12 | ``` 13 | [root@machine ~]# systemctl status myservice.service 14 | ● myservice.service - My Spring Boot Service 15 | Loaded: loaded (/etc/systemd/system/myservice.service; disabled; vendor preset: disabled) 16 | Active: active (running) since Wed 2018-03-07 23:01:36 WET; 11min ago 17 | Main PID: 21034 (java) 18 | Status: "Heap: 339 MiB/512 MiB, Non-heap: 118.1 MiB/121.5 MiB, Classes: 15734, http-nio-8080: 2/10" 19 | CGroup: /system.slice/myservice.service 20 | └─21034 /opt/jdk17/bin/java -XX:+ExitOnOutOfMemoryError -jar /opt/myservice/myservice.jar 21 | ``` 22 | -------------------------------------------------------------------------------- /jsystemd-core/src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdServletContextListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import jakarta.servlet.ServletContextEvent; 20 | import jakarta.servlet.ServletContextListener; 21 | 22 | /** 23 | * Implementation of {@link ServletContextListener} that closes the systemd integration channel. 24 | * 25 | * @author Joao Silva 26 | * @see SystemdNotify#close() 27 | */ 28 | public class SystemdServletContextListener implements ServletContextListener { 29 | 30 | @Override 31 | public void contextDestroyed(ServletContextEvent sce) { 32 | SystemdNotify.close(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/site/markdown/index.md: -------------------------------------------------------------------------------- 1 | # jsystemd 2 | 3 | jsystemd aims to provide a better platform to integrate Java applications with systemd, and run them as proper operating system services. 4 | 5 | The integration provides bindings for any Java process to notify systemd once it completes initialization, as well 6 | as provide detailed status information to the service supervisor. 7 | 8 | The distribution of this software is composed of two parts: 9 | 10 | * the [jsystemd-core](jsystemd-core/index.html) module that includes all the basic bindings to integrate with systemd 11 | * the [jsystemd-spring-boot-starter](jsystemd-spring-boot-starter/index.html) module that provides automatic configuration of jsystemd when running under 12 | inside Spring Boot applications. 13 | 14 | **Warning:** As of release 3, the minimum supported JVM version is 17, and the Spring Boot starter requires at least Spring 3. 15 | 16 | The jsystemd-core module packages some components that expose the status of the JVM to systemd (the Spring Boot starter package automatically configures those). 17 | 18 | Binaries are available for download from [GitHub](https://github.com/jpmsilva/jsystemd/releases), 19 | and are also published to [Maven Central](https://search.maven.org/#search%7Cga%7C1%7Cg%3A%22com.github.jpmsilva.jsystemd%22). 20 | 21 | Check the [How to](howto.html) page for information on how to get started. 22 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/ConditionalOnSystemd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | import org.springframework.context.annotation.Conditional; 25 | 26 | /** 27 | * {@link Conditional} that only matches when the process is running under systemd. 28 | * 29 | * @author Joao Silva 30 | * @see SystemdUtilities#isUnderSystemd() 31 | * @see OnSystemdCondition 32 | */ 33 | @SuppressWarnings("WeakerAccess") 34 | @Target({ElementType.TYPE, ElementType.METHOD}) 35 | @Retention(RetentionPolicy.RUNTIME) 36 | @Documented 37 | @Conditional(OnSystemdCondition.class) 38 | public @interface ConditionalOnSystemd { 39 | 40 | } 41 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import org.jetbrains.annotations.NotNull; 20 | 21 | /** 22 | * Interface that represents any object that can contribute with status information regarding the running process to the service supervisor. 23 | * 24 | *

Status providers can be registered through {@link com.github.jpmsilva.jsystemd.Systemd#addStatusProviders(SystemdStatusProvider...)} 25 | * 26 | * @author Joao Silva 27 | * @see Systemd#addStatusProviders(SystemdStatusProvider...) 28 | */ 29 | @SuppressWarnings("WeakerAccess") 30 | public interface SystemdStatusProvider { 31 | 32 | /** 33 | * Exposes status information to send to systemd. 34 | * 35 | * @return the string message to add to the service status 36 | */ 37 | default @NotNull String status() { 38 | return ""; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdClassLoaderStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import java.lang.management.ClassLoadingMXBean; 20 | import java.lang.management.ManagementFactory; 21 | import java.util.Optional; 22 | import org.jetbrains.annotations.NotNull; 23 | import org.springframework.core.annotation.Order; 24 | 25 | /** 26 | * Implementation of {@link SystemdStatusProvider} that provides information regarding loaded classes. 27 | * 28 | * @author Joao Silva 29 | */ 30 | @Order(-1000) 31 | public class SystemdClassLoaderStatusProvider implements SystemdStatusProvider { 32 | 33 | /** 34 | * Create a new SystemdClassLoaderStatusProvider. 35 | */ 36 | public SystemdClassLoaderStatusProvider() { 37 | } 38 | 39 | @Override 40 | public @NotNull String status() { 41 | return Optional.ofNullable(ManagementFactory.getClassLoadingMXBean()) 42 | .map(ClassLoadingMXBean::getLoadedClassCount) 43 | .map(t -> String.format("Classes: %d", t)) 44 | .orElse(""); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdHeapStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.formatByteCount; 20 | 21 | import java.lang.management.ManagementFactory; 22 | import java.lang.management.MemoryMXBean; 23 | import java.util.Optional; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.springframework.core.annotation.Order; 26 | 27 | /** 28 | * Implementation of {@link SystemdStatusProvider} that provides information regarding the heap zone of the memory. 29 | * 30 | * @author Joao Silva 31 | */ 32 | @Order(-3000) 33 | public class SystemdHeapStatusProvider implements SystemdStatusProvider { 34 | 35 | /** 36 | * Create a new SystemdHeapStatusProvider. 37 | */ 38 | public SystemdHeapStatusProvider() { 39 | } 40 | 41 | @Override 42 | public @NotNull String status() { 43 | return Optional.ofNullable(ManagementFactory.getMemoryMXBean()) 44 | .map(MemoryMXBean::getHeapMemoryUsage) 45 | .map(t -> String.format("Heap: %s/%s", formatByteCount(t.getUsed()), formatByteCount(t.getCommitted()))) 46 | .orElse(""); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdNonHeapStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.formatByteCount; 20 | 21 | import java.lang.management.ManagementFactory; 22 | import java.lang.management.MemoryMXBean; 23 | import java.util.Optional; 24 | import org.jetbrains.annotations.NotNull; 25 | import org.springframework.core.annotation.Order; 26 | 27 | /** 28 | * Implementation of {@link SystemdStatusProvider} that provides information regarding the non-heap zone of the memory. 29 | * 30 | * @author Joao Silva 31 | */ 32 | @Order(-2000) 33 | public class SystemdNonHeapStatusProvider implements SystemdStatusProvider { 34 | 35 | /** 36 | * Create a new SystemdNonHeapStatusProvider. 37 | */ 38 | public SystemdNonHeapStatusProvider() { 39 | } 40 | 41 | @Override 42 | public @NotNull String status() { 43 | return Optional.ofNullable(ManagementFactory.getMemoryMXBean()) 44 | .map(MemoryMXBean::getNonHeapMemoryUsage) 45 | .map(t -> String.format("Non-heap: %s/%s", formatByteCount(t.getUsed()), formatByteCount(t.getCommitted()))) 46 | .orElse(""); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdLifecycle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import org.springframework.context.SmartLifecycle; 20 | 21 | /** 22 | * Implementation of {@link SmartLifecycle} that logs systemd integration status on startup, and sends a stopping signal when the application is shutting down. 23 | * 24 | * @author Joao Silva 25 | * @see SmartLifecycle 26 | * @see Systemd#logStatus() 27 | * @see Systemd#stopping() 28 | */ 29 | public class SystemdLifecycle implements SmartLifecycle { 30 | 31 | private final Systemd systemd; 32 | private boolean running = false; 33 | 34 | /** 35 | * Creates a new instance using the provided {@link Systemd} as the integration point. 36 | * 37 | * @param systemd the {@link Systemd} to send status information to 38 | */ 39 | public SystemdLifecycle(Systemd systemd) { 40 | this.systemd = systemd; 41 | } 42 | 43 | @Override 44 | public void start() { 45 | systemd.logStatus(); 46 | running = true; 47 | } 48 | 49 | @Override 50 | public void stop() { 51 | systemd.stopping(); 52 | running = false; 53 | } 54 | 55 | @Override 56 | public boolean isRunning() { 57 | return running; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/site/markdown/startup-progress.md: -------------------------------------------------------------------------------- 1 | # Startup progress 2 | 3 | _Note: this page is only relevant for Spring Boot applications._ 4 | 5 | As a Spring Boot application starts up, it progresses through various stages as defined 6 | in [SpringApplicationRunListener](https://docs.spring.io/spring-boot/docs/3.0.0/api/org/springframework/boot/SpringApplicationRunListener.html): 7 | * starting 8 | * environment prepared 9 | * context prepared 10 | * context loaded 11 | * started 12 | * ready 13 | * failed (not used) 14 | 15 | These stages compose the startup sequence. 16 | 17 | The current stage is shown when requesting the service status: 18 | 19 | ``` 20 | [root@machine ~]# systemctl status myservice.service 21 | ● myservice.service - My Spring Boot Service 22 | Loaded: loaded (/etc/systemd/system/myservice.service; disabled; vendor preset: disabled) 23 | Active: activating (start) since Wed 2018-03-07 23:01:10 WET; 3s ago 24 | Main PID: 21034 (java) 25 | Status: "State: context prepared" 26 | CGroup: /system.slice/myservice.service 27 | └─21034 /opt/jdk17/bin/java -XX:+ExitOnOutOfMemoryError -jar /opt/myservice/myservice.jar 28 | ``` 29 | 30 | As soon as the application context is ready and starts creating beans, the status will also show the progress on bean creation, which can give you an 31 | approximate measure of the percent complete: 32 | 33 | ``` 34 | [root@machine ~]# systemctl status myservice.service 35 | ● myservice.service - My Spring Boot Service 36 | Loaded: loaded (/etc/systemd/system/myservice.service; disabled; vendor preset: disabled) 37 | Active: activating (start) since Wed 2018-03-07 23:01:10 WET; 11s ago 38 | Main PID: 21034 (java) 39 | Status: "State: context loaded, Creating bean 94 of 472" 40 | CGroup: /system.slice/myservice.service 41 | └─21034 /opt/jdk17/bin/java -XX:+ExitOnOutOfMemoryError -jar /opt/myservice/myservice.jar 42 | ``` 43 | 44 | This status information will only be shown during the startup sequence of a Spring Boot application, and will no longer be displayed after systemd is notified 45 | that the service is ready. 46 | -------------------------------------------------------------------------------- /jsystemd-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 4.0.0 21 | 22 | jsystemd-core 23 | 24 | 25 | com.github.jpmsilva.jsystemd 26 | jsystemd 27 | 3.1.2-SNAPSHOT 28 | 29 | 30 | jsystemd core 31 | 32 | The core library for systemd integration. This component does not depend on Spring, and can be used in any Java application. 33 | 34 | 35 | 36 | 37 | net.java.dev.jna 38 | jna 39 | 40 | 41 | org.slf4j 42 | slf4j-api 43 | 44 | 45 | org.springframework 46 | spring-core 47 | provided 48 | 49 | 50 | 51 | 52 | 53 | 54 | org.apache.maven.plugins 55 | maven-compiler-plugin 56 | 57 | 58 | --add-modules 59 | java.management 60 | 61 | 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /src/site/markdown/howto.md.vm: -------------------------------------------------------------------------------- 1 | #set($h1 = '#') 2 | #set($h2 = '##') 3 | #set($h3 = '###') 4 | #set($h4 = '####') 5 | 6 | $h1 How to use 7 | 8 | $h2 Under Spring Boot 9 | 10 | When using Spring Boot, simply import the `systemd-spring-boot-starter` module as a dependency: 11 | 12 | ```xml 13 | 14 | 15 | com.github.jpmsilva.jsystemd 16 | jsystemd-spring-boot-starter 17 | ${project.version} 18 | 19 | 20 | ``` 21 | 22 | Autoconfiguration takes place through the class [SystemdAutoConfiguration](apidocs/com/github/jpmsilva/jsystemd/SystemdAutoConfiguration.html), 23 | and will notify systemd once your application starts up successfully, via a Spring Boot event listener 24 | of type [ApplicationReadyEvent](https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/context/event/ApplicationReadyEvent.html). 25 | 26 | $h2 Regular Java application 27 | 28 | As a regular Java application, you can also use jsystemd. 29 | 30 | First, import the `jsystemd-core` module as a dependency: 31 | 32 | ```xml 33 | 34 | 35 | com.github.jpmsilva.jsystemd 36 | jsystemd-core 37 | ${project.version} 38 | 39 | 40 | ``` 41 | 42 | Then, create an instance of [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html) using the provided builder methods, 43 | and once your application completes the startup sequence and is ready to do work, just call [ready](apidocs/com/github/jpmsilva/jsystemd/Systemd.html#ready--). 44 | 45 | $h2 Systemd service unit 46 | 47 | When using this library, service units should now use `Type=notify` under the `[Service]` unit configuration: 48 | 49 | ``` 50 | [Unit] 51 | Description=MyService 52 | Requires=network.target 53 | After=network.target 54 | After=syslog.target 55 | 56 | [Service] 57 | Type=notify 58 | WorkingDirectory=/opt/myservice 59 | ExecStart=/opt/jdk17/bin/java -XX:+ExitOnOutOfMemoryError -jar /opt/myservice/myservice.jar 60 | SuccessExitStatus=143 61 | KillMode=mixed 62 | TimeoutStopSec=10 63 | TimeoutStartSec=30 64 | 65 | [Install] 66 | WantedBy=multi-user.target 67 | ``` 68 | 69 | In the above example, `/opt/myservice/myservice.jar` is a packed Java application. 70 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 4.0.0 21 | 22 | jsystemd-spring-boot-starter 23 | 24 | 25 | com.github.jpmsilva.jsystemd 26 | jsystemd 27 | 3.1.2-SNAPSHOT 28 | 29 | 30 | jsystemd starter module for Spring Boot 31 | Spring boot starter that provides autoconfiguration for jsystemd, effectively turning any spring boot application into a proper system service 32 | under systemd. 33 | 34 | 35 | 36 | 37 | ${project.groupId} 38 | jsystemd-core 39 | ${project.version} 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-autoconfigure 44 | 45 | 46 | org.springframework.boot 47 | spring-boot-starter-tomcat 48 | true 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-starter-actuator 53 | true 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-configuration-processor 58 | true 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/site/site.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 23 | 24 |

25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | org.apache.maven.skins 42 | maven-fluido-skin 43 | 2.0.0-M6 44 | 45 | 46 | 47 | 48 | jpmsilva/jsystemd 49 | right 50 | gray 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/OnSystemdCondition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.isUnderSystemd; 20 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.notifySocketPath; 21 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.osName; 22 | 23 | import org.jetbrains.annotations.NotNull; 24 | import org.springframework.boot.autoconfigure.condition.ConditionMessage; 25 | import org.springframework.boot.autoconfigure.condition.ConditionMessage.Builder; 26 | import org.springframework.boot.autoconfigure.condition.ConditionOutcome; 27 | import org.springframework.boot.autoconfigure.condition.SpringBootCondition; 28 | import org.springframework.context.annotation.Condition; 29 | import org.springframework.context.annotation.ConditionContext; 30 | import org.springframework.core.type.AnnotatedTypeMetadata; 31 | 32 | /** 33 | * {@link Condition} that checks if the program is running under systemd. 34 | * 35 | * @author Joao Silva 36 | * @see SystemdUtilities#isUnderSystemd() 37 | * @see ConditionalOnSystemd 38 | */ 39 | class OnSystemdCondition extends SpringBootCondition { 40 | 41 | @NotNull 42 | private final Builder message = ConditionMessage.forCondition(ConditionalOnSystemd.class); 43 | 44 | @Override 45 | public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { 46 | if (isUnderSystemd()) { 47 | return ConditionOutcome.match(message.foundExactly( 48 | "Operating system is " + osName() + " and NOTIFY_SOCKET points to \"" + notifySocketPath() + "\"")); 49 | } 50 | return ConditionOutcome.noMatch(message.notAvailable( 51 | "Operating system is " + osName() + " and NOTIFY_SOCKET points to \"" + notifySocketPath() + "\"")); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/HealthProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 TomTom N.V. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import java.util.Collections; 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | import java.util.Objects; 23 | 24 | /** 25 | * A health provider can be used to control the watchdog heartbeat. An unhealthy state does not trigger the heartbeat. 26 | * 27 | * @author Christian Lorenz 28 | */ 29 | public interface HealthProvider { 30 | 31 | /** 32 | * Determines if an application is healthy. 33 | * 34 | * @return representation of the application {@link Health} state 35 | */ 36 | Health health(); 37 | 38 | /** 39 | * Wrapper of health state. 40 | */ 41 | class Health { 42 | 43 | /** 44 | * true if this object represents a healthy state, false otherwise. 45 | */ 46 | public final boolean healthy; 47 | 48 | /** 49 | * Additional health details for each application component. 50 | */ 51 | public final Map details; 52 | 53 | /** 54 | * Provides for a health representation (healthy/unhealthy), with health details for each component. 55 | * 56 | * @param healthy true if this object represents a healthy state, false otherwise 57 | * @param details additional health details for each application component 58 | * @throws NullPointerException if details is null 59 | */ 60 | public Health(boolean healthy, Map details) { 61 | this.healthy = healthy; 62 | this.details = new HashMap<>(Objects.requireNonNull(details, "Details must not be null")); 63 | } 64 | 65 | /** 66 | * Synthesize a health object that represents a healthy application state. 67 | * 68 | * @return a synthetic healthy state. 69 | */ 70 | public static Health healthy() { 71 | return new Health(true, Collections.emptyMap()); 72 | } 73 | 74 | @Override 75 | public String toString() { 76 | return "Health{healthy=" + this.healthy + ", details=" + this.details + '}'; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-sample/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 4.0.0 21 | 22 | jsystemd-spring-boot-sample 23 | 24 | 25 | com.github.jpmsilva.jsystemd 26 | jsystemd 27 | 3.1.2-SNAPSHOT 28 | 29 | 30 | jsystemd sample Spring Boot application 31 | Spring boot sample application. 32 | 33 | 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-web 38 | 39 | 40 | 41 | org.springframework.boot 42 | spring-boot-starter-actuator 43 | 44 | 45 | 46 | com.github.jpmsilva.jsystemd 47 | jsystemd-spring-boot-starter 48 | ${project.version} 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-starter-test 54 | test 55 | 56 | 57 | 58 | 59 | 60 | 61 | org.apache.maven.plugins 62 | maven-deploy-plugin 63 | 64 | true 65 | 66 | 67 | 68 | 69 | org.springframework.boot 70 | spring-boot-maven-plugin 71 | ${org.springframework.boot.version} 72 | 73 | 74 | repackage 75 | 76 | repackage 77 | 78 | 79 | 80 | 81 | com.github.jpmsilva.jsystemd.SampleApplication 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/PendingHealthProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 TomTom N.V. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static java.lang.invoke.MethodHandles.lookup; 20 | import static org.slf4j.LoggerFactory.getLogger; 21 | 22 | import java.time.Instant; 23 | import java.time.temporal.ChronoUnit; 24 | import java.time.temporal.TemporalUnit; 25 | import java.util.Objects; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | import org.slf4j.Logger; 29 | 30 | /** 31 | * Implementation of {@link HealthProvider} that suppresses/delays the unhealthy state of a delegate for a configurable period. 32 | * 33 | * @author Christian Lorenz 34 | */ 35 | public class PendingHealthProvider implements HealthProvider { 36 | 37 | private static final Logger logger = getLogger(lookup().lookupClass()); 38 | 39 | @NotNull 40 | private final HealthProvider delegate; 41 | private final long delay; 42 | @NotNull 43 | private final TemporalUnit delayUnit; 44 | @Nullable 45 | private Instant unhealthySince; 46 | 47 | /** 48 | * Creates a new health provider that delegates the actual work to the delegate, and delays 49 | * reporting unhealthy status. 50 | * 51 | * @param delegate health provider from which to obtain the true health status of the application, never null 52 | * @param delay time that has to be elapsed before the real unhealthy status is provided 53 | * @param delayUnit {@link TemporalUnit} for delay, never null 54 | */ 55 | public PendingHealthProvider(@NotNull HealthProvider delegate, long delay, @NotNull TemporalUnit delayUnit) { 56 | this.delegate = Objects.requireNonNull(delegate, "Delegate must not be null"); 57 | this.delay = delay; 58 | this.delayUnit = Objects.requireNonNull(delayUnit, "Delay unit must not be null"); 59 | } 60 | 61 | @Override 62 | public Health health() { 63 | Health health = delegate.health(); 64 | Instant now = Instant.now(); 65 | if (!health.healthy) { 66 | if (unhealthySince == null) { 67 | unhealthySince = now; 68 | } 69 | Instant deadline = unhealthySince.plus(delay, delayUnit); 70 | logger.debug("Application unhealthy since {} (unhealthy status suppressed until {})", unhealthySince, deadline); 71 | if (deadline.isAfter(now)) { 72 | // healthy until delay has expired 73 | return HealthProvider.Health.healthy(); 74 | } 75 | } else { 76 | if (unhealthySince != null) { 77 | logger.debug("Application healthy again (unhealthy for {}s", ChronoUnit.SECONDS.between(unhealthySince, now)); 78 | unhealthySince = null; 79 | } 80 | } 81 | return health; 82 | } 83 | } -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdActuatorHealthProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 TomTom N.V. 3 | * Copyright 2023 Joao Silva 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.github.jpmsilva.jsystemd; 19 | 20 | import static java.lang.invoke.MethodHandles.lookup; 21 | import static org.slf4j.LoggerFactory.getLogger; 22 | 23 | import java.util.Collection; 24 | import java.util.List; 25 | import java.util.Map; 26 | import java.util.Objects; 27 | import java.util.Set; 28 | import java.util.stream.Collectors; 29 | import org.jetbrains.annotations.NotNull; 30 | import org.slf4j.Logger; 31 | import org.springframework.boot.actuate.health.HealthIndicator; 32 | import org.springframework.boot.actuate.health.Status; 33 | import org.springframework.core.annotation.Order; 34 | 35 | /** 36 | * Implementation of {@link HealthProvider} that provides application health based on Spring Boot Actuator Health Indicators. 37 | * 38 | * @author Christian Lorenz 39 | */ 40 | @Order(2000) 41 | public class SystemdActuatorHealthProvider implements SystemdStatusProvider, HealthProvider { 42 | 43 | private static final Logger logger = getLogger(lookup().lookupClass()); 44 | 45 | @NotNull 46 | private final List healthIndicators; 47 | @NotNull 48 | private final Set unhealthyStatusCodes; 49 | 50 | /** 51 | * Creates a new instance using the provided {@link HealthIndicator} and {@link Status}. 52 | * 53 | *

{@link HealthIndicator} are used to determine the health of the application, whereas {@link Status} 54 | * indicate which status should be considered as unhealthy. 55 | * 56 | * @param healthIndicators Spring Boot Actuator Health Indicators 57 | * @param unhealthyStatusCodes list of status codes considered as unhealthy 58 | */ 59 | public SystemdActuatorHealthProvider(@NotNull List healthIndicators, @NotNull Set unhealthyStatusCodes) { 60 | this.healthIndicators = Objects.requireNonNull(healthIndicators, "Health indicators must not be null"); 61 | this.unhealthyStatusCodes = Objects.requireNonNull(unhealthyStatusCodes, "Unhealthy status codes must not be null"); 62 | if (this.unhealthyStatusCodes.isEmpty()) { 63 | logger.warn("No status codes considered as unhealthy"); 64 | } else { 65 | logger.debug("Status codes considered as unhealthy={}", unhealthyStatusCodes); 66 | } 67 | } 68 | 69 | @Override 70 | public Health health() { 71 | Collection unhealthyIndicators = healthIndicators.stream() 72 | .filter(it -> unhealthyStatusCodes.contains(it.health().getStatus())) 73 | .toList(); 74 | logger.debug("Application health state={}", unhealthyIndicators.stream().map(HealthIndicator::health).collect(Collectors.toList())); 75 | boolean healthy = unhealthyIndicators.isEmpty(); 76 | return new Health(healthy, unhealthyIndicators.stream() 77 | .map(it -> it.health().getDetails().entrySet()) 78 | .flatMap(Collection::stream) 79 | .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue))); 80 | } 81 | 82 | @Override 83 | public @NotNull String status() { 84 | Health health = health(); 85 | return "health status: " + (health.healthy ? "healthy" : "unhealthy=" + health); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdTomcatStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static java.lang.invoke.MethodHandles.lookup; 20 | import static org.slf4j.LoggerFactory.getLogger; 21 | 22 | import java.util.LinkedList; 23 | import java.util.List; 24 | import java.util.Objects; 25 | import java.util.Set; 26 | import java.util.stream.Collectors; 27 | import javax.management.MBeanAttributeInfo; 28 | import javax.management.MBeanServer; 29 | import javax.management.ObjectName; 30 | import org.apache.tomcat.util.modeler.Registry; 31 | import org.jetbrains.annotations.NotNull; 32 | import org.slf4j.Logger; 33 | import org.springframework.core.annotation.Order; 34 | 35 | /** 36 | * Implementation of {@link SystemdStatusProvider} that provides information regarding Tomcat thread pools. 37 | * 38 | * @author Joao Silva 39 | * @see Registry 40 | */ 41 | @Order(1000) 42 | public class SystemdTomcatStatusProvider implements SystemdStatusProvider { 43 | 44 | private static final Logger logger = getLogger(lookup().lookupClass()); 45 | 46 | @NotNull 47 | private final MBeanServer mbeanServer; 48 | 49 | 50 | /** 51 | * Create a new SystemdTomcatStatusProvider. 52 | */ 53 | public SystemdTomcatStatusProvider() { 54 | mbeanServer = Objects.requireNonNull(Registry.getRegistry(null, null).getMBeanServer(), "No usable MBeanServer instance"); 55 | } 56 | 57 | @Override 58 | public @NotNull String status() { 59 | List statuses = new LinkedList<>(); 60 | Set objectNames; 61 | try { 62 | objectNames = mbeanServer.queryNames(new ObjectName("Tomcat:type=ThreadPool,*"), null); 63 | } catch (Exception e) { 64 | logger.warn("Could not get request processors from mBean server", e); 65 | return ""; 66 | } 67 | 68 | for (ObjectName objectName : objectNames) { 69 | try { 70 | ConnectorStatus status = new ConnectorStatus(); 71 | 72 | for (MBeanAttributeInfo attr : mbeanServer.getMBeanInfo(objectName).getAttributes()) { 73 | if (!attr.isReadable()) { 74 | continue; 75 | } 76 | 77 | String name = attr.getName(); 78 | try { 79 | switch (name) { 80 | case "name" -> status.name = (String) mbeanServer.getAttribute(objectName, name); 81 | case "currentThreadsBusy" -> status.currentThreadsBusy = (int) mbeanServer.getAttribute(objectName, name); 82 | case "currentThreadCount" -> status.currentThreadCount = (int) mbeanServer.getAttribute(objectName, name); 83 | default -> { 84 | } 85 | } 86 | } catch (Throwable t) { 87 | logger.warn("Could not read attribute {}", name, t); 88 | } 89 | } 90 | statuses.add(status); 91 | } catch (Exception e) { 92 | logger.warn("Could not read object {}", objectName, e); 93 | } 94 | } 95 | return statuses.stream().map(ConnectorStatus::toString).collect(Collectors.joining(", ")); 96 | } 97 | 98 | private static class ConnectorStatus { 99 | 100 | private String name; 101 | private int currentThreadsBusy; 102 | private int currentThreadCount; 103 | 104 | @Override 105 | public String toString() { 106 | return String.format("%s: %d/%d", name, currentThreadsBusy, currentThreadCount); 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdApplicationContextStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import java.util.Arrays; 20 | import java.util.HashSet; 21 | import java.util.Objects; 22 | import java.util.Set; 23 | import java.util.function.Predicate; 24 | import java.util.stream.Collectors; 25 | import org.jetbrains.annotations.NotNull; 26 | import org.jetbrains.annotations.Nullable; 27 | import org.springframework.beans.BeansException; 28 | import org.springframework.beans.factory.config.BeanPostProcessor; 29 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 30 | import org.springframework.core.annotation.Order; 31 | 32 | /** 33 | * Implementation of {@link SystemdStatusProvider} that provides information regarding the bean creation progress of a bean factory. 34 | * 35 | * @author Joao Silva 36 | * @see BeanPostProcessor 37 | */ 38 | @Order(-4000) 39 | public class SystemdApplicationContextStatusProvider implements SystemdStatusProvider, BeanPostProcessor { 40 | 41 | @NotNull 42 | private final Systemd systemd; 43 | private final int applicationId; 44 | private final String contextId; 45 | private final ConfigurableListableBeanFactory factory; 46 | private boolean definitionsLoaded = false; 47 | @NotNull 48 | private Set definitions = new HashSet<>(); 49 | @NotNull 50 | private String status = ""; 51 | 52 | /** 53 | * Creates a new instance using the provided {@link Systemd} as the integration point. 54 | * 55 | *

This constructor automatically registers the created instance as a status provider in the {@link Systemd} passed as a parameter. 56 | * 57 | * @param systemd the {@link Systemd} to send status information to 58 | */ 59 | SystemdApplicationContextStatusProvider(@SuppressWarnings("SameParameterValue") @NotNull Systemd systemd, int applicationId, String contextId, 60 | ConfigurableListableBeanFactory factory) { 61 | this.systemd = Objects.requireNonNull(systemd, "Systemd must not be null"); 62 | this.applicationId = applicationId; 63 | this.contextId = contextId; 64 | this.factory = factory; 65 | this.systemd.addStatusProviders(this); 66 | } 67 | 68 | private void ensureDefinitionsLoaded() { 69 | if (!definitionsLoaded) { 70 | definitions = Arrays.stream(factory.getBeanDefinitionNames()) 71 | .filter(isSingleton(factory)) 72 | .collect(Collectors.toSet()); 73 | definitionsLoaded = true; 74 | } 75 | } 76 | 77 | private long getSingletonCount() { 78 | return definitions.stream() 79 | .filter(factory::containsSingleton) 80 | .count(); 81 | } 82 | 83 | private Predicate isSingleton(ConfigurableListableBeanFactory factory) { 84 | return beanName -> factory.getBeanDefinition(beanName).isSingleton(); 85 | } 86 | 87 | @Override 88 | public @NotNull String status() { 89 | return systemd.isReady() ? "" : status; 90 | } 91 | 92 | @Override 93 | public @Nullable Object postProcessBeforeInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { 94 | if (factory != null) { 95 | ensureDefinitionsLoaded(); 96 | status = String.format("Application %d (%s): creating bean %d of %d", applicationId, contextId, getSingletonCount(), definitions.size()); 97 | systemd.extendTimeout(); 98 | systemd.updateStatus(); 99 | } 100 | return bean; 101 | } 102 | 103 | @Override 104 | public @Nullable Object postProcessAfterInitialization(@NotNull Object bean, @NotNull String beanName) throws BeansException { 105 | return bean; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdUtilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static java.lang.invoke.MethodHandles.lookup; 20 | import static org.slf4j.LoggerFactory.getLogger; 21 | 22 | import java.nio.file.Path; 23 | import org.slf4j.Logger; 24 | 25 | /** 26 | * Generic utilities for the systemd library. Not meant to be used directly or instantiated. 27 | * 28 | * @author Joao Silva 29 | */ 30 | abstract class SystemdUtilities { 31 | 32 | private static final Logger logger = getLogger(lookup().lookupClass()); 33 | private static final String notifySocket = System.getenv("NOTIFY_SOCKET"); 34 | 35 | private static final Path notifySocketPath; 36 | 37 | static { 38 | if (notifySocket != null) { 39 | notifySocketPath = Path.of(notifySocket); 40 | } else { 41 | notifySocketPath = null; 42 | } 43 | } 44 | 45 | private static final long watchdogUsec = readWatchdogUsec(); 46 | 47 | private SystemdUtilities() { 48 | } 49 | 50 | private static long readWatchdogUsec() { 51 | String watchdogUsec = System.getenv("WATCHDOG_USEC"); 52 | if (isNotEmpty(watchdogUsec)) { 53 | try { 54 | return Long.parseLong(watchdogUsec); 55 | } catch (NumberFormatException e) { 56 | logger.warn("Value of environment property WATCHDOG_USEC cannot be read as a number - watchdog disabled: " + watchdogUsec); 57 | } 58 | } 59 | return 0; 60 | } 61 | 62 | private static boolean isNotEmpty(String input) { 63 | return input != null && !input.isEmpty(); 64 | } 65 | 66 | /** 67 | * Allows determining if the process is running under systemd. 68 | * 69 | *

A process is said to be running under systemd if

  1. the operating system name contains {@code linux}.
  2. an environment property {@code 70 | * NOTIFY_SOCKET} exists
  3. {@link SystemdNotify} was able connect to the socket
71 | * 72 | * @return {@code true} if the process is running under systemd 73 | */ 74 | static boolean isUnderSystemd() { 75 | return SystemdNotify.usable(); 76 | } 77 | 78 | /** 79 | * Allows determining the current OS name. 80 | * 81 | * @return the contents of the system property {@code os.name} 82 | */ 83 | static String osName() { 84 | return System.getProperty("os.name"); 85 | } 86 | 87 | /** 88 | * Allows determining the current systemd notify socket. 89 | * 90 | * @return the {@link Path} corresponding the contents of the environment variable {@code NOTIFY_SOCKET}, or null if the environment variable is 91 | * not set 92 | */ 93 | static Path notifySocketPath() { 94 | return notifySocketPath; 95 | } 96 | 97 | /** 98 | * Allows determining the current watchdog keep-alive. 99 | * 100 | * @return the contents of the environment property {@code WATCHDOG_USEC} as a long, or 0 if undefined, empty or unparseable 101 | */ 102 | static long watchdogUsec() { 103 | return watchdogUsec; 104 | } 105 | 106 | static boolean isLinux() { 107 | return osName().toLowerCase().startsWith("linux"); 108 | } 109 | 110 | static boolean hasNotifySocket() { 111 | return notifySocketPath != null && notifySocketPath.toFile().exists(); 112 | } 113 | 114 | private static final String[] unitPrefixes = new String[]{"", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei"}; 115 | 116 | static String formatByteCount(long bytes) { 117 | long abs = Math.abs(bytes); 118 | if (bytes == Long.MIN_VALUE) { 119 | abs = Long.MAX_VALUE; 120 | } 121 | 122 | int divider = 0; 123 | int index = 0; 124 | long remainder = abs; 125 | while (remainder >> divider >= 1024) { 126 | divider += 10; 127 | index += 1; 128 | } 129 | 130 | double division = abs / Long.valueOf(1L << divider).doubleValue() * Math.signum(bytes); 131 | return String.format("%,.1f %sB", division, unitPrefixes[index]); 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdHealthProviderProperties.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2020 TomTom N.V. 3 | * Copyright 2023 Joao Silva 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | package com.github.jpmsilva.jsystemd; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Objects; 23 | import org.jetbrains.annotations.NotNull; 24 | import org.jetbrains.annotations.Nullable; 25 | import org.springframework.boot.actuate.health.Status; 26 | import org.springframework.boot.context.properties.ConfigurationProperties; 27 | 28 | /** 29 | * Properties class for systemd integration when using Spring Boot Actuator. 30 | * 31 | * @author Christian Lorenz 32 | */ 33 | @ConfigurationProperties(prefix = "systemd.health-provider") 34 | public class SystemdHealthProviderProperties { 35 | 36 | SystemdHealthProviderProperties() { 37 | } 38 | 39 | /** 40 | * Enable integration between Spring Boot Actuator health status and systemd watchdog. 41 | */ 42 | private boolean enabled; 43 | 44 | /** 45 | * Status codes from {@link Status} to consider as unhealthy. If omitted {@link Status#DOWN} is used. 46 | */ 47 | @NotNull 48 | private List unhealthyStatusCodes = initStatusCodes(); 49 | 50 | /** 51 | * Delay reporting unhealthy status to systemd watchdog. This parameter is provided in milliseconds, and may be null to disable the delay. 52 | */ 53 | @Nullable 54 | private Long unhealthyPendingPeriodMs; 55 | 56 | private static List initStatusCodes() { 57 | List list = new ArrayList<>(1); 58 | list.add(Status.DOWN.getCode()); 59 | return list; 60 | } 61 | 62 | /** 63 | * Check if the integration between Spring Boot Actuator health status and systemd watchdog is enabled. 64 | * 65 | * @return true if the integration between Spring Boot Actuator health status and systemd watchdog is enabled 66 | */ 67 | public boolean isEnabled() { 68 | return enabled; 69 | } 70 | 71 | /** 72 | * Enable integration between Spring Boot Actuator health status and systemd watchdog. 73 | * 74 | * @param enabled true to enable the integration between Spring Boot Actuator health status and systemd watchdog 75 | */ 76 | public void setEnabled(boolean enabled) { 77 | this.enabled = enabled; 78 | } 79 | 80 | /** 81 | * Check the status codes from {@link Status} to consider as unhealthy. 82 | * 83 | * @return the status codes from {@link Status} to consider as unhealthy 84 | * @see #setUnhealthyStatusCodes(List) 85 | */ 86 | public @NotNull List getUnhealthyStatusCodes() { 87 | return unhealthyStatusCodes; 88 | } 89 | 90 | /** 91 | * Set the status codes from {@link Status} to consider as unhealthy. If omitted {@link Status#DOWN} is used. 92 | * 93 | * @param unhealthyStatusCodes the status codes from {@link Status} to consider as unhealthy 94 | */ 95 | public void setUnhealthyStatusCodes(@NotNull List unhealthyStatusCodes) { 96 | Objects.requireNonNull(unhealthyStatusCodes, "Unhealthy status codes must not be null"); 97 | this.unhealthyStatusCodes = unhealthyStatusCodes; 98 | } 99 | 100 | /** 101 | * Check the delay reporting unhealthy status to systemd watchdog. 102 | * 103 | * @return the delay reporting unhealthy status to systemd watchdog 104 | * @see #setUnhealthyPendingPeriodMs(Long) 105 | */ 106 | public @Nullable Long getUnhealthyPendingPeriodMs() { 107 | return unhealthyPendingPeriodMs; 108 | } 109 | 110 | /** 111 | * Set the delay reporting unhealthy status to systemd watchdog. This parameter is provided in milliseconds, and may be null to disable the 112 | * delay. 113 | * 114 | * @param unhealthyPendingPeriodMs the delay reporting unhealthy status to systemd watchdog 115 | */ 116 | public void setUnhealthyPendingPeriodMs(@Nullable Long unhealthyPendingPeriodMs) { 117 | this.unhealthyPendingPeriodMs = unhealthyPendingPeriodMs; 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdSpringApplicationRunListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.isUnderSystemd; 20 | import static java.util.Objects.requireNonNull; 21 | import static java.util.concurrent.TimeUnit.MICROSECONDS; 22 | import static java.util.concurrent.TimeUnit.SECONDS; 23 | 24 | import com.github.jpmsilva.jsystemd.SystemdApplicationRunStatusProvider.ApplicationState; 25 | import java.time.Duration; 26 | import org.jetbrains.annotations.NotNull; 27 | import org.jetbrains.annotations.Nullable; 28 | import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; 29 | import org.springframework.boot.ConfigurableBootstrapContext; 30 | import org.springframework.boot.SpringApplication; 31 | import org.springframework.boot.SpringApplicationRunListener; 32 | import org.springframework.context.ConfigurableApplicationContext; 33 | import org.springframework.core.env.ConfigurableEnvironment; 34 | 35 | /** 36 | * A Spring Application Run Listener that sets up a {@link SystemdApplicationRunStatusProvider} to provide status updates of the current phase of the 37 | * application life cycle. 38 | * 39 | * @author Joao Silva 40 | * @see SpringApplicationRunListener 41 | */ 42 | public class SystemdSpringApplicationRunListener implements SpringApplicationRunListener { 43 | 44 | private static final String SYSTEMD_BEAN_NAME = "systemd"; 45 | 46 | private final Systemd systemd = createSystemd(); 47 | 48 | private final int applicationId; 49 | @Nullable 50 | private SystemdApplicationRunStatusProvider provider; 51 | 52 | /** 53 | * Mandatory constructor of SpringApplicationRunListener. 54 | * 55 | * @param springApplication the current Spring Application 56 | * @param args the arguments passed to the Spring Application 57 | */ 58 | @SuppressWarnings({"PMD.UnusedFormalParameter", "unused"}) 59 | public SystemdSpringApplicationRunListener(@NotNull SpringApplication springApplication, String[] args) { 60 | applicationId = requireNonNull(springApplication, "Spring application must not be null").hashCode(); 61 | if (isUnderSystemd()) { 62 | provider = new SystemdApplicationRunStatusProvider(requireNonNull(systemd, "Systemd must not be null"), applicationId); 63 | } 64 | } 65 | 66 | private static Systemd createSystemd() { 67 | if (isUnderSystemd()) { 68 | return Systemd.builder().watchdog(SystemdUtilities.watchdogUsec() / 2, MICROSECONDS).statusUpdate(5, SECONDS).build(); 69 | } else { 70 | return null; 71 | } 72 | } 73 | 74 | @Override 75 | public void starting(ConfigurableBootstrapContext bootstrapContext) { 76 | if (provider != null) { 77 | provider.state(ApplicationState.STARTING); 78 | } 79 | } 80 | 81 | @Override 82 | public void environmentPrepared(ConfigurableBootstrapContext bootstrapContext, ConfigurableEnvironment environment) { 83 | if (provider != null) { 84 | provider.state(ApplicationState.ENVIRONMENT_PREPARED); 85 | } 86 | } 87 | 88 | @Override 89 | public void contextPrepared(ConfigurableApplicationContext context) { 90 | if (provider != null) { 91 | provider.state(ApplicationState.CONTEXT_PREPARED); 92 | 93 | ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); 94 | if (!beanFactory.containsSingleton(SYSTEMD_BEAN_NAME)) { 95 | beanFactory.registerSingleton(SYSTEMD_BEAN_NAME, requireNonNull(systemd)); 96 | } 97 | beanFactory.registerSingleton("systemdApplicationContextStatusProvider", 98 | new SystemdApplicationContextStatusProvider(requireNonNull(systemd), applicationId, context.getId(), beanFactory)); 99 | } 100 | } 101 | 102 | @Override 103 | public void contextLoaded(ConfigurableApplicationContext context) { 104 | if (provider != null) { 105 | provider.state(ApplicationState.CONTEXT_LOADED); 106 | } 107 | } 108 | 109 | @Override 110 | public void started(ConfigurableApplicationContext context, Duration timeTaken) { 111 | if (provider != null) { 112 | provider.state(ApplicationState.STARTED, timeTaken); 113 | } 114 | } 115 | 116 | @Override 117 | public void ready(ConfigurableApplicationContext context, Duration timeTaken) { 118 | if (provider != null) { 119 | provider.state(ApplicationState.READY, timeTaken); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdApplicationRunStatusProvider.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import java.time.Duration; 20 | import org.jetbrains.annotations.NotNull; 21 | import org.springframework.boot.ConfigurableBootstrapContext; 22 | import org.springframework.context.ConfigurableApplicationContext; 23 | import org.springframework.core.annotation.Order; 24 | import org.springframework.core.env.ConfigurableEnvironment; 25 | 26 | /** 27 | * Implementation of {@link SystemdStatusProvider} that provides information regarding the Spring Boot application startup sequence state. 28 | * 29 | * @author Joao Silva 30 | * @see SystemdSpringApplicationRunListener 31 | * @see org.springframework.boot.SpringApplicationRunListener 32 | */ 33 | @Order(-5000) 34 | public class SystemdApplicationRunStatusProvider implements SystemdStatusProvider { 35 | 36 | @NotNull 37 | private final Systemd systemd; 38 | private final int applicationId; 39 | @NotNull 40 | private String status = ""; 41 | 42 | /** 43 | * Creates a new instance using the provided {@link Systemd} as the integration point. 44 | * 45 | *

This constructor automatically registers the created instance as a status provider in the {@link Systemd} passed as a parameter. 46 | * 47 | * @param systemd the {@link Systemd} to send status information to 48 | */ 49 | SystemdApplicationRunStatusProvider(@SuppressWarnings("SameParameterValue") @NotNull Systemd systemd, int applicationId) { 50 | this.systemd = systemd; 51 | this.applicationId = applicationId; 52 | this.systemd.addStatusProviders(0, this); 53 | } 54 | 55 | @Override 56 | public @NotNull String status() { 57 | return systemd.isReady() ? "" : status; 58 | } 59 | 60 | /** 61 | * Updates the current application startup sequence state of the application, sending the update to systemd as well. 62 | * 63 | * @param state the current application startup sequence state 64 | */ 65 | void state(@NotNull ApplicationState state) { 66 | status = String.format("Application %d state: %s", applicationId, state.toString().toLowerCase().replace("_", " ")); 67 | systemd.extendTimeout(); 68 | systemd.updateStatus(); 69 | } 70 | 71 | /** 72 | * Updates the current application startup sequence state of the application, sending the update to systemd as well. 73 | * 74 | * @param state the current application startup sequence state 75 | * @param timeTaken the time taken for the application to reach this state 76 | */ 77 | public void state(@NotNull ApplicationState state, @NotNull Duration timeTaken) { 78 | status = String.format("Application %d state: %s, time taken: %s", applicationId, state.toString().toLowerCase().replace("_", " "), 79 | formatDuration(timeTaken)); 80 | systemd.extendTimeout(); 81 | systemd.updateStatus(); 82 | } 83 | 84 | private String formatDuration(Duration duration) { 85 | long hours = duration.toHours(); 86 | int minutes = duration.toMinutesPart(); 87 | int seconds = duration.toSecondsPart(); 88 | if (hours > 0) { 89 | return String.format("%dh%02dm%02ds", hours, minutes, seconds); 90 | } else if (minutes > 0) { 91 | return String.format("%dm%02ds", minutes, seconds); 92 | } else { 93 | return String.format("%ds", seconds); 94 | } 95 | } 96 | 97 | /** 98 | * Enumeration of supported application startup sequence state. 99 | */ 100 | public enum ApplicationState { 101 | 102 | /** 103 | * The application is starting. 104 | * 105 | * @see org.springframework.boot.SpringApplicationRunListener#starting(ConfigurableBootstrapContext) 106 | */ 107 | STARTING, 108 | 109 | /** 110 | * The application environment is prepared. 111 | * 112 | * @see org.springframework.boot.SpringApplicationRunListener#environmentPrepared(ConfigurableBootstrapContext, ConfigurableEnvironment) 113 | */ 114 | ENVIRONMENT_PREPARED, 115 | 116 | /** 117 | * The application context is prepared. 118 | * 119 | * @see org.springframework.boot.SpringApplicationRunListener#contextPrepared(ConfigurableApplicationContext) 120 | */ 121 | CONTEXT_PREPARED, 122 | 123 | /** 124 | * The application context is loaded. 125 | * 126 | * @see org.springframework.boot.SpringApplicationRunListener#contextLoaded(ConfigurableApplicationContext) 127 | */ 128 | CONTEXT_LOADED, 129 | 130 | /** 131 | * The application is started. 132 | * 133 | * @see org.springframework.boot.SpringApplicationRunListener#started(ConfigurableApplicationContext, Duration) 134 | */ 135 | STARTED, 136 | 137 | /** 138 | * The application is ready. 139 | * 140 | * @see org.springframework.boot.SpringApplicationRunListener#ready(ConfigurableApplicationContext, Duration) 141 | */ 142 | READY 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /src/site/markdown/status-providers.md: -------------------------------------------------------------------------------- 1 | # Status providers 2 | 3 | ## Under Spring Boot 4 | 5 | When using the autoconfiguration class, [SystemdNotifyStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdNotifyStatusProvider.html) beans 6 | will also be searched in the application context and used to compose an extended status message that systemd will display 7 | when using the `status` verb. 8 | 9 | Out of the box this module will show memory (heap/non-heap), classloader information, application startup sequence state and bean creation progress. 10 | 11 | Additionally, if running with the embedded Tomcat container, status regarding Tomcat's connectors will also be displayed. 12 | 13 | ``` 14 | [root@machine ~]# systemctl status myservice.service 15 | ● myservice.service - My Spring Boot Service 16 | Loaded: loaded (/etc/systemd/system/myservice.service; disabled; vendor preset: disabled) 17 | Active: active (running) since Qui 2018-02-15 10:19:28 WET; 1h 24min ago 18 | Main PID: 11142 (java) 19 | Status: "Heap: 139.5 MiB/256 MiB, Non-heap: 62.7 MiB/64.1 MiB, Classes: 7915" 20 | CGroup: /system.slice/myservice.service 21 | └─11142 /opt/jdk17/bin/java -XX:+ExitOnOutOfMemoryError -jar /opt/myservice/myservice.jar 22 | ``` 23 | 24 | ## Regular Java application 25 | 26 | After creating the [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html) instance to interface with the service supervisor, register 27 | any instance of [SystemdNotifyStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdNotifyStatusProvider.html) 28 | with [addStatusProviders](apidocs/com/github/jpmsilva/jsystemd/Systemd.html#addStatusProviders-com.github.jpmsilva.jsystemd.SystemdStatusProvider...-). 29 | 30 | The following table lists the provided implementations: 31 | 32 | | Class | Purpose | Sample | 33 | |------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------|-------------------------------| 34 | | [SystemdHeapStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdHeapStatusProvider.html) | Provides information regarding heap memory status | `Heap: 139.5 MiB/256 MiB` | 35 | | [SystemdNonHeapStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdNonHeapStatusProvider.html) | Provides information regarding non heap memory status | `Non-heap: 62.7 MiB/64.1 MiB` | 36 | | [SystemdClassLoaderStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdClassLoaderStatusProvider.html) | Provides information regarding the number of loaded classes | `Classes: 7915` | 37 | | [SystemdApplicationRunStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdApplicationRunStatusProvider.html) | Provides information regarding the application startup sequence state | `State: context prepared` | 38 | | [SystemdApplicationContextStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdApplicationContextStatusProvider.html) | Provides information regarding the bean creation status of the application context | `Creating bean 94 of 472` | 39 | | [SystemdTomcatStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdTomcatStatusProvider.html) | Provides information regarding Tomcat's connectors | `http-nio-8080: 2/10` | 40 | 41 | See the [Startup progress](startup-progress.html) page for more information 42 | regarding `SystemdApplicationRunStatusProvider` and `SystemdApplicationContextStatusProvider`. 43 | 44 | See the [Tomcat status](tomcat-status.html) page for more information regarding `SystemdTomcatStatusProvider`. 45 | 46 | ## Custom status providers 47 | 48 | You can create your own status information extending [SystemdNotifyStatusProvider](apidocs/com/github/jpmsilva/jsystemd/SystemdNotifyStatusProvider.html), 49 | and provide an instance of such custom classes to systemd 50 | 51 | * if using your own managed instance of [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html), 52 | by using [addStatusProviders](apidocs/com/github/jpmsilva/jsystemd/Systemd.html#addStatusProviders-com.github.jpmsilva.jsystemd.SystemdStatusProvider...-) 53 | * under Spring Boot, by registering it as a bean in any configuration class: 54 | 55 | ```java 56 | @Configuration 57 | @ConditionalOnSystemD 58 | public class MyConfiguration { 59 | 60 | @Bean 61 | MyStatusProvider myStatusProvider() { 62 | return new MyStatusProvider(); 63 | } 64 | } 65 | ``` 66 | 67 | In the example above, the configuration class does not need to be annotated with [ConditionalOnSystemd](apidocs/com/github/jpmsilva/jsystemd/ConditionalOnSystemd.html), because 68 | these beans are harmless even when not running under systemd. However, this was these extra resources do not have to be built at all. 69 | Check the [Conditionals](conditionals.html) page for more information. 70 | 71 | Under Spring Boot you can also autowire the current instance of [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html) and programmatically manage it: 72 | 73 | ```java 74 | @Configuration 75 | @ConditionalOnSystemD 76 | public class MyConfiguration { 77 | 78 | @Autowire 79 | MyConfiguration(Systemd systemd) { 80 | // Force update current service status 81 | systemd.updateStatus(); 82 | } 83 | } 84 | ``` 85 | 86 | Out of the box, [Systemd](apidocs/com/github/jpmsilva/jsystemd/Systemd.html) sends status updates to the supervisor daemon once every five seconds. 87 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/SystemdNotify.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.hasNotifySocket; 20 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.isLinux; 21 | import static java.lang.invoke.MethodHandles.lookup; 22 | import static java.util.Objects.requireNonNull; 23 | import static org.slf4j.LoggerFactory.getLogger; 24 | 25 | import com.sun.jna.Native; 26 | import java.net.UnixDomainSocketAddress; 27 | import org.jetbrains.annotations.NotNull; 28 | import org.slf4j.Logger; 29 | 30 | /** 31 | * Low level API that interfaces with systemd through a {@link UnixDomainSocketAddress}. Not meant for direct usage. 32 | * 33 | * @author Joao Silva 34 | * @see sd_notify 35 | */ 36 | public class SystemdNotify { 37 | 38 | private static final Logger logger = getLogger(lookup().lookupClass()); 39 | 40 | /** 41 | * Allows knowing if this library is usable under current execution conditions (operating system type, systemd available, etc...). 42 | * 43 | * @return {@code true} if and only if the library can be used 44 | */ 45 | static boolean usable() { 46 | return Library.initialized; 47 | } 48 | 49 | /** 50 | * Notifies systemd that the program has completed startup. 51 | * 52 | * @see ready 53 | */ 54 | static void ready() { 55 | if (usable()) { 56 | logger.info("Notifying systemd that service is ready"); 57 | invoke("READY=1"); 58 | } 59 | } 60 | 61 | /** 62 | * Notifies systemd about the current status of the program. 63 | * 64 | * @see status 65 | */ 66 | static void status(@NotNull String message) { 67 | if (usable()) { 68 | logger.debug("Notifying systemd that service status is {}", requireNonNull(message, "Message must not be null")); 69 | invoke("STATUS=" + message); 70 | } 71 | } 72 | 73 | /** 74 | * Notifies systemd to extend the startup or shutdown timeout for the specified microseconds. Available in systemd versions 236 and above. 75 | * 76 | * @see extend timeout 77 | * @see news 78 | */ 79 | static void extendTimeout(long timeout) { 80 | if (usable()) { 81 | logger.debug("Extending startup timeout with {} microseconds", timeout); 82 | invoke("EXTEND_TIMEOUT_USEC=" + timeout); 83 | } 84 | } 85 | 86 | /** 87 | * Notifies systemd that the program is still alive and well. 88 | * 89 | * @see watchdog 90 | */ 91 | static void watchdog() { 92 | if (usable()) { 93 | logger.debug("Updating watchdog timestamp"); 94 | invoke("WATCHDOG=1"); 95 | } 96 | } 97 | 98 | /** 99 | * Notifies systemd that the program is stopping. 100 | * 101 | * @see stopping 102 | */ 103 | static void stopping() { 104 | if (usable()) { 105 | logger.info("Notifying systemd that service is stopping"); 106 | invoke("STOPPING=1"); 107 | } 108 | } 109 | 110 | /** 111 | * Low level method that sends the {@code sd_notify} formatted message to systemd. 112 | * 113 | * @param message the message to send, according to specification 114 | */ 115 | private static void invoke(String message) { 116 | if (usable() && message != null && !message.isEmpty()) { 117 | Library.sd_notify(0, message); 118 | } 119 | } 120 | 121 | /** 122 | * Registers a JVM shutdown hook that closes the systemd integration channel. 123 | * 124 | * @see Runtime#addShutdownHook(Thread) 125 | * @see #close() 126 | */ 127 | public static void registerShutdownHook() { 128 | Runtime.getRuntime().addShutdownHook(new Thread(SystemdNotify::close)); 129 | } 130 | 131 | /** 132 | * Attempts to orderly close the systemd integration channel. 133 | * 134 | *

Normally, the integration channel will be closed when the JVM shuts down.
135 | * However, you may wish of explicitly close the integration channel, to ensure that all closeable resources are effectively closed.
As such, this method 136 | * should only be called at most once during the lifecycle of the JVM. 137 | * 138 | *

Currently this does nothing, as currently the only supported integration channel is the native libsystemd library, which does not need any cleanup. 139 | */ 140 | public static void close() { 141 | } 142 | 143 | private static class Library { 144 | 145 | private static boolean initialized = false; 146 | 147 | static { 148 | if (isLinux() && hasNotifySocket()) { 149 | try { 150 | Native.register("systemd"); 151 | initialized = true; 152 | } catch (UnsatisfiedLinkError ignored) { 153 | } 154 | } 155 | } 156 | 157 | @SuppressWarnings({"UnusedReturnValue", "checkstyle:ParameterName"}) 158 | public static native int sd_notify(int unset_environment, String state); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /jsystemd-spring-boot-starter/src/main/java/com/github/jpmsilva/jsystemd/SystemdAutoConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static java.util.Collections.emptyList; 20 | import static java.util.Objects.requireNonNull; 21 | 22 | import java.time.temporal.ChronoUnit; 23 | import java.util.ArrayList; 24 | import java.util.HashSet; 25 | import java.util.List; 26 | import java.util.Optional; 27 | import java.util.Set; 28 | import java.util.stream.Collectors; 29 | import org.apache.catalina.startup.Tomcat; 30 | import org.jetbrains.annotations.NotNull; 31 | import org.springframework.beans.factory.ObjectProvider; 32 | import org.springframework.beans.factory.annotation.Autowired; 33 | import org.springframework.boot.actuate.health.HealthIndicator; 34 | import org.springframework.boot.actuate.health.Status; 35 | import org.springframework.boot.autoconfigure.AutoConfiguration; 36 | import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; 37 | import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 38 | import org.springframework.boot.availability.AvailabilityChangeEvent; 39 | import org.springframework.boot.availability.ReadinessState; 40 | import org.springframework.boot.context.properties.EnableConfigurationProperties; 41 | import org.springframework.context.ConfigurableApplicationContext; 42 | import org.springframework.context.annotation.Bean; 43 | import org.springframework.context.annotation.Configuration; 44 | import org.springframework.context.event.EventListener; 45 | import org.springframework.core.annotation.AnnotationAwareOrderComparator; 46 | 47 | /** 48 | * Autoconfiguration class for systemd integration. 49 | * 50 | *

Sets up some basic {@link SystemdStatusProvider} as well. 51 | * 52 | * @author Joao Silva 53 | * @author Christian Lorenz 54 | */ 55 | @AutoConfiguration 56 | @ConditionalOnSystemd 57 | public class SystemdAutoConfiguration { 58 | 59 | @NotNull 60 | private final Systemd systemd; 61 | 62 | @Autowired 63 | SystemdAutoConfiguration(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @NotNull Systemd systemd) { 64 | this.systemd = requireNonNull(systemd, "Systemd must not be null"); 65 | } 66 | 67 | /** 68 | * Event listener for the {@link AvailabilityChangeEvent} event to report to systemd that the service is ready. 69 | * 70 | *

The application is considered ready when the event is a {@link ReadinessState} with the state {@link ReadinessState#ACCEPTING_TRAFFIC}. 71 | * 72 | * @param event the {@link AvailabilityChangeEvent} received 73 | */ 74 | @EventListener 75 | public void started(@SuppressWarnings("unused") AvailabilityChangeEvent event) { 76 | if (event.getState() == ReadinessState.ACCEPTING_TRAFFIC) { 77 | systemd.ready(); 78 | } 79 | } 80 | 81 | @Bean 82 | @NotNull 83 | SystemdLifecycle systemdLifecycle() { 84 | return new SystemdLifecycle(systemd); 85 | } 86 | 87 | @Bean 88 | @NotNull 89 | SystemdStatusProvider systemdNotifyHeapStatus() { 90 | return new SystemdHeapStatusProvider(); 91 | } 92 | 93 | @Bean 94 | @NotNull 95 | SystemdStatusProvider systemdNotifyNonHeapStatus() { 96 | return new SystemdNonHeapStatusProvider(); 97 | } 98 | 99 | @Bean 100 | @NotNull 101 | SystemdStatusProvider systemdNotifyClassLoaderStatus() { 102 | return new SystemdClassLoaderStatusProvider(); 103 | } 104 | 105 | @Configuration 106 | @ConditionalOnSystemd 107 | static class SystemdStatusProviderConfiguration { 108 | 109 | @Autowired 110 | SystemdStatusProviderConfiguration(@SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @NotNull Systemd systemd, 111 | @NotNull ConfigurableApplicationContext applicationContext, @NotNull ObjectProvider> statuses) { 112 | requireNonNull(systemd, "Systemd must not be null"); 113 | requireNonNull(applicationContext, "Application context must not be null"); 114 | requireNonNull(statuses, "Statuses must not be null"); 115 | 116 | Set uniqueProviders = new HashSet<>(); 117 | uniqueProviders.addAll(Optional.ofNullable(statuses.getIfAvailable()).orElse(emptyList())); 118 | uniqueProviders.addAll(systemd.getStatusProviders()); 119 | 120 | List newProviders = new ArrayList<>(uniqueProviders); 121 | newProviders.sort(AnnotationAwareOrderComparator.INSTANCE); 122 | systemd.setStatusProviders(newProviders); 123 | } 124 | } 125 | 126 | /** 127 | * Autoconfiguration class for systemd integration when running under Tomcat. 128 | */ 129 | @Configuration 130 | @ConditionalOnSystemd 131 | @ConditionalOnClass(Tomcat.class) 132 | public static class SystemdAutoTomcatConfiguration { 133 | 134 | SystemdAutoTomcatConfiguration() { 135 | } 136 | 137 | @Bean 138 | @NotNull 139 | SystemdTomcatStatusProvider systemdTomcatStatusProvider() { 140 | return new SystemdTomcatStatusProvider(); 141 | } 142 | } 143 | 144 | /** 145 | * Autoconfiguration class for systemd integration when using Spring Boot Actuator. 146 | */ 147 | @Configuration 148 | @ConditionalOnSystemd 149 | @ConditionalOnClass(HealthIndicator.class) 150 | @ConditionalOnProperty(name = "enabled", prefix = "systemd.health-provider") 151 | @EnableConfigurationProperties(SystemdHealthProviderProperties.class) 152 | public static class SystemdAutoActuatorHealthConfiguration { 153 | 154 | SystemdAutoActuatorHealthConfiguration() { 155 | } 156 | 157 | @Bean 158 | @NotNull 159 | SystemdActuatorHealthProvider systemdActuatorHealthProvider( 160 | @SuppressWarnings("SpringJavaInjectionPointsAutowiringInspection") @NotNull Systemd systemd, 161 | @NotNull ObjectProvider> healthIndicatorsProvider, @NotNull SystemdHealthProviderProperties properties) { 162 | requireNonNull(systemd, "Systemd must not be null"); 163 | requireNonNull(healthIndicatorsProvider, "Health indicators provider must not be null"); 164 | requireNonNull(properties, "Properties must not be null"); 165 | 166 | List healthIndicators = Optional.ofNullable(healthIndicatorsProvider.getIfAvailable()).orElse(emptyList()); 167 | Set unhealthyStatusCodes = properties.getUnhealthyStatusCodes().stream().map(Status::new).collect(Collectors.toSet()); 168 | SystemdActuatorHealthProvider healthProvider = new SystemdActuatorHealthProvider(healthIndicators, unhealthyStatusCodes); 169 | if (properties.getUnhealthyPendingPeriodMs() != null) { 170 | systemd.setHealthProvider(new PendingHealthProvider(healthProvider, properties.getUnhealthyPendingPeriodMs(), ChronoUnit.MILLIS)); 171 | } else { 172 | systemd.setHealthProvider(healthProvider); 173 | } 174 | return healthProvider; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM http://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Apache Maven Wrapper startup batch script, version 3.2.0 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 28 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 29 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 30 | @REM e.g. to debug Maven itself, use 31 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 32 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 33 | @REM ---------------------------------------------------------------------------- 34 | 35 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 36 | @echo off 37 | @REM set title of command window 38 | title %0 39 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 40 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 41 | 42 | @REM set %HOME% to equivalent of $HOME 43 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 44 | 45 | @REM Execute a user defined script before this one 46 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 47 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 48 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 49 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 50 | :skipRcPre 51 | 52 | @setlocal 53 | 54 | set ERROR_CODE=0 55 | 56 | @REM To isolate internal variables from possible post scripts, we use another setlocal 57 | @setlocal 58 | 59 | @REM ==== START VALIDATION ==== 60 | if not "%JAVA_HOME%" == "" goto OkJHome 61 | 62 | echo. 63 | echo Error: JAVA_HOME not found in your environment. >&2 64 | echo Please set the JAVA_HOME variable in your environment to match the >&2 65 | echo location of your Java installation. >&2 66 | echo. 67 | goto error 68 | 69 | :OkJHome 70 | if exist "%JAVA_HOME%\bin\java.exe" goto init 71 | 72 | echo. 73 | echo Error: JAVA_HOME is set to an invalid directory. >&2 74 | echo JAVA_HOME = "%JAVA_HOME%" >&2 75 | echo Please set the JAVA_HOME variable in your environment to match the >&2 76 | echo location of your Java installation. >&2 77 | echo. 78 | goto error 79 | 80 | @REM ==== END VALIDATION ==== 81 | 82 | :init 83 | 84 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 85 | @REM Fallback to current working directory if not found. 86 | 87 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 88 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 89 | 90 | set EXEC_DIR=%CD% 91 | set WDIR=%EXEC_DIR% 92 | :findBaseDir 93 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 94 | cd .. 95 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 96 | set WDIR=%CD% 97 | goto findBaseDir 98 | 99 | :baseDirFound 100 | set MAVEN_PROJECTBASEDIR=%WDIR% 101 | cd "%EXEC_DIR%" 102 | goto endDetectBaseDir 103 | 104 | :baseDirNotFound 105 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 106 | cd "%EXEC_DIR%" 107 | 108 | :endDetectBaseDir 109 | 110 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 111 | 112 | @setlocal EnableExtensions EnableDelayedExpansion 113 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 114 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 115 | 116 | :endReadAdditionalConfig 117 | 118 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 123 | 124 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 125 | IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B 126 | ) 127 | 128 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 129 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 130 | if exist %WRAPPER_JAR% ( 131 | if "%MVNW_VERBOSE%" == "true" ( 132 | echo Found %WRAPPER_JAR% 133 | ) 134 | ) else ( 135 | if not "%MVNW_REPOURL%" == "" ( 136 | SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 137 | ) 138 | if "%MVNW_VERBOSE%" == "true" ( 139 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 140 | echo Downloading from: %WRAPPER_URL% 141 | ) 142 | 143 | powershell -Command "&{"^ 144 | "$webclient = new-object System.Net.WebClient;"^ 145 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 146 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 147 | "}"^ 148 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ 149 | "}" 150 | if "%MVNW_VERBOSE%" == "true" ( 151 | echo Finished downloading %WRAPPER_JAR% 152 | ) 153 | ) 154 | @REM End of extension 155 | 156 | @REM If specified, validate the SHA-256 sum of the Maven wrapper jar file 157 | SET WRAPPER_SHA_256_SUM="" 158 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 159 | IF "%%A"=="wrapperSha256Sum" SET WRAPPER_SHA_256_SUM=%%B 160 | ) 161 | IF NOT %WRAPPER_SHA_256_SUM%=="" ( 162 | powershell -Command "&{"^ 163 | "$hash = (Get-FileHash \"%WRAPPER_JAR%\" -Algorithm SHA256).Hash.ToLower();"^ 164 | "If('%WRAPPER_SHA_256_SUM%' -ne $hash){"^ 165 | " Write-Output 'Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised.';"^ 166 | " Write-Output 'Investigate or delete %WRAPPER_JAR% to attempt a clean download.';"^ 167 | " Write-Output 'If you updated your Maven version, you need to update the specified wrapperSha256Sum property.';"^ 168 | " exit 1;"^ 169 | "}"^ 170 | "}" 171 | if ERRORLEVEL 1 goto error 172 | ) 173 | 174 | @REM Provide a "standardized" way to retrieve the CLI args that will 175 | @REM work with both Windows and non-Windows executions. 176 | set MAVEN_CMD_LINE_ARGS=%* 177 | 178 | %MAVEN_JAVA_EXE% ^ 179 | %JVM_CONFIG_MAVEN_PROPS% ^ 180 | %MAVEN_OPTS% ^ 181 | %MAVEN_DEBUG_OPTS% ^ 182 | -classpath %WRAPPER_JAR% ^ 183 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 184 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 185 | if ERRORLEVEL 1 goto error 186 | goto end 187 | 188 | :error 189 | set ERROR_CODE=1 190 | 191 | :end 192 | @endlocal & set ERROR_CODE=%ERROR_CODE% 193 | 194 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 195 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 196 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 197 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 198 | :skipRcPost 199 | 200 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 201 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 202 | 203 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 204 | 205 | cmd /C exit /B %ERROR_CODE% 206 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Apache Maven Wrapper startup batch script, version 3.2.0 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | # e.g. to debug Maven itself, use 32 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | # ---------------------------------------------------------------------------- 35 | 36 | if [ -z "$MAVEN_SKIP_RC" ] ; then 37 | 38 | if [ -f /usr/local/etc/mavenrc ] ; then 39 | . /usr/local/etc/mavenrc 40 | fi 41 | 42 | if [ -f /etc/mavenrc ] ; then 43 | . /etc/mavenrc 44 | fi 45 | 46 | if [ -f "$HOME/.mavenrc" ] ; then 47 | . "$HOME/.mavenrc" 48 | fi 49 | 50 | fi 51 | 52 | # OS specific support. $var _must_ be set to either true or false. 53 | cygwin=false; 54 | darwin=false; 55 | mingw=false 56 | case "$(uname)" in 57 | CYGWIN*) cygwin=true ;; 58 | MINGW*) mingw=true;; 59 | Darwin*) darwin=true 60 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 61 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 62 | if [ -z "$JAVA_HOME" ]; then 63 | if [ -x "/usr/libexec/java_home" ]; then 64 | JAVA_HOME="$(/usr/libexec/java_home)"; export JAVA_HOME 65 | else 66 | JAVA_HOME="/Library/Java/Home"; export JAVA_HOME 67 | fi 68 | fi 69 | ;; 70 | esac 71 | 72 | if [ -z "$JAVA_HOME" ] ; then 73 | if [ -r /etc/gentoo-release ] ; then 74 | JAVA_HOME=$(java-config --jre-home) 75 | fi 76 | fi 77 | 78 | # For Cygwin, ensure paths are in UNIX format before anything is touched 79 | if $cygwin ; then 80 | [ -n "$JAVA_HOME" ] && 81 | JAVA_HOME=$(cygpath --unix "$JAVA_HOME") 82 | [ -n "$CLASSPATH" ] && 83 | CLASSPATH=$(cygpath --path --unix "$CLASSPATH") 84 | fi 85 | 86 | # For Mingw, ensure paths are in UNIX format before anything is touched 87 | if $mingw ; then 88 | [ -n "$JAVA_HOME" ] && [ -d "$JAVA_HOME" ] && 89 | JAVA_HOME="$(cd "$JAVA_HOME" || (echo "cannot cd into $JAVA_HOME."; exit 1); pwd)" 90 | fi 91 | 92 | if [ -z "$JAVA_HOME" ]; then 93 | javaExecutable="$(which javac)" 94 | if [ -n "$javaExecutable" ] && ! [ "$(expr "\"$javaExecutable\"" : '\([^ ]*\)')" = "no" ]; then 95 | # readlink(1) is not available as standard on Solaris 10. 96 | readLink=$(which readlink) 97 | if [ ! "$(expr "$readLink" : '\([^ ]*\)')" = "no" ]; then 98 | if $darwin ; then 99 | javaHome="$(dirname "\"$javaExecutable\"")" 100 | javaExecutable="$(cd "\"$javaHome\"" && pwd -P)/javac" 101 | else 102 | javaExecutable=$(readlink -f "$javaExecutable") 103 | fi 104 | javaHome=$(dirname "$javaExecutable") 105 | javaHome=$(expr "$javaHome" : '\(.*\)/bin') 106 | JAVA_HOME="$javaHome" 107 | export JAVA_HOME 108 | fi 109 | fi 110 | fi 111 | 112 | if [ -z "$JAVACMD" ] ; then 113 | if [ -n "$JAVA_HOME" ] ; then 114 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 115 | # IBM's JDK on AIX uses strange locations for the executables 116 | JAVACMD="$JAVA_HOME/jre/sh/java" 117 | else 118 | JAVACMD="$JAVA_HOME/bin/java" 119 | fi 120 | else 121 | JAVACMD="$(\unset -f command 2>/dev/null; \command -v java)" 122 | fi 123 | fi 124 | 125 | if [ ! -x "$JAVACMD" ] ; then 126 | echo "Error: JAVA_HOME is not defined correctly." >&2 127 | echo " We cannot execute $JAVACMD" >&2 128 | exit 1 129 | fi 130 | 131 | if [ -z "$JAVA_HOME" ] ; then 132 | echo "Warning: JAVA_HOME environment variable is not set." 133 | fi 134 | 135 | # traverses directory structure from process work directory to filesystem root 136 | # first directory with .mvn subdirectory is considered project base directory 137 | find_maven_basedir() { 138 | if [ -z "$1" ] 139 | then 140 | echo "Path not specified to find_maven_basedir" 141 | return 1 142 | fi 143 | 144 | basedir="$1" 145 | wdir="$1" 146 | while [ "$wdir" != '/' ] ; do 147 | if [ -d "$wdir"/.mvn ] ; then 148 | basedir=$wdir 149 | break 150 | fi 151 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 152 | if [ -d "${wdir}" ]; then 153 | wdir=$(cd "$wdir/.." || exit 1; pwd) 154 | fi 155 | # end of workaround 156 | done 157 | printf '%s' "$(cd "$basedir" || exit 1; pwd)" 158 | } 159 | 160 | # concatenates all lines of a file 161 | concat_lines() { 162 | if [ -f "$1" ]; then 163 | # Remove \r in case we run on Windows within Git Bash 164 | # and check out the repository with auto CRLF management 165 | # enabled. Otherwise, we may read lines that are delimited with 166 | # \r\n and produce $'-Xarg\r' rather than -Xarg due to word 167 | # splitting rules. 168 | tr -s '\r\n' ' ' < "$1" 169 | fi 170 | } 171 | 172 | log() { 173 | if [ "$MVNW_VERBOSE" = true ]; then 174 | printf '%s\n' "$1" 175 | fi 176 | } 177 | 178 | BASE_DIR=$(find_maven_basedir "$(dirname "$0")") 179 | if [ -z "$BASE_DIR" ]; then 180 | exit 1; 181 | fi 182 | 183 | MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR 184 | log "$MAVEN_PROJECTBASEDIR" 185 | 186 | ########################################################################################## 187 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 188 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 189 | ########################################################################################## 190 | wrapperJarPath="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" 191 | if [ -r "$wrapperJarPath" ]; then 192 | log "Found $wrapperJarPath" 193 | else 194 | log "Couldn't find $wrapperJarPath, downloading it ..." 195 | 196 | if [ -n "$MVNW_REPOURL" ]; then 197 | wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 198 | else 199 | wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.2.0/maven-wrapper-3.2.0.jar" 200 | fi 201 | while IFS="=" read -r key value; do 202 | # Remove '\r' from value to allow usage on windows as IFS does not consider '\r' as a separator ( considers space, tab, new line ('\n'), and custom '=' ) 203 | safeValue=$(echo "$value" | tr -d '\r') 204 | case "$key" in (wrapperUrl) wrapperUrl="$safeValue"; break ;; 205 | esac 206 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 207 | log "Downloading from: $wrapperUrl" 208 | 209 | if $cygwin; then 210 | wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath") 211 | fi 212 | 213 | if command -v wget > /dev/null; then 214 | log "Found wget ... using wget" 215 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--quiet" 216 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 217 | wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 218 | else 219 | wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 220 | fi 221 | elif command -v curl > /dev/null; then 222 | log "Found curl ... using curl" 223 | [ "$MVNW_VERBOSE" = true ] && QUIET="" || QUIET="--silent" 224 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 225 | curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 226 | else 227 | curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L || rm -f "$wrapperJarPath" 228 | fi 229 | else 230 | log "Falling back to using Java to download" 231 | javaSource="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.java" 232 | javaClass="$MAVEN_PROJECTBASEDIR/.mvn/wrapper/MavenWrapperDownloader.class" 233 | # For Cygwin, switch paths to Windows format before running javac 234 | if $cygwin; then 235 | javaSource=$(cygpath --path --windows "$javaSource") 236 | javaClass=$(cygpath --path --windows "$javaClass") 237 | fi 238 | if [ -e "$javaSource" ]; then 239 | if [ ! -e "$javaClass" ]; then 240 | log " - Compiling MavenWrapperDownloader.java ..." 241 | ("$JAVA_HOME/bin/javac" "$javaSource") 242 | fi 243 | if [ -e "$javaClass" ]; then 244 | log " - Running MavenWrapperDownloader.java ..." 245 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$wrapperUrl" "$wrapperJarPath") || rm -f "$wrapperJarPath" 246 | fi 247 | fi 248 | fi 249 | fi 250 | ########################################################################################## 251 | # End of extension 252 | ########################################################################################## 253 | 254 | # If specified, validate the SHA-256 sum of the Maven wrapper jar file 255 | wrapperSha256Sum="" 256 | while IFS="=" read -r key value; do 257 | case "$key" in (wrapperSha256Sum) wrapperSha256Sum=$value; break ;; 258 | esac 259 | done < "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.properties" 260 | if [ -n "$wrapperSha256Sum" ]; then 261 | wrapperSha256Result=false 262 | if command -v sha256sum > /dev/null; then 263 | if echo "$wrapperSha256Sum $wrapperJarPath" | sha256sum -c > /dev/null 2>&1; then 264 | wrapperSha256Result=true 265 | fi 266 | elif command -v shasum > /dev/null; then 267 | if echo "$wrapperSha256Sum $wrapperJarPath" | shasum -a 256 -c > /dev/null 2>&1; then 268 | wrapperSha256Result=true 269 | fi 270 | else 271 | echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." 272 | echo "Please install either command, or disable validation by removing 'wrapperSha256Sum' from your maven-wrapper.properties." 273 | exit 1 274 | fi 275 | if [ $wrapperSha256Result = false ]; then 276 | echo "Error: Failed to validate Maven wrapper SHA-256, your Maven wrapper might be compromised." >&2 277 | echo "Investigate or delete $wrapperJarPath to attempt a clean download." >&2 278 | echo "If you updated your Maven version, you need to update the specified wrapperSha256Sum property." >&2 279 | exit 1 280 | fi 281 | fi 282 | 283 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 284 | 285 | # For Cygwin, switch paths to Windows format before running java 286 | if $cygwin; then 287 | [ -n "$JAVA_HOME" ] && 288 | JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME") 289 | [ -n "$CLASSPATH" ] && 290 | CLASSPATH=$(cygpath --path --windows "$CLASSPATH") 291 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 292 | MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR") 293 | fi 294 | 295 | # Provide a "standardized" way to retrieve the CLI args that will 296 | # work with both Windows and non-Windows executions. 297 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $*" 298 | export MAVEN_CMD_LINE_ARGS 299 | 300 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 301 | 302 | # shellcheck disable=SC2086 # safe args 303 | exec "$JAVACMD" \ 304 | $MAVEN_OPTS \ 305 | $MAVEN_DEBUG_OPTS \ 306 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 307 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 308 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 309 | -------------------------------------------------------------------------------- /jsystemd-core/src/main/java/com/github/jpmsilva/jsystemd/Systemd.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2018-2023 Joao Silva 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package com.github.jpmsilva.jsystemd; 18 | 19 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.isUnderSystemd; 20 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.notifySocketPath; 21 | import static com.github.jpmsilva.jsystemd.SystemdUtilities.osName; 22 | import static java.lang.invoke.MethodHandles.lookup; 23 | import static java.util.Objects.requireNonNull; 24 | import static java.util.concurrent.TimeUnit.MICROSECONDS; 25 | import static java.util.concurrent.TimeUnit.SECONDS; 26 | import static org.slf4j.LoggerFactory.getLogger; 27 | 28 | import java.util.Arrays; 29 | import java.util.Collections; 30 | import java.util.List; 31 | import java.util.Optional; 32 | import java.util.concurrent.CopyOnWriteArrayList; 33 | import java.util.concurrent.Executors; 34 | import java.util.concurrent.ScheduledExecutorService; 35 | import java.util.concurrent.ScheduledFuture; 36 | import java.util.concurrent.TimeUnit; 37 | import java.util.concurrent.atomic.AtomicBoolean; 38 | import java.util.concurrent.atomic.AtomicLong; 39 | import java.util.concurrent.atomic.AtomicReference; 40 | import java.util.stream.Collectors; 41 | import org.jetbrains.annotations.NotNull; 42 | import org.jetbrains.annotations.Nullable; 43 | import org.slf4j.Logger; 44 | 45 | /** 46 | * The main systemd integration class. 47 | * 48 | *

Implements and exposes most of the {@code sd_notify} protocol, as well as some convenience methods. 49 | * 50 | *

Since this object sets up some timers for periodic communication with systemd, client code is expected to call {@link #close()} then they no longer need 51 | * Systemd instances. The implementation also implements {@link AutoCloseable} to ease implementation in DI containers that support the semantic. 52 | * 53 | * @author Joao Silva 54 | */ 55 | public class Systemd implements AutoCloseable { 56 | 57 | private static final Logger logger = getLogger(lookup().lookupClass()); 58 | 59 | @NotNull 60 | private final AtomicLong counter = new AtomicLong(0); 61 | 62 | @NotNull 63 | private final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1, r -> { 64 | final Thread thread = new Thread(r); 65 | thread.setName(String.format("jsystemd-%d", counter.incrementAndGet())); 66 | return thread; 67 | }); 68 | 69 | @NotNull 70 | private final List providers = new CopyOnWriteArrayList<>(); 71 | 72 | @Nullable 73 | private HealthProvider healthProvider; 74 | 75 | private long period = 5; 76 | 77 | private TimeUnit unit = SECONDS; 78 | 79 | private long timeout = MICROSECONDS.convert(29, SECONDS); 80 | 81 | @NotNull 82 | private final AtomicReference> future = new AtomicReference<>(); 83 | 84 | @NotNull 85 | private final AtomicBoolean ready = new AtomicBoolean(false); 86 | 87 | String options = ""; 88 | 89 | private Systemd() { 90 | } 91 | 92 | /** 93 | * Provides a dedicated builder instance that knows how to create Systemd instances. 94 | * 95 | * @return a builder instance 96 | */ 97 | public static @NotNull Builder builder() { 98 | return new Builder(); 99 | } 100 | 101 | /** 102 | * Adds the status providers to the list of providers in the specified position. 103 | * 104 | * @param index position to add the providers 105 | * @param providers the providers to add 106 | */ 107 | public void addStatusProviders(int index, SystemdStatusProvider... providers) { 108 | addStatusProviders(index, Arrays.asList(providers)); 109 | } 110 | 111 | /** 112 | * Adds the status providers to the end of the list of providers. 113 | * 114 | * @param providers the providers to add 115 | */ 116 | public void addStatusProviders(SystemdStatusProvider... providers) { 117 | addStatusProviders(this.providers.size(), Arrays.asList(providers)); 118 | } 119 | 120 | /** 121 | * Adds the status providers to the list of providers in the specified position. 122 | * 123 | * @param index position to add the providers 124 | * @param providers the providers to add 125 | */ 126 | @SuppressWarnings("WeakerAccess") 127 | public void addStatusProviders(int index, List providers) { 128 | this.providers.addAll(index, providers); 129 | } 130 | 131 | /** 132 | * Returns a read only view of the current providers. 133 | * 134 | * @return the current list of providers 135 | */ 136 | public @NotNull List getStatusProviders() { 137 | return Collections.unmodifiableList(providers); 138 | } 139 | 140 | /** 141 | * Sets the current providers, unregistering any other previously added. 142 | * 143 | * @param providers the providers to set 144 | */ 145 | public void setStatusProviders(@NotNull List providers) { 146 | this.providers.clear(); 147 | this.providers.addAll(requireNonNull(providers, "Providers must not be null")); 148 | } 149 | 150 | /** 151 | * Returns the current health provider. 152 | * 153 | * @return the current provider 154 | */ 155 | public Optional getHealthProvider() { 156 | return Optional.ofNullable(healthProvider); 157 | } 158 | 159 | /** 160 | * Sets the current health provider, overrides any other previously set. 161 | * 162 | * @param provider the provider to set, or null to disable the watchdog health integration 163 | */ 164 | public void setHealthProvider(@Nullable HealthProvider provider) { 165 | this.healthProvider = provider; 166 | } 167 | 168 | private void enableStatusUpdate(long period, @NotNull TimeUnit unit) { 169 | executor.scheduleAtFixedRate(this::updateStatus, period, period, unit); 170 | } 171 | 172 | private void enablePeriodicExtendTimeout(long period, @NotNull TimeUnit unit, long timeout) { 173 | this.period = period; 174 | this.unit = unit; 175 | this.timeout = timeout; 176 | enablePeriodicExtendTimeout(); 177 | } 178 | 179 | /** 180 | * Enables the periodic extension of the timeout. 181 | * 182 | * @see #extendTimeout() 183 | * @see #disablePeriodicExtendTimeout() 184 | */ 185 | public void enablePeriodicExtendTimeout() { 186 | future.getAndUpdate((future) -> { 187 | if (future == null) { 188 | return executor.scheduleAtFixedRate(this::extendTimeout, period, period, unit); 189 | } 190 | return null; 191 | }); 192 | } 193 | 194 | /** 195 | * Disables the periodic extension of the timeout. 196 | * 197 | * @see #extendTimeout() 198 | * @see #enablePeriodicExtendTimeout() 199 | */ 200 | public void disablePeriodicExtendTimeout() { 201 | future.getAndUpdate((future) -> { 202 | if (future != null && !future.isDone()) { 203 | future.cancel(false); 204 | } 205 | return null; 206 | }); 207 | } 208 | 209 | private void enableWatchdog(long period, @NotNull TimeUnit unit) { 210 | executor.scheduleAtFixedRate(this::watchdog, period, period, unit); 211 | } 212 | 213 | /** 214 | * Forces the current status to be calculated and sent to systemd. The method {@link Systemd.Builder#enableStatusUpdate(long, TimeUnit)} can be used to enable 215 | * periodic status updates. 216 | */ 217 | public void updateStatus() { 218 | SystemdNotify.status(providers.stream().map(SystemdStatusProvider::status).filter(t -> t.length() > 0).collect(Collectors.joining(", "))); 219 | } 220 | 221 | /** 222 | * Forces the timeout to be extended. The amount of time to extend is specified when the Systemd instance is build with 223 | * {@link Systemd.Builder#extendTimeout(long, TimeUnit, long)}, or 29 seconds if otherwise. Timeout extensions can only be sent during startup. 224 | * 225 | * @see Systemd.Builder#extendTimeout(long, TimeUnit, long) 226 | */ 227 | public void extendTimeout() { 228 | if (!ready.get()) { 229 | SystemdNotify.extendTimeout(timeout); 230 | } 231 | } 232 | 233 | /** 234 | * Forces the watchdog timestamp to be updated. The method {@link Systemd.Builder#enableWatchdog(long, TimeUnit)} can be used to enable periodic watchdog 235 | * updates. 236 | * 237 | *

If health provider is set and returns unhealthy the watchdog timestamp is not updated. 238 | */ 239 | @SuppressWarnings("WeakerAccess") 240 | public void watchdog() { 241 | Optional healthProvider = getHealthProvider(); 242 | if (healthProvider.isPresent() && !(healthProvider.get().health()).healthy) { 243 | logger.warn("Suppressing heartbeat to watchdog because application is unhealthy (details={})", healthProvider.get().health().details); 244 | return; 245 | } 246 | logger.debug("Triggering heartbeat to watchdog"); 247 | SystemdNotify.watchdog(); 248 | } 249 | 250 | /** 251 | * Notifies systemd that the application is ready. 252 | */ 253 | public void ready() { 254 | if (ready.compareAndSet(false, true)) { 255 | SystemdNotify.ready(); 256 | updateStatus(); 257 | } 258 | } 259 | 260 | /** 261 | * Returns if the application as completed startup. 262 | * 263 | * @return {@code true} if and only if {@link #ready} has been called. 264 | */ 265 | public boolean isReady() { 266 | return ready.get(); 267 | } 268 | 269 | /** 270 | * Signals that the application is stopping. 271 | */ 272 | public void stopping() { 273 | SystemdNotify.stopping(); 274 | } 275 | 276 | private void options(String options) { 277 | this.options = options; 278 | } 279 | 280 | /** 281 | * Logs information regarding the status of the integration with systemd. Meant to be used once the application has done sufficient work to initialize 282 | * logging. 283 | */ 284 | public void logStatus() { 285 | if (isUnderSystemd()) { 286 | logger.info("Running under systemd, OS name: \"{}\", notify socket: \"{}\"", osName(), notifySocketPath()); 287 | logger.info("Enabled Systemd integration with options=({})", options); 288 | } else { 289 | logger.info("Not running under systemd, OS name: \"{}\"", osName()); 290 | } 291 | } 292 | 293 | @Override 294 | public void close() throws Exception { 295 | synchronized (executor) { 296 | if (!executor.isShutdown()) { 297 | executor.shutdown(); 298 | boolean terminated = executor.awaitTermination(10, SECONDS); 299 | if (!terminated) { 300 | executor.shutdownNow(); 301 | } 302 | } 303 | } 304 | } 305 | 306 | /** 307 | * Specialized build class of {@link Systemd} objects. 308 | */ 309 | public static class Builder { 310 | 311 | private Builder() { 312 | } 313 | 314 | private long statusUpdatePeriod = -1; 315 | private TimeUnit statusUpdateUnit; 316 | private long extendTimeoutPeriod = -1; 317 | private TimeUnit extendTimeoutUnit; 318 | private long extendTimeoutTimeout; 319 | private long watchdogPeriod = -1; 320 | private TimeUnit watchdogUnit; 321 | 322 | /** 323 | * Enables periodic status updates. 324 | * 325 | * @param period the period to use 326 | * @param unit the time unit of the period 327 | * @return the same builder instance 328 | */ 329 | public Builder statusUpdate(long period, @NotNull TimeUnit unit) { 330 | if (period < 0) { 331 | throw new IllegalArgumentException("Illegal value for period"); 332 | } 333 | requireNonNull(unit, "Unit must not be null"); 334 | 335 | this.statusUpdatePeriod = period; 336 | this.statusUpdateUnit = unit; 337 | return this; 338 | } 339 | 340 | /** 341 | * Enables periodic timeout extensions. 342 | * 343 | * @param period the period to use 344 | * @param unit the time unit of the period 345 | * @param timeout the amount of time to extend the timeout in microseconds 346 | * @return the same builder instance 347 | */ 348 | @SuppressWarnings("unused") 349 | public Builder extendTimeout(long period, @NotNull TimeUnit unit, long timeout) { 350 | if (period < 0) { 351 | throw new IllegalArgumentException("Illegal value for period"); 352 | } 353 | requireNonNull(unit, "Unit must not be null"); 354 | if (timeout <= 0) { 355 | throw new IllegalArgumentException("Illegal value for timeout"); 356 | } 357 | 358 | this.extendTimeoutPeriod = period; 359 | this.extendTimeoutUnit = unit; 360 | this.extendTimeoutTimeout = timeout; 361 | return this; 362 | } 363 | 364 | /** 365 | * Enables periodic watchdog timestamp updates. 366 | * 367 | * @param period the period to use - must be greater than 0; if 0 this method does nothing 368 | * @param unit the time unit of the period 369 | * @return the same builder instance 370 | */ 371 | public Builder watchdog(long period, @NotNull TimeUnit unit) { 372 | if (period < 0) { 373 | throw new IllegalArgumentException("Illegal value for period"); 374 | } 375 | requireNonNull(unit, "Unit must not be null"); 376 | 377 | if (period > 0) { 378 | this.watchdogPeriod = period; 379 | this.watchdogUnit = unit; 380 | } 381 | return this; 382 | } 383 | 384 | /** 385 | * Builds a {@link Systemd} instance. 386 | * 387 | * @return the instance built 388 | */ 389 | public Systemd build() { 390 | Systemd systemd = new Systemd(); 391 | if (statusUpdatePeriod > -1) { 392 | systemd.enableStatusUpdate(statusUpdatePeriod, requireNonNull(statusUpdateUnit)); 393 | } 394 | if (extendTimeoutPeriod > -1) { 395 | systemd.enablePeriodicExtendTimeout(extendTimeoutPeriod, requireNonNull(extendTimeoutUnit), extendTimeoutTimeout); 396 | } 397 | if (watchdogPeriod > -1) { 398 | systemd.enableWatchdog(watchdogPeriod, requireNonNull(watchdogUnit)); 399 | } 400 | systemd.options( 401 | String.format("statusUpdatePeriod=%d %s, extendTimeoutPeriod=%d %s, extendTimeoutTimeout=%d MICROSECONDS, watchdogPeriod=%d %s", statusUpdatePeriod, 402 | statusUpdateUnit, extendTimeoutPeriod, extendTimeoutUnit, extendTimeoutTimeout, watchdogPeriod, watchdogUnit)); 403 | return systemd; 404 | } 405 | } 406 | } 407 | -------------------------------------------------------------------------------- /google_checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 57 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 78 | 79 | 80 | 86 | 87 | 88 | 89 | 92 | 93 | 94 | 95 | 96 | 100 | 101 | 102 | 103 | 104 | 106 | 107 | 108 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 127 | 129 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 177 | 178 | 179 | 181 | 183 | 184 | 185 | 186 | 188 | 189 | 190 | 191 | 193 | 194 | 195 | 196 | 198 | 199 | 200 | 201 | 203 | 204 | 205 | 206 | 208 | 209 | 210 | 211 | 213 | 214 | 215 | 216 | 218 | 219 | 220 | 221 | 223 | 224 | 225 | 226 | 228 | 229 | 230 | 231 | 233 | 234 | 235 | 236 | 238 | 239 | 240 | 241 | 243 | 245 | 247 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 278 | 279 | 280 | 283 | 284 | 285 | 286 | 292 | 293 | 294 | 295 | 298 | 299 | 300 | 301 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 316 | 317 | 318 | 319 | 320 | 321 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 337 | 338 | 339 | 340 | 343 | 344 | 345 | 346 | 347 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 363 | 364 | 365 | 366 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | 20 | 4.0.0 21 | 22 | com.github.jpmsilva.jsystemd 23 | jsystemd 24 | 3.1.2-SNAPSHOT 25 | pom 26 | 27 | jsystemd 28 | jsystemd aims to provide a better platform to integrate Java applications with systemd, and run them as proper operating system services. 29 | 30 | 31 | 2017 32 | 33 | https://jpmsilva.github.io/jsystemd-site/ 34 | 35 | 36 | https://github.com/jpmsilva/jsystemd/issues 37 | GitHub Issues 38 | 39 | 40 | 41 | 42 | The Apache Software License, Version 2.0 43 | https://www.apache.org/licenses/LICENSE-2.0.txt 44 | repo 45 | 46 | 47 | 48 | 49 | https://github.com/jpmsilva/jsystemd 50 | scm:git:https://github.com/jpmsilva/jsystemd.git 51 | scm:git:https://github.com/jpmsilva/jsystemd.git 52 | 53 | 54 | 55 | 56 | jpmsilva 57 | Joao Silva 58 | joao.p.m.silva@gmail.com 59 | https://github.com/jpmsilva 60 | Europe/Lisbon 61 | 62 | 63 | 64 | 65 | 17 66 | 67 | 68 | 3.2.0 69 | 3.1.1 70 | 3.3.1 71 | 3.1.0 72 | 3.11.0 73 | 3.3.0 74 | 4.0.0-M8 75 | 0.12 76 | 1.6.13 77 | 3.1.1 78 | 3.4.3 79 | 1.0-m10 80 | 3.1.0 81 | 3.2.1 82 | 3.5.0 83 | 3.2.2 84 | 1.4.0 85 | 3.3.0 86 | 3.21.0 87 | 3.3.0 88 | 89 | UTF-8 90 | UTF-8 91 | 92 | 3.0.0 93 | 2.0.7 94 | 95 | 96 | 97 | 98 | ossrh 99 | https://oss.sonatype.org/content/repositories/snapshots 100 | 101 | 102 | github-pages-sites 103 | Deployment through GitHub's site deployment plugin 104 | _versions/${project.version} 105 | 106 | 107 | 108 | 109 | 110 | 111 | net.java.dev.jna 112 | jna 113 | 5.13.0 114 | 115 | 116 | org.slf4j 117 | slf4j-api 118 | ${org.slf4j.version} 119 | 120 | 121 | org.jetbrains 122 | annotations 123 | 24.0.1 124 | 125 | 126 | 127 | 128 | org.springframework.boot 129 | spring-boot-dependencies 130 | ${org.springframework.boot.version} 131 | pom 132 | import 133 | 134 | 135 | 136 | 137 | 138 | 139 | org.jetbrains 140 | annotations 141 | provided 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | org.apache.maven.plugins 150 | maven-clean-plugin 151 | ${plugin.clean.version} 152 | 153 | 154 | 155 | org.apache.maven.plugins 156 | maven-install-plugin 157 | ${plugin.install.version} 158 | 159 | 160 | 161 | org.apache.maven.plugins 162 | maven-resources-plugin 163 | ${plugin.resources.version} 164 | 165 | 166 | 167 | org.apache.maven.plugins 168 | maven-surefire-plugin 169 | ${plugin.surefire.version} 170 | 171 | 172 | 173 | org.apache.maven.plugins 174 | maven-compiler-plugin 175 | ${plugin.compiler.version} 176 | 177 | true 178 | true 179 | 180 | 181 | 182 | 183 | org.apache.maven.plugins 184 | maven-jar-plugin 185 | ${plugin.jar.version} 186 | 187 | 188 | 189 | org.apache.maven.plugins 190 | maven-site-plugin 191 | ${plugin.site.version} 192 | 193 | true 194 | 195 | 196 | 197 | 198 | com.github.github 199 | site-maven-plugin 200 | ${plugin.github-site.version} 201 | 202 | github 203 | New site version: ${project.version} 204 | true 205 | jpmsilva 206 | jsystemd-site 207 | refs/heads/master 208 | 209 | **/* 210 | 211 | ${project.distributionManagement.site.url} 212 | 213 | 214 | 215 | 216 | org.sonatype.plugins 217 | nexus-staging-maven-plugin 218 | ${plugin.nexus-staging.version} 219 | true 220 | 221 | ossrh 222 | https://oss.sonatype.org/ 223 | true 224 | 225 | 226 | 227 | 228 | org.apache.maven.plugins 229 | maven-deploy-plugin 230 | ${plugin.deploy.version} 231 | 232 | 233 | 234 | org.apache.maven.plugins 235 | maven-project-info-reports-plugin 236 | ${plugin.project-info-reports.version} 237 | 238 | 239 | 240 | ch.dvbern.oss.maven.jgitflow 241 | jgitflow-maven-plugin 242 | ${plugin.jgitflow.version} 243 | 244 | 245 | master 246 | development 247 | feature- 248 | release- 249 | hotfix- 250 | version- 251 | 252 | false 253 | false 254 | [RELEASE] 255 | true 256 | true 257 | 258 | 259 | 260 | 261 | org.apache.maven.plugins 262 | maven-gpg-plugin 263 | ${plugin.gpg.version} 264 | 265 | 266 | 267 | org.apache.maven.plugins 268 | maven-source-plugin 269 | ${plugin.source.version} 270 | 271 | 272 | 273 | org.apache.maven.plugins 274 | maven-javadoc-plugin 275 | ${plugin.javadoc.version} 276 | 277 | 278 | https://docs.oracle.com/en/java/javase/17/docs/api/ 279 | https://docs.spring.io/spring-boot/docs/3.0.0/api/ 280 | https://docs.spring.io/spring-framework/docs/6.0.2/javadoc-api/ 281 | https://java-native-access.github.io/jna/5.13.0/javadoc/ 282 | 283 | ${project.name} ${project.version} 284 | ${project.artifactId} ${project.version} 285 | 286 | 287 | 288 | 289 | org.apache.maven.plugins 290 | maven-checkstyle-plugin 291 | ${plugin.checkstyle.version} 292 | 293 | 294 | com.puppycrawl.tools 295 | checkstyle 296 | 8.40 297 | 298 | 299 | 300 | 301 | 302 | de.jutzig 303 | github-release-plugin 304 | ${plugin.github-release.version} 305 | 306 | Version ${project.version} 307 | ${project.version} 308 | version-${project.version} 309 | jpmsilva/jsystemd 310 | 311 | 312 | 313 | 314 | org.apache.maven.plugins 315 | maven-jxr-plugin 316 | ${plugin.jxr.version} 317 | 318 | 319 | 320 | org.apache.maven.plugins 321 | maven-pmd-plugin 322 | ${plugin.pmd.version} 323 | 324 | 325 | 326 | 327 | 328 | 329 | org.apache.maven.plugins 330 | maven-enforcer-plugin 331 | ${plugin.enforcer.version} 332 | 333 | 334 | 335 | 336 | 337 | com.github.github 338 | site-maven-plugin 339 | 340 | 341 | 342 | site 343 | 344 | site-deploy 345 | 346 | 347 | 348 | 349 | org.apache.maven.plugins 350 | maven-checkstyle-plugin 351 | 352 | 353 | validate 354 | 355 | checkstyle 356 | 357 | 358 | true 359 | google_checkstyle.xml 360 | true 361 | 362 | 363 | 364 | 365 | 366 | org.apache.maven.plugins 367 | maven-pmd-plugin 368 | 369 | 370 | validate-pmd 371 | validate 372 | 373 | check 374 | 375 | 376 | exclude-pmd.properties 377 | 378 | 379 | 380 | validate-cpd 381 | validate 382 | 383 | cpd-check 384 | 385 | 386 | 387 | 388 | 389 | org.apache.maven.plugins 390 | maven-enforcer-plugin 391 | 392 | 393 | enforce-maven 394 | 395 | enforce 396 | 397 | 398 | 399 | 400 | 3.6.3 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | sample 413 | 414 | 415 | !performRelease 416 | 417 | 418 | 419 | jsystemd-spring-boot-sample 420 | 421 | 422 | 423 | release-profile 424 | 425 | 426 | performRelease 427 | true 428 | 429 | 430 | 431 | 432 | 433 | org.sonatype.plugins 434 | nexus-staging-maven-plugin 435 | true 436 | 437 | 438 | 439 | org.apache.maven.plugins 440 | maven-gpg-plugin 441 | 442 | 443 | sign-artifacts 444 | verify 445 | 446 | sign 447 | 448 | 449 | 450 | --pinentry-mode 451 | loopback 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | org.apache.maven.plugins 460 | maven-source-plugin 461 | 462 | 463 | attach-sources 464 | 465 | jar-no-fork 466 | 467 | 468 | 469 | 470 | 471 | 472 | org.apache.maven.plugins 473 | maven-javadoc-plugin 474 | 475 | 476 | attach-javadocs 477 | 478 | jar 479 | 480 | 481 | 482 | 483 | 484 | 485 | de.jutzig 486 | github-release-plugin 487 | 488 | 489 | github-release 490 | deploy 491 | 492 | release 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | org.apache.maven.plugins 506 | maven-project-info-reports-plugin 507 | 508 | 509 | all 510 | 511 | index 512 | dependencies 513 | licenses 514 | 515 | 516 | 517 | top 518 | false 519 | 520 | index 521 | team 522 | issue-management 523 | licenses 524 | scm 525 | distribution-management 526 | 527 | 528 | 529 | 530 | 531 | org.apache.maven.plugins 532 | maven-javadoc-plugin 533 | 534 | 535 | default 536 | 537 | 538 | 539 | 540 | 541 | aggregate 542 | false 543 | 544 | aggregate 545 | 546 | 547 | 548 | 549 | 550 | org.apache.maven.plugins 551 | maven-checkstyle-plugin 552 | 553 | true 554 | google_checkstyle.xml 555 | true 556 | 557 | 558 | 559 | 560 | checkstyle 561 | 562 | 563 | 564 | 565 | 566 | org.apache.maven.plugins 567 | maven-jxr-plugin 568 | false 569 | 570 | 571 | aggregate 572 | false 573 | 574 | aggregate 575 | 576 | 577 | 578 | 579 | 580 | org.apache.maven.plugins 581 | maven-pmd-plugin 582 | 583 | 584 | 585 | pmd 586 | cpd 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | jsystemd-core 596 | jsystemd-spring-boot-starter 597 | 598 | 599 | -------------------------------------------------------------------------------- /codestyle/intellij-java-google-style.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 15 | 21 | 28 |

225 | 226 | 227 | 228 | xmlns:android 229 | 230 | ^$ 231 | 232 | 233 | 234 |
235 |
236 | 237 | 238 | 239 | xmlns:.* 240 | 241 | ^$ 242 | 243 | 244 | BY_NAME 245 | 246 |
247 |
248 | 249 | 250 | 251 | .*:id 252 | 253 | http://schemas.android.com/apk/res/android 254 | 255 | 256 | 257 |
258 |
259 | 260 | 261 | 262 | style 263 | 264 | ^$ 265 | 266 | 267 | 268 |
269 |
270 | 271 | 272 | 273 | .* 274 | 275 | ^$ 276 | 277 | 278 | BY_NAME 279 | 280 |
281 |
282 | 283 | 284 | 285 | .*:.*Style 286 | 287 | http://schemas.android.com/apk/res/android 288 | 289 | 290 | BY_NAME 291 | 292 |
293 |
294 | 295 | 296 | 297 | .*:layout_width 298 | 299 | http://schemas.android.com/apk/res/android 300 | 301 | 302 | 303 |
304 |
305 | 306 | 307 | 308 | .*:layout_height 309 | 310 | http://schemas.android.com/apk/res/android 311 | 312 | 313 | 314 |
315 |
316 | 317 | 318 | 319 | .*:layout_weight 320 | 321 | http://schemas.android.com/apk/res/android 322 | 323 | 324 | 325 |
326 |
327 | 328 | 329 | 330 | .*:layout_margin 331 | 332 | http://schemas.android.com/apk/res/android 333 | 334 | 335 | 336 |
337 |
338 | 339 | 340 | 341 | .*:layout_marginTop 342 | 343 | http://schemas.android.com/apk/res/android 344 | 345 | 346 | 347 |
348 |
349 | 350 | 351 | 352 | .*:layout_marginBottom 353 | 354 | http://schemas.android.com/apk/res/android 355 | 356 | 357 | 358 |
359 |
360 | 361 | 362 | 363 | .*:layout_marginStart 364 | 365 | http://schemas.android.com/apk/res/android 366 | 367 | 368 | 369 |
370 |
371 | 372 | 373 | 374 | .*:layout_marginEnd 375 | 376 | http://schemas.android.com/apk/res/android 377 | 378 | 379 | 380 |
381 |
382 | 383 | 384 | 385 | .*:layout_marginLeft 386 | 387 | http://schemas.android.com/apk/res/android 388 | 389 | 390 | 391 |
392 |
393 | 394 | 395 | 396 | .*:layout_marginRight 397 | 398 | http://schemas.android.com/apk/res/android 399 | 400 | 401 | 402 |
403 |
404 | 405 | 406 | 407 | .*:layout_.* 408 | 409 | http://schemas.android.com/apk/res/android 410 | 411 | 412 | BY_NAME 413 | 414 |
415 |
416 | 417 | 418 | 419 | .*:padding 420 | 421 | http://schemas.android.com/apk/res/android 422 | 423 | 424 | 425 |
426 |
427 | 428 | 429 | 430 | .*:paddingTop 431 | 432 | http://schemas.android.com/apk/res/android 433 | 434 | 435 | 436 |
437 |
438 | 439 | 440 | 441 | .*:paddingBottom 442 | 443 | http://schemas.android.com/apk/res/android 444 | 445 | 446 | 447 |
448 |
449 | 450 | 451 | 452 | .*:paddingStart 453 | 454 | http://schemas.android.com/apk/res/android 455 | 456 | 457 | 458 |
459 |
460 | 461 | 462 | 463 | .*:paddingEnd 464 | 465 | http://schemas.android.com/apk/res/android 466 | 467 | 468 | 469 |
470 |
471 | 472 | 473 | 474 | .*:paddingLeft 475 | 476 | http://schemas.android.com/apk/res/android 477 | 478 | 479 | 480 |
481 |
482 | 483 | 484 | 485 | .*:paddingRight 486 | 487 | http://schemas.android.com/apk/res/android 488 | 489 | 490 | 491 |
492 |
493 | 494 | 495 | 496 | .* 497 | http://schemas.android.com/apk/res/android 498 | 499 | 500 | BY_NAME 501 | 502 |
503 |
504 | 505 | 506 | 507 | .* 508 | http://schemas.android.com/apk/res-auto 509 | 510 | 511 | BY_NAME 512 | 513 |
514 |
515 | 516 | 517 | 518 | .* 519 | http://schemas.android.com/tools 520 | 521 | 522 | BY_NAME 523 | 524 |
525 |
526 | 527 | 528 | 529 | .* 530 | .* 531 | 532 | 533 | BY_NAME 534 | 535 |
536 | 537 | 538 | 539 | 540 | 553 | 554 | 581 | 582 | 598 | 599 | --------------------------------------------------------------------------------