Skip to content

Commit d44b288

Browse files
committed
Add start/stop goals to maven plugin
SpringApplicationLifecycle provides lifecycle operations on the current Spring Boot application. It can be registered as an MBean of the platform MBean server if a specific property is set. Besides, the JMX name can also be customized via a property in case more than one Spring Boot application is started in the same process. The Maven plugin uses that MBean to check that the application is ready before ending the "start" phase. It uses it to trigger a proper shutdown of the application during the "stop" phase. If the process has to be forked, the platform MBean server is exposed on a configurable port so that the maven plugin can connect to it. Such change permits the maven plugin to integrate a classical integration test scenario where the "start" goal is invoked during the pre-integration phase and the "stop" goal during the post-integration phase. Closes spring-projectsgh-2525
1 parent 2176afb commit d44b288

File tree

25 files changed

+1918
-382
lines changed

25 files changed

+1918
-382
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
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 org.springframework.boot.autoconfigure.lifecycle;
18+
19+
import javax.management.MalformedObjectNameException;
20+
21+
import org.springframework.beans.factory.annotation.Autowired;
22+
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
23+
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
24+
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.Configuration;
27+
import org.springframework.core.env.Environment;
28+
import org.springframework.jmx.export.MBeanExporter;
29+
30+
/**
31+
* Register a JMX component that allows to manage the lifecycle of the current
32+
* application. Intended for internal use only.
33+
*
34+
* @author Stephane Nicoll
35+
* @since 1.3.0
36+
* @see SpringApplicationLifecycleMXBean
37+
*/
38+
@Configuration
39+
@AutoConfigureAfter(JmxAutoConfiguration.class)
40+
@ConditionalOnProperty(value = "spring.application.lifecycle.enabled", havingValue = "true", matchIfMissing = false)
41+
class SpringApplicationLifecycleAutoConfiguration {
42+
43+
/**
44+
* The property to use to customize the {@code ObjectName} of the application lifecycle mbean.
45+
*/
46+
static final String JMX_NAME_PROPERTY = "spring.application.lifecycle.jmx-name";
47+
48+
/**
49+
* The default {@code ObjectName} of the application lifecycle mbean.
50+
*/
51+
static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Lifecycle,name=springApplicationLifecycle";
52+
53+
@Autowired(required = false)
54+
private MBeanExporter mbeanExporter;
55+
56+
@Autowired
57+
private Environment environment;
58+
59+
@Bean
60+
public SpringApplicationLifecycleRegistrar springApplicationLifecycleRegistrar()
61+
throws MalformedObjectNameException {
62+
63+
String jmxName = this.environment.getProperty(JMX_NAME_PROPERTY, DEFAULT_JMX_NAME);
64+
if (mbeanExporter != null) { // Make sure to not register that MBean twice
65+
mbeanExporter.addExcludedBean(jmxName);
66+
}
67+
return new SpringApplicationLifecycleRegistrar(jmxName);
68+
}
69+
70+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
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 org.springframework.boot.autoconfigure.lifecycle;
18+
19+
/**
20+
* A simple MBean contract to control the lifecycle of a {@code SpringApplication} via
21+
* JMX. Intended for internal use only.
22+
*
23+
* @author Stephane Nicoll
24+
* @since 1.3.0
25+
*/
26+
public interface SpringApplicationLifecycleMXBean {
27+
28+
/**
29+
* Specify if the application has fully started and is now ready.
30+
* @return {@code true} if the application is ready
31+
* @see org.springframework.boot.context.event.ApplicationReadyEvent
32+
*/
33+
boolean isReady();
34+
35+
/**
36+
* Shutdown the application.
37+
* @see org.springframework.context.ConfigurableApplicationContext#close()
38+
*/
39+
void shutdown();
40+
41+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Copyright 2012-2015 the original author or authors.
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 org.springframework.boot.autoconfigure.lifecycle;
18+
19+
import java.lang.management.ManagementFactory;
20+
import javax.management.MBeanServer;
21+
import javax.management.MalformedObjectNameException;
22+
import javax.management.ObjectName;
23+
24+
import org.apache.commons.logging.Log;
25+
import org.apache.commons.logging.LogFactory;
26+
27+
import org.springframework.beans.BeansException;
28+
import org.springframework.beans.factory.DisposableBean;
29+
import org.springframework.beans.factory.InitializingBean;
30+
import org.springframework.boot.context.event.ApplicationReadyEvent;
31+
import org.springframework.context.ApplicationContext;
32+
import org.springframework.context.ApplicationContextAware;
33+
import org.springframework.context.ApplicationListener;
34+
import org.springframework.context.ConfigurableApplicationContext;
35+
import org.springframework.util.Assert;
36+
37+
/**
38+
* Register a {@link SpringApplicationLifecycleMXBean} implementation to the platform
39+
* {@link MBeanServer}.
40+
*
41+
* @author Stephane Nicoll
42+
* @since 1.3.0
43+
*/
44+
public class SpringApplicationLifecycleRegistrar implements ApplicationContextAware, InitializingBean, DisposableBean,
45+
ApplicationListener<ApplicationReadyEvent> {
46+
47+
private static final Log logger = LogFactory.getLog(SpringApplicationLifecycle.class);
48+
49+
private ConfigurableApplicationContext applicationContext;
50+
51+
private final ObjectName objectName;
52+
53+
private boolean ready = false;
54+
55+
public SpringApplicationLifecycleRegistrar(String name) throws MalformedObjectNameException {
56+
this.objectName = new ObjectName(name);
57+
}
58+
59+
@Override
60+
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
61+
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
62+
"ApplicationContext does not implement ConfigurableApplicationContext");
63+
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
64+
}
65+
66+
@Override
67+
public void onApplicationEvent(ApplicationReadyEvent event) {
68+
ready = true;
69+
}
70+
71+
@Override
72+
public void afterPropertiesSet() throws Exception {
73+
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
74+
server.registerMBean(new SpringApplicationLifecycle(), objectName);
75+
if (logger.isDebugEnabled()) {
76+
logger.debug("Application lifecycle MBean registered with name '" + objectName + "'");
77+
}
78+
}
79+
80+
@Override
81+
public void destroy() throws Exception {
82+
ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
83+
}
84+
85+
86+
private class SpringApplicationLifecycle implements SpringApplicationLifecycleMXBean {
87+
88+
@Override
89+
public boolean isReady() {
90+
return ready;
91+
}
92+
93+
@Override
94+
public void shutdown() {
95+
logger.info("Application shutdown requested.");
96+
applicationContext.close();
97+
}
98+
}
99+
100+
}
101+

spring-boot-autoconfigure/src/main/resources/META-INF/spring.factories

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchDataAutoConfig
3737
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
3838
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
3939
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
40+
org.springframework.boot.autoconfigure.lifecycle.SpringApplicationLifecycleAutoConfiguration,\
4041
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
4142
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
4243
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\

0 commit comments

Comments
 (0)