Skip to content

Commit

Permalink
Add start/stop goals to maven plugin
Browse files Browse the repository at this point in the history
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
  • Loading branch information
snicoll committed Apr 16, 2015
1 parent 92702b4 commit 54e12a0
Show file tree
Hide file tree
Showing 25 changed files with 1,918 additions and 382 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.autoconfigure.lifecycle;

import javax.management.MalformedObjectNameException;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.jmx.export.MBeanExporter;

/**
* Register a JMX component that allows to manage the lifecycle of the current
* application. Intended for internal use only.
*
* @author Stephane Nicoll
* @since 1.3.0
* @see SpringApplicationLifecycleMXBean
*/
@Configuration
@AutoConfigureAfter(JmxAutoConfiguration.class)
@ConditionalOnProperty(value = "spring.application.lifecycle.enabled", havingValue = "true", matchIfMissing = false)
class SpringApplicationLifecycleAutoConfiguration {

/**
* The property to use to customize the {@code ObjectName} of the application lifecycle mbean.
*/
static final String JMX_NAME_PROPERTY = "spring.application.lifecycle.jmx-name";

/**
* The default {@code ObjectName} of the application lifecycle mbean.
*/
static final String DEFAULT_JMX_NAME = "org.springframework.boot:type=Lifecycle,name=springApplicationLifecycle";

@Autowired(required = false)
private MBeanExporter mbeanExporter;

@Autowired
private Environment environment;

@Bean
public SpringApplicationLifecycleRegistrar springApplicationLifecycleRegistrar()
throws MalformedObjectNameException {

String jmxName = this.environment.getProperty(JMX_NAME_PROPERTY, DEFAULT_JMX_NAME);
if (mbeanExporter != null) { // Make sure to not register that MBean twice
mbeanExporter.addExcludedBean(jmxName);
}
return new SpringApplicationLifecycleRegistrar(jmxName);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.autoconfigure.lifecycle;

/**
* A simple MBean contract to control the lifecycle of a {@code SpringApplication} via
* JMX. Intended for internal use only.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public interface SpringApplicationLifecycleMXBean {

/**
* Specify if the application has fully started and is now ready.
* @return {@code true} if the application is ready
* @see org.springframework.boot.context.event.ApplicationReadyEvent
*/
boolean isReady();

/**
* Shutdown the application.
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
void shutdown();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.boot.autoconfigure.lifecycle;

import java.lang.management.ManagementFactory;
import javax.management.MBeanServer;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectName;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationListener;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.util.Assert;

/**
* Register a {@link SpringApplicationLifecycleMXBean} implementation to the platform
* {@link MBeanServer}.
*
* @author Stephane Nicoll
* @since 1.3.0
*/
public class SpringApplicationLifecycleRegistrar implements ApplicationContextAware, InitializingBean, DisposableBean,
ApplicationListener<ApplicationReadyEvent> {

private static final Log logger = LogFactory.getLog(SpringApplicationLifecycle.class);

private ConfigurableApplicationContext applicationContext;

private final ObjectName objectName;

private boolean ready = false;

public SpringApplicationLifecycleRegistrar(String name) throws MalformedObjectNameException {
this.objectName = new ObjectName(name);
}

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
Assert.isTrue(applicationContext instanceof ConfigurableApplicationContext,
"ApplicationContext does not implement ConfigurableApplicationContext");
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}

@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
ready = true;
}

@Override
public void afterPropertiesSet() throws Exception {
MBeanServer server = ManagementFactory.getPlatformMBeanServer();
server.registerMBean(new SpringApplicationLifecycle(), objectName);
if (logger.isDebugEnabled()) {
logger.debug("Application lifecycle MBean registered with name '" + objectName + "'");
}
}

@Override
public void destroy() throws Exception {
ManagementFactory.getPlatformMBeanServer().unregisterMBean(objectName);
}


private class SpringApplicationLifecycle implements SpringApplicationLifecycleMXBean {

@Override
public boolean isReady() {
return ready;
}

@Override
public void shutdown() {
logger.info("Application shutdown requested.");
applicationContext.close();
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchDataAutoConfig
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
org.springframework.boot.autoconfigure.lifecycle.SpringApplicationLifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
Expand Down
Loading

0 comments on commit 54e12a0

Please sign in to comment.