org.zodiac.actuate.context.ApplicationRestartEndpoint Maven / Gradle / Ivy
package org.zodiac.actuate.context;
import java.io.Closeable;
import java.io.IOException;
import java.util.Collections;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.integration.monitor.IntegrationMBeanExporter;
import org.springframework.util.ClassUtils;
import org.zodiac.commons.constants.SystemPropertiesConstants;
@Endpoint(id = ApplicationRestartEndpoint.ENDPOINT_NAME, enableByDefault = false)
public class ApplicationRestartEndpoint implements ApplicationListener {
private static Logger logger = LoggerFactory.getLogger(ApplicationRestartEndpoint.class);
public static final String ENDPOINT_NAME = "app-restart";
private ConfigurableApplicationContext context;
private SpringApplication application;
private String[] args;
private ApplicationPreparedEvent event;
private IntegrationShutdown integrationShutdown;
private long timeout;
// @ManagedAttribute
public long getTimeout() {
return this.timeout;
}
public void setTimeout(long timeout) {
this.timeout = timeout;
}
public void setIntegrationMBeanExporter(Object exporter) {
if (exporter != null) {
this.integrationShutdown = new IntegrationShutdown(exporter);
}
}
@Override
public void onApplicationEvent(ApplicationPreparedEvent input) {
this.event = input;
if (this.context == null) {
this.context = this.event.getApplicationContext();
this.args = this.event.getArgs();
this.application = this.event.getSpringApplication();
}
}
@WriteOperation
public Object restart() {
Thread thread = new Thread(this::safeRestart);
thread.setDaemon(false);
thread.start();
return Collections.singletonMap("message", "Application restarting");
}
private Boolean safeRestart() {
try {
doRestart();
logger.info("Application restarted");
return true;
} catch (Exception e) {
if (logger.isDebugEnabled()) {
logger.info("Could not doRestart", e);
} else {
logger.info("Could not doRestart: {}", e.getMessage());
}
return false;
}
}
public PauseEndpoint getPauseEndpoint() {
return new PauseEndpoint();
}
public ResumeEndpoint getResumeEndpoint() {
return new ResumeEndpoint();
}
// @ManagedOperation
public synchronized ConfigurableApplicationContext doRestart() {
if (this.context != null) {
if (this.integrationShutdown != null) {
this.integrationShutdown.stop(this.timeout);
}
this.application.setEnvironment(this.context.getEnvironment());
close();
// If running in a webapp then the context classloader is probably going to
// die so we need to revert to a safe place before starting again
overrideClassLoaderForRestart();
this.context = this.application.run(this.args);
}
return this.context;
}
private void close() {
ApplicationContext context = this.context;
while (context instanceof Closeable) {
try {
((Closeable)context).close();
} catch (IOException e) {
logger.error("Cannot close context: " + context.getId(), e);
}
context = context.getParent();
}
}
// @ManagedAttribute
public boolean isRunning() {
if (this.context != null) {
return this.context.isRunning();
}
return false;
}
// @ManagedOperation
public synchronized void doPause() {
if (this.context != null) {
this.context.stop();
}
}
// @ManagedOperation
public synchronized void doResume() {
if (this.context != null) {
this.context.start();
}
}
private void overrideClassLoaderForRestart() {
ClassUtils.overrideThreadContextClassLoader(this.application.getClass().getClassLoader());
}
/**
* Pause endpoint configuration.
*/
@Endpoint(id = PauseEndpoint.ENDPOINT_NAME)
public class PauseEndpoint {
public static final String ENDPOINT_NAME = "app-pause";
@WriteOperation
public Boolean pause() {
if (isRunning()) {
doPause();
return true;
}
return false;
}
}
/**
* Resume endpoint configuration.
*/
@Endpoint(id = ResumeEndpoint.ENDPOINT_NAME)
@ConfigurationProperties(value = SystemPropertiesConstants.Spring.MANAGEMENT_ENDPOINT_RESUME)
public class ResumeEndpoint {
public static final String ENDPOINT_NAME = "app-resume";
@WriteOperation
public Boolean resume() {
if (!isRunning()) {
doResume();
return true;
}
return false;
}
}
private class IntegrationShutdown {
private IntegrationMBeanExporter exporter;
IntegrationShutdown(Object exporter) {
this.exporter = (IntegrationMBeanExporter)exporter;
}
public void stop(long timeout) {
this.exporter.stopActiveComponents(timeout);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy