Skip to content

Commit d154089

Browse files
committed
Add start/stop goals to maven plugin
SpringApplicationLifecycle provides lifecycle operations on the current Spring Boot application. It can be registered to the platform mBean server of the 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. 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. See spring-projectsgh-2525
1 parent 370e957 commit d154089

File tree

16 files changed

+1254
-351
lines changed

16 files changed

+1254
-351
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 shutdown 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)