Revert "Add start/stop goals to maven plugin"
This reverts commit 54e12a07e6
.
This commit is contained in:
parent
2085a5b0cf
commit
a8d099f6b8
|
@ -1,70 +0,0 @@
|
||||||
/*
|
|
||||||
* 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
/*
|
|
||||||
* 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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -37,7 +37,6 @@ org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchDataAutoConfig
|
||||||
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
|
org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\
|
||||||
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
|
org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\
|
||||||
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
|
org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\
|
||||||
org.springframework.boot.autoconfigure.lifecycle.SpringApplicationLifecycleAutoConfiguration,\
|
|
||||||
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
|
org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\
|
||||||
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
|
org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\
|
||||||
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
|
org.springframework.boot.autoconfigure.mobile.DeviceResolverAutoConfiguration,\
|
||||||
|
|
|
@ -1,179 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.InstanceNotFoundException;
|
|
||||||
import javax.management.MBeanServer;
|
|
||||||
import javax.management.MalformedObjectNameException;
|
|
||||||
import javax.management.ObjectInstance;
|
|
||||||
import javax.management.ObjectName;
|
|
||||||
|
|
||||||
import org.junit.After;
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Rule;
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.rules.ExpectedException;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration;
|
|
||||||
import org.springframework.boot.context.event.ApplicationReadyEvent;
|
|
||||||
import org.springframework.boot.test.EnvironmentTestUtils;
|
|
||||||
import org.springframework.context.ApplicationListener;
|
|
||||||
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
|
|
||||||
import org.springframework.context.annotation.Configuration;
|
|
||||||
import org.springframework.context.event.ContextRefreshedEvent;
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
import static org.junit.Assert.assertFalse;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertTrue;
|
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tests for {@link SpringApplicationLifecycleAutoConfiguration}.
|
|
||||||
*
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
public class SpringApplicationLifecycleAutoConfigurationTests {
|
|
||||||
|
|
||||||
public static final String ENABLE_LIFECYCLE_PROP = "spring.application.lifecycle.enabled=true";
|
|
||||||
|
|
||||||
@Rule
|
|
||||||
public final ExpectedException thrown = ExpectedException.none();
|
|
||||||
|
|
||||||
private AnnotationConfigApplicationContext context;
|
|
||||||
|
|
||||||
private MBeanServer mBeanServer;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() throws MalformedObjectNameException {
|
|
||||||
this.mBeanServer = ManagementFactory.getPlatformMBeanServer();
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
public void tearDown() {
|
|
||||||
if (this.context != null) {
|
|
||||||
this.context.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void notRegisteredByDefault() throws MalformedObjectNameException, InstanceNotFoundException {
|
|
||||||
load();
|
|
||||||
|
|
||||||
thrown.expect(InstanceNotFoundException.class);
|
|
||||||
this.mBeanServer.getObjectInstance(createDefaultObjectName());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registeredWithProperty() throws Exception {
|
|
||||||
load(ENABLE_LIFECYCLE_PROP);
|
|
||||||
|
|
||||||
ObjectName objectName = createDefaultObjectName();
|
|
||||||
ObjectInstance objectInstance = this.mBeanServer.getObjectInstance(objectName);
|
|
||||||
assertNotNull(objectInstance);
|
|
||||||
|
|
||||||
assertEquals("Simple context does not trigger proper event",
|
|
||||||
false, isCurrentApplicationReady(objectName));
|
|
||||||
this.context.publishEvent(new ApplicationReadyEvent(new SpringApplication(), null, this.context));
|
|
||||||
assertEquals("Application should be ready",
|
|
||||||
true, isCurrentApplicationReady(objectName));
|
|
||||||
|
|
||||||
assertTrue("context has been started", this.context.isActive());
|
|
||||||
mBeanServer.invoke(objectName, "shutdown", null, null);
|
|
||||||
assertFalse("Context should have been closed", this.context.isActive());
|
|
||||||
|
|
||||||
thrown.expect(InstanceNotFoundException.class); // JMX cleanup
|
|
||||||
this.mBeanServer.getObjectInstance(objectName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registerWithCustomJmxName() throws InstanceNotFoundException {
|
|
||||||
String customJmxName = "org.acme:name=FooBar";
|
|
||||||
System.setProperty(SpringApplicationLifecycleAutoConfiguration.JMX_NAME_PROPERTY, customJmxName);
|
|
||||||
try {
|
|
||||||
load(ENABLE_LIFECYCLE_PROP);
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.mBeanServer.getObjectInstance(createObjectName(customJmxName));
|
|
||||||
}
|
|
||||||
catch (InstanceNotFoundException e) {
|
|
||||||
fail("lifecycle MBean should have been exposed with custom name");
|
|
||||||
}
|
|
||||||
|
|
||||||
thrown.expect(InstanceNotFoundException.class); // Should not be exposed
|
|
||||||
this.mBeanServer.getObjectInstance(createDefaultObjectName());
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
System.clearProperty(SpringApplicationLifecycleAutoConfiguration.JMX_NAME_PROPERTY);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void registerWithSpringApplication() throws Exception {
|
|
||||||
final ObjectName objectName = createDefaultObjectName();
|
|
||||||
SpringApplication application = new SpringApplication(ExampleConfig.class,
|
|
||||||
SpringApplicationLifecycleAutoConfiguration.class);
|
|
||||||
application.setWebEnvironment(false);
|
|
||||||
application.addListeners(new ApplicationListener<ContextRefreshedEvent>() {
|
|
||||||
@Override
|
|
||||||
public void onApplicationEvent(ContextRefreshedEvent event) {
|
|
||||||
try {
|
|
||||||
assertFalse("Application should not be ready yet", isCurrentApplicationReady(objectName));
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new IllegalStateException("Could not contact spring application lifecycle bean", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
application.run("--" + ENABLE_LIFECYCLE_PROP);
|
|
||||||
assertTrue("application should be ready now", isCurrentApplicationReady(objectName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectName createDefaultObjectName() {
|
|
||||||
return createObjectName(SpringApplicationLifecycleAutoConfiguration.DEFAULT_JMX_NAME);
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectName createObjectName(String jmxName) {
|
|
||||||
try {
|
|
||||||
return new ObjectName(jmxName);
|
|
||||||
}
|
|
||||||
catch (MalformedObjectNameException e) {
|
|
||||||
throw new IllegalStateException("Invalid jmx name " + jmxName, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Boolean isCurrentApplicationReady(ObjectName objectName) throws Exception {
|
|
||||||
return (Boolean) this.mBeanServer.getAttribute(objectName, "Ready");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void load(String... environment) {
|
|
||||||
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
|
|
||||||
EnvironmentTestUtils.addEnvironment(applicationContext, environment);
|
|
||||||
applicationContext.register(JmxAutoConfiguration.class, SpringApplicationLifecycleAutoConfiguration.class);
|
|
||||||
applicationContext.refresh();
|
|
||||||
this.context = applicationContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Configuration
|
|
||||||
static class ExampleConfig {
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2015 the original author or authors.
|
* Copyright 2012-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -48,7 +48,7 @@ class RunProcessCommand extends AbstractCommand {
|
||||||
|
|
||||||
protected ExitStatus run(Collection<String> args) throws IOException {
|
protected ExitStatus run(Collection<String> args) throws IOException {
|
||||||
this.process = new RunProcess(this.command);
|
this.process = new RunProcess(this.command);
|
||||||
int code = this.process.run(true, args.toArray(new String[args.size()]));
|
int code = this.process.run(args.toArray(new String[args.size()]));
|
||||||
if (code == 0) {
|
if (code == 0) {
|
||||||
return ExitStatus.OK;
|
return ExitStatus.OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,6 @@
|
||||||
</properties>
|
</properties>
|
||||||
<modules>
|
<modules>
|
||||||
<module>spring-boot-gradle-tests</module>
|
<module>spring-boot-gradle-tests</module>
|
||||||
<module>spring-boot-maven-tests</module>
|
|
||||||
<module>spring-boot-security-tests</module>
|
<module>spring-boot-security-tests</module>
|
||||||
</modules>
|
</modules>
|
||||||
<profiles>
|
<profiles>
|
||||||
|
|
|
@ -1,19 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-integration-tests</artifactId>
|
|
||||||
<version>1.3.0.BUILD-SNAPSHOT</version>
|
|
||||||
</parent>
|
|
||||||
<artifactId>spring-boot-maven-tests</artifactId>
|
|
||||||
<packaging>pom</packaging>
|
|
||||||
<name>Spring Boot Maven Tests</name>
|
|
||||||
<properties>
|
|
||||||
<main.basedir>${basedir}/..</main.basedir>
|
|
||||||
</properties>
|
|
||||||
<modules>
|
|
||||||
<module>spring-boot-maven-tests-it</module>
|
|
||||||
<module>spring-boot-maven-tests-it-fork</module>
|
|
||||||
</modules>
|
|
||||||
</project>
|
|
|
@ -1,82 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-tests</artifactId>
|
|
||||||
<version>1.3.0.BUILD-SNAPSHOT</version>
|
|
||||||
<relativePath>../</relativePath>
|
|
||||||
</parent>
|
|
||||||
<artifactId>spring-boot-maven-tests-it-fork</artifactId>
|
|
||||||
<name>Spring Boot Maven Tests - Forked Integration test</name>
|
|
||||||
<properties>
|
|
||||||
<main.basedir>${basedir}/../../..</main.basedir>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>reserve-tomcat-port</id>
|
|
||||||
<goals>
|
|
||||||
<goal>reserve-network-port</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>process-resources</phase>
|
|
||||||
<configuration>
|
|
||||||
<portNames>
|
|
||||||
<portName>tomcat.http.port</portName>
|
|
||||||
</portNames>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>pre-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>start</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<arguments>
|
|
||||||
<argument>--server.port=${tomcat.http.port}</argument>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>post-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>stop</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
<fork>true</fork>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.server.port>${tomcat.http.port}</test.server.port>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 sample;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
@SpringBootApplication
|
|
||||||
@RestController
|
|
||||||
public class IntegrationTestApplication {
|
|
||||||
|
|
||||||
@RequestMapping("/")
|
|
||||||
public String home() {
|
|
||||||
return "Hello World!";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(IntegrationTestApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 sample;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import static org.hamcrest.core.Is.is;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
public class IntegrationTestApplicationIT {
|
|
||||||
|
|
||||||
private int actualServerPort;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
String port = System.getProperty("test.server.port");
|
|
||||||
assertNotNull("System property 'test.server.port' must be set", port);
|
|
||||||
this.actualServerPort = Integer.parseInt(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test() {
|
|
||||||
String content = new RestTemplate().getForObject(
|
|
||||||
"http://localhost:" + actualServerPort + "/", String.class);
|
|
||||||
assertThat(content, is("Hello World!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,81 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
|
||||||
<modelVersion>4.0.0</modelVersion>
|
|
||||||
<parent>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-tests</artifactId>
|
|
||||||
<version>1.3.0.BUILD-SNAPSHOT</version>
|
|
||||||
<relativePath>../</relativePath>
|
|
||||||
</parent>
|
|
||||||
<artifactId>spring-boot-maven-tests-it</artifactId>
|
|
||||||
<name>Spring Boot Maven Tests - Integration test</name>
|
|
||||||
<properties>
|
|
||||||
<main.basedir>${basedir}/../../..</main.basedir>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-starter-web</artifactId>
|
|
||||||
</dependency>
|
|
||||||
</dependencies>
|
|
||||||
|
|
||||||
<build>
|
|
||||||
<plugins>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>reserve-tomcat-port</id>
|
|
||||||
<goals>
|
|
||||||
<goal>reserve-network-port</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>process-resources</phase>
|
|
||||||
<configuration>
|
|
||||||
<portNames>
|
|
||||||
<portName>tomcat.http.port</portName>
|
|
||||||
</portNames>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.springframework.boot</groupId>
|
|
||||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>pre-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>start</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<arguments>
|
|
||||||
<argument>--server.port=${tomcat.http.port}</argument>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>post-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>stop</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
<configuration>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.server.port>${tomcat.http.port}</test.server.port>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
</plugins>
|
|
||||||
</build>
|
|
||||||
|
|
||||||
</project>
|
|
|
@ -1,40 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 sample;
|
|
||||||
|
|
||||||
import org.springframework.boot.SpringApplication;
|
|
||||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
@SpringBootApplication
|
|
||||||
@RestController
|
|
||||||
public class IntegrationTestApplication {
|
|
||||||
|
|
||||||
@RequestMapping("/")
|
|
||||||
public String home() {
|
|
||||||
return "Hello World!";
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
SpringApplication.run(IntegrationTestApplication.class, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,49 +0,0 @@
|
||||||
/*
|
|
||||||
* 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 sample;
|
|
||||||
|
|
||||||
import org.junit.Before;
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import org.springframework.web.client.RestTemplate;
|
|
||||||
|
|
||||||
import static org.hamcrest.core.Is.is;
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
|
||||||
import static org.junit.Assert.assertThat;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
*/
|
|
||||||
public class IntegrationTestApplicationIT {
|
|
||||||
|
|
||||||
private int actualServerPort;
|
|
||||||
|
|
||||||
@Before
|
|
||||||
public void setup() {
|
|
||||||
String port = System.getProperty("test.server.port");
|
|
||||||
assertNotNull("System property 'test.server.port' must be set", port);
|
|
||||||
this.actualServerPort = Integer.parseInt(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
public void test() {
|
|
||||||
String content = new RestTemplate().getForObject(
|
|
||||||
"http://localhost:" + actualServerPort + "/", String.class);
|
|
||||||
assertThat(content, is("Hello World!"));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2015 the original author or authors.
|
* Copyright 2012-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -31,7 +31,6 @@ import org.springframework.util.ReflectionUtils;
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Dave Syer
|
* @author Dave Syer
|
||||||
* @author Andy Wilkinson
|
* @author Andy Wilkinson
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
*/
|
*/
|
||||||
public class RunProcess {
|
public class RunProcess {
|
||||||
|
@ -51,18 +50,11 @@ public class RunProcess {
|
||||||
this.command = command;
|
this.command = command;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int run(boolean waitForProcess, String... args) throws IOException {
|
public int run(String... args) throws IOException {
|
||||||
return run(waitForProcess, Arrays.asList(args));
|
return run(Arrays.asList(args));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected int run(Collection<String> args) throws IOException {
|
||||||
* Kill this process.
|
|
||||||
*/
|
|
||||||
public void kill() {
|
|
||||||
doKill();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected int run(boolean waitForProcess, Collection<String> args) throws IOException {
|
|
||||||
ProcessBuilder builder = new ProcessBuilder(this.command);
|
ProcessBuilder builder = new ProcessBuilder(this.command);
|
||||||
builder.command().addAll(args);
|
builder.command().addAll(args);
|
||||||
builder.redirectErrorStream(true);
|
builder.redirectErrorStream(true);
|
||||||
|
@ -79,22 +71,17 @@ public class RunProcess {
|
||||||
handleSigInt();
|
handleSigInt();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (waitForProcess) {
|
try {
|
||||||
try {
|
return process.waitFor();
|
||||||
return process.waitFor();
|
}
|
||||||
}
|
catch (InterruptedException ex) {
|
||||||
catch (InterruptedException ex) {
|
Thread.currentThread().interrupt();
|
||||||
Thread.currentThread().interrupt();
|
return 1;
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return 5;
|
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
if (waitForProcess) {
|
this.endTime = System.currentTimeMillis();
|
||||||
this.endTime = System.currentTimeMillis();
|
this.process = null;
|
||||||
this.process = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,11 +163,7 @@ public class RunProcess {
|
||||||
if (hasJustEnded()) {
|
if (hasJustEnded()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return doKill();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean doKill() {
|
|
||||||
// destroy the running process
|
// destroy the running process
|
||||||
Process process = this.process;
|
Process process = this.process;
|
||||||
if (process != null) {
|
if (process != null) {
|
||||||
|
|
|
@ -1,441 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.maven;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.net.MalformedURLException;
|
|
||||||
import java.net.URL;
|
|
||||||
import java.security.CodeSource;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.apache.maven.artifact.Artifact;
|
|
||||||
import org.apache.maven.model.Resource;
|
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
|
||||||
import org.apache.maven.plugin.MojoFailureException;
|
|
||||||
import org.apache.maven.plugins.annotations.Parameter;
|
|
||||||
import org.apache.maven.project.MavenProject;
|
|
||||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter;
|
|
||||||
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
|
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.FileUtils;
|
|
||||||
import org.springframework.boot.loader.tools.MainClassFinder;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base class to run a spring application.
|
|
||||||
*
|
|
||||||
* @author Phillip Webb
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @author David Liu
|
|
||||||
* @see RunMojo
|
|
||||||
* @see StartMojo
|
|
||||||
*/
|
|
||||||
public abstract class AbstractRunMojo extends AbstractDependencyFilterMojo {
|
|
||||||
|
|
||||||
private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Maven project.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
|
||||||
private MavenProject project;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add maven resources to the classpath directly, this allows live in-place editing of
|
|
||||||
* resources. Duplicate resources are removed from {@code target/classes} to prevent
|
|
||||||
* them to appear twice if {@code ClassLoader.getResources()} is called.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(property = "run.addResources", defaultValue = "true")
|
|
||||||
private boolean addResources;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Path to agent jar. NOTE: the use of agents means that processes will be started by
|
|
||||||
* forking a new JVM.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(property = "run.agent")
|
|
||||||
private File[] agent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to say that the agent requires -noverify.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(property = "run.noverify")
|
|
||||||
private Boolean noverify;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* JVM arguments that should be associated with the forked process used to run the
|
|
||||||
* application. On command line, make sure to wrap multiple values between quotes.
|
|
||||||
* @since 1.1
|
|
||||||
*/
|
|
||||||
@Parameter(property = "run.jvmArguments")
|
|
||||||
private String jvmArguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Arguments that should be passed to the application. On command line use commas to
|
|
||||||
* separate multiple arguments.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(property = "run.arguments")
|
|
||||||
private String[] arguments;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The name of the main class. If not specified the first compiled class found that
|
|
||||||
* contains a 'main' method will be used.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private String mainClass;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Additional folders besides the classes directory that should be added to the
|
|
||||||
* classpath.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private String[] folders;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Directory containing the classes and resource files that should be packaged into
|
|
||||||
* the archive.
|
|
||||||
* @since 1.0
|
|
||||||
*/
|
|
||||||
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
|
|
||||||
private File classesDirectory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate if the run processes should be forked. By default process forking
|
|
||||||
* is only used if an agent or jvmArguments are specified.
|
|
||||||
* @since 1.2
|
|
||||||
*/
|
|
||||||
@Parameter(property = "fork")
|
|
||||||
private Boolean fork;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify if the application process should be forked.
|
|
||||||
* @return {@code true} if the application process should be forked
|
|
||||||
*/
|
|
||||||
protected boolean isFork() {
|
|
||||||
return (Boolean.TRUE.equals(this.fork)
|
|
||||||
|| (this.fork == null && (hasAgent() || hasJvmArgs())));
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasAgent() {
|
|
||||||
return (this.agent != null && this.agent.length > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean hasJvmArgs() {
|
|
||||||
return (this.jvmArguments != null && this.jvmArguments.length() > 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
|
||||||
final String startClassName = getStartClass();
|
|
||||||
run(startClassName);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void findAgent() {
|
|
||||||
try {
|
|
||||||
if (this.agent == null || this.agent.length == 0) {
|
|
||||||
Class<?> loaded = Class.forName(SPRING_LOADED_AGENT_CLASSNAME);
|
|
||||||
if (loaded != null) {
|
|
||||||
if (this.noverify == null) {
|
|
||||||
this.noverify = true;
|
|
||||||
}
|
|
||||||
CodeSource source = loaded.getProtectionDomain().getCodeSource();
|
|
||||||
if (source != null) {
|
|
||||||
this.agent = new File[] {new File(source.getLocation().getFile())};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (ClassNotFoundException ex) {
|
|
||||||
// ignore;
|
|
||||||
}
|
|
||||||
if (this.noverify == null) {
|
|
||||||
this.noverify = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void run(String startClassName) throws MojoExecutionException, MojoFailureException {
|
|
||||||
findAgent();
|
|
||||||
if (isFork()) {
|
|
||||||
doRunWithForkedJvm(startClassName);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (hasAgent()) {
|
|
||||||
getLog().warn("Fork mode disabled, ignoring agent");
|
|
||||||
}
|
|
||||||
if (hasJvmArgs()) {
|
|
||||||
getLog().warn(
|
|
||||||
"Fork mode disabled, ignoring JVM argument(s) ["
|
|
||||||
+ this.jvmArguments + "]");
|
|
||||||
}
|
|
||||||
runWithMavenJvm(startClassName, resolveApplicationArguments().asArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doRunWithForkedJvm(String startClassName)
|
|
||||||
throws MojoExecutionException, MojoFailureException {
|
|
||||||
List<String> args = new ArrayList<String>();
|
|
||||||
addAgents(args);
|
|
||||||
addJvmArgs(args);
|
|
||||||
addClasspath(args);
|
|
||||||
args.add(startClassName);
|
|
||||||
addArgs(args);
|
|
||||||
runWithForkedJvm(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run with a forked VM, using the specified command line arguments.
|
|
||||||
* @param args the arguments (JVM arguments and application arguments)
|
|
||||||
* @throws MojoExecutionException
|
|
||||||
* @throws MojoFailureException
|
|
||||||
*/
|
|
||||||
protected abstract void runWithForkedJvm(List<String> args) throws MojoExecutionException, MojoFailureException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run with the current VM, using the specified arguments.
|
|
||||||
* @param startClassName the class to run
|
|
||||||
* @param arguments the class arguments
|
|
||||||
* @throws MojoExecutionException
|
|
||||||
* @throws MojoFailureException
|
|
||||||
*/
|
|
||||||
protected abstract void runWithMavenJvm(String startClassName, String... arguments)
|
|
||||||
throws MojoExecutionException, MojoFailureException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the application arguments to use.
|
|
||||||
* @return a {@link RunArguments} defining the application arguments
|
|
||||||
*/
|
|
||||||
protected RunArguments resolveApplicationArguments() {
|
|
||||||
return new RunArguments(this.arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addArgs(List<String> args) {
|
|
||||||
RunArguments applicationArguments = resolveApplicationArguments();
|
|
||||||
Collections.addAll(args, applicationArguments.asArray());
|
|
||||||
logArguments("Application argument(s): ", this.arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resolve the JVM arguments to use.
|
|
||||||
* @return a {@link RunArguments} defining the JVM arguments
|
|
||||||
*/
|
|
||||||
protected RunArguments resolveJvmArguments() {
|
|
||||||
return new RunArguments(this.jvmArguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addJvmArgs(List<String> args) {
|
|
||||||
RunArguments jvmArguments = resolveJvmArguments();
|
|
||||||
Collections.addAll(args, jvmArguments.asArray());
|
|
||||||
logArguments("JVM argument(s): ", jvmArguments.asArray());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addAgents(List<String> args) {
|
|
||||||
if (this.agent != null) {
|
|
||||||
getLog().info("Attaching agents: " + Arrays.asList(this.agent));
|
|
||||||
for (File agent : this.agent) {
|
|
||||||
args.add("-javaagent:" + agent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (this.noverify) {
|
|
||||||
args.add("-noverify");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addClasspath(List<String> args) throws MojoExecutionException {
|
|
||||||
try {
|
|
||||||
StringBuilder classpath = new StringBuilder();
|
|
||||||
for (URL ele : getClassPathUrls()) {
|
|
||||||
classpath = classpath.append((classpath.length() > 0 ? File.pathSeparator
|
|
||||||
: "") + new File(ele.toURI()));
|
|
||||||
}
|
|
||||||
getLog().debug("Classpath for forked process: " + classpath);
|
|
||||||
args.add("-cp");
|
|
||||||
args.add(classpath.toString());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new MojoExecutionException("Could not build classpath", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final String getStartClass() throws MojoExecutionException {
|
|
||||||
String mainClass = this.mainClass;
|
|
||||||
if (mainClass == null) {
|
|
||||||
try {
|
|
||||||
mainClass = MainClassFinder.findSingleMainClass(this.classesDirectory);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new MojoExecutionException(ex.getMessage(), ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mainClass == null) {
|
|
||||||
throw new MojoExecutionException("Unable to find a suitable main class, "
|
|
||||||
+ "please add a 'mainClass' property");
|
|
||||||
}
|
|
||||||
return mainClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected URL[] getClassPathUrls() throws MojoExecutionException {
|
|
||||||
try {
|
|
||||||
List<URL> urls = new ArrayList<URL>();
|
|
||||||
addUserDefinedFolders(urls);
|
|
||||||
addResources(urls);
|
|
||||||
addProjectClasses(urls);
|
|
||||||
addDependencies(urls);
|
|
||||||
return urls.toArray(new URL[urls.size()]);
|
|
||||||
}
|
|
||||||
catch (MalformedURLException ex) {
|
|
||||||
throw new MojoExecutionException("Unable to build classpath", ex);
|
|
||||||
}
|
|
||||||
catch (IOException ex) {
|
|
||||||
throw new MojoExecutionException("Unable to build classpath", ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addUserDefinedFolders(List<URL> urls) throws MalformedURLException {
|
|
||||||
if (this.folders != null) {
|
|
||||||
for (String folder : this.folders) {
|
|
||||||
urls.add(new File(folder).toURI().toURL());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addResources(List<URL> urls) throws IOException {
|
|
||||||
if (this.addResources) {
|
|
||||||
for (Resource resource : this.project.getResources()) {
|
|
||||||
File directory = new File(resource.getDirectory());
|
|
||||||
urls.add(directory.toURI().toURL());
|
|
||||||
FileUtils.removeDuplicatesFromOutputDirectory(this.classesDirectory,
|
|
||||||
directory);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addProjectClasses(List<URL> urls) throws MalformedURLException {
|
|
||||||
urls.add(this.classesDirectory.toURI().toURL());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void addDependencies(List<URL> urls) throws MalformedURLException,
|
|
||||||
MojoExecutionException {
|
|
||||||
FilterArtifacts filters = getFilters(new TestArtifactFilter());
|
|
||||||
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), filters);
|
|
||||||
for (Artifact artifact : artifacts) {
|
|
||||||
if (artifact.getFile() != null) {
|
|
||||||
urls.add(artifact.getFile().toURI().toURL());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void logArguments(String message, String[] args) {
|
|
||||||
StringBuffer sb = new StringBuffer(message);
|
|
||||||
for (String arg : args) {
|
|
||||||
sb.append(arg).append(" ");
|
|
||||||
}
|
|
||||||
getLog().debug(sb.toString().trim());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static class TestArtifactFilter extends AbstractArtifactFeatureFilter {
|
|
||||||
public TestArtifactFilter() {
|
|
||||||
super("", Artifact.SCOPE_TEST);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected String getArtifactFeature(Artifact artifact) {
|
|
||||||
return artifact.getScope();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Isolated {@link ThreadGroup} to capture uncaught exceptions.
|
|
||||||
*/
|
|
||||||
class IsolatedThreadGroup extends ThreadGroup {
|
|
||||||
|
|
||||||
private Throwable exception;
|
|
||||||
|
|
||||||
public IsolatedThreadGroup(String name) {
|
|
||||||
super(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void uncaughtException(Thread thread, Throwable ex) {
|
|
||||||
if (!(ex instanceof ThreadDeath)) {
|
|
||||||
synchronized (this) {
|
|
||||||
this.exception = (this.exception == null ? ex : this.exception);
|
|
||||||
}
|
|
||||||
getLog().warn(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized void rethrowUncaughtException() throws MojoExecutionException {
|
|
||||||
if (this.exception != null) {
|
|
||||||
throw new MojoExecutionException("An exception occured while running. "
|
|
||||||
+ this.exception.getMessage(), this.exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runner used to launch the application.
|
|
||||||
*/
|
|
||||||
class LaunchRunner implements Runnable {
|
|
||||||
|
|
||||||
private final String startClassName;
|
|
||||||
private final String[] args;
|
|
||||||
|
|
||||||
public LaunchRunner(String startClassName, String... args) {
|
|
||||||
this.startClassName = startClassName;
|
|
||||||
this.args = (args != null ? args : new String[] {});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Thread thread = Thread.currentThread();
|
|
||||||
ClassLoader classLoader = thread.getContextClassLoader();
|
|
||||||
try {
|
|
||||||
Class<?> startClass = classLoader.loadClass(this.startClassName);
|
|
||||||
Method mainMethod = startClass.getMethod("main", String[].class);
|
|
||||||
if (!mainMethod.isAccessible()) {
|
|
||||||
mainMethod.setAccessible(true);
|
|
||||||
}
|
|
||||||
mainMethod.invoke(null, new Object[] {this.args});
|
|
||||||
}
|
|
||||||
catch (NoSuchMethodException ex) {
|
|
||||||
Exception wrappedEx = new Exception(
|
|
||||||
"The specified mainClass doesn't contain a "
|
|
||||||
+ "main method with appropriate signature.", ex);
|
|
||||||
thread.getThreadGroup().uncaughtException(thread, wrappedEx);
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
thread.getThreadGroup().uncaughtException(thread, ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2015 the original author or authors.
|
* Copyright 2012-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,13 +16,10 @@
|
||||||
|
|
||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import org.codehaus.plexus.util.cli.CommandLineUtils;
|
import org.codehaus.plexus.util.cli.CommandLineUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse and expose arguments specified in a single string.
|
* Parse and expose arguments specified as {@link RunMojo} parameters.
|
||||||
*
|
*
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
* @since 1.1.0
|
* @since 1.1.0
|
||||||
|
@ -31,25 +28,13 @@ class RunArguments {
|
||||||
|
|
||||||
private static final String[] NO_ARGS = {};
|
private static final String[] NO_ARGS = {};
|
||||||
|
|
||||||
private final LinkedList<String> args;
|
private final String[] args;
|
||||||
|
|
||||||
public RunArguments(String arguments) {
|
public RunArguments(String arguments) {
|
||||||
this(parseArgs(arguments));
|
this.args = parseArgs(arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
public RunArguments(String[] args) {
|
private String[] parseArgs(String arguments) {
|
||||||
this.args = new LinkedList<String>(Arrays.asList(args));
|
|
||||||
}
|
|
||||||
|
|
||||||
public LinkedList<String> getArgs() {
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String[] asArray() {
|
|
||||||
return this.args.toArray(new String[this.args.size()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String[] parseArgs(String arguments) {
|
|
||||||
if (arguments == null || arguments.trim().isEmpty()) {
|
if (arguments == null || arguments.trim().isEmpty()) {
|
||||||
return NO_ARGS;
|
return NO_ARGS;
|
||||||
}
|
}
|
||||||
|
@ -63,4 +48,8 @@ class RunArguments {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String[] asArray() {
|
||||||
|
return this.args;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2012-2015 the original author or authors.
|
* Copyright 2012-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
|
@ -16,16 +16,34 @@
|
||||||
|
|
||||||
package org.springframework.boot.maven;
|
package org.springframework.boot.maven;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.security.CodeSource;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.apache.maven.artifact.Artifact;
|
||||||
|
import org.apache.maven.model.Resource;
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
import org.apache.maven.plugin.MojoExecutionException;
|
||||||
|
import org.apache.maven.plugin.MojoFailureException;
|
||||||
import org.apache.maven.plugins.annotations.Execute;
|
import org.apache.maven.plugins.annotations.Execute;
|
||||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
||||||
import org.apache.maven.plugins.annotations.Mojo;
|
import org.apache.maven.plugins.annotations.Mojo;
|
||||||
|
import org.apache.maven.plugins.annotations.Parameter;
|
||||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
import org.apache.maven.plugins.annotations.ResolutionScope;
|
||||||
|
import org.apache.maven.project.MavenProject;
|
||||||
|
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactFeatureFilter;
|
||||||
|
import org.apache.maven.shared.artifact.filter.collection.FilterArtifacts;
|
||||||
|
import org.springframework.boot.loader.tools.FileUtils;
|
||||||
import org.springframework.boot.loader.tools.JavaExecutable;
|
import org.springframework.boot.loader.tools.JavaExecutable;
|
||||||
|
import org.springframework.boot.loader.tools.MainClassFinder;
|
||||||
import org.springframework.boot.loader.tools.RunProcess;
|
import org.springframework.boot.loader.tools.RunProcess;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,15 +51,152 @@ import org.springframework.boot.loader.tools.RunProcess;
|
||||||
*
|
*
|
||||||
* @author Phillip Webb
|
* @author Phillip Webb
|
||||||
* @author Stephane Nicoll
|
* @author Stephane Nicoll
|
||||||
|
* @author David Liu
|
||||||
*/
|
*/
|
||||||
@Mojo(name = "run", requiresProject = true, defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.TEST)
|
@Mojo(name = "run", requiresProject = true, defaultPhase = LifecyclePhase.VALIDATE, requiresDependencyResolution = ResolutionScope.TEST)
|
||||||
@Execute(phase = LifecyclePhase.TEST_COMPILE)
|
@Execute(phase = LifecyclePhase.TEST_COMPILE)
|
||||||
public class RunMojo extends AbstractRunMojo {
|
public class RunMojo extends AbstractDependencyFilterMojo {
|
||||||
|
|
||||||
|
private static final String SPRING_LOADED_AGENT_CLASSNAME = "org.springsource.loaded.agent.SpringLoadedAgent";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Maven project.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "${project}", readonly = true, required = true)
|
||||||
|
private MavenProject project;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add maven resources to the classpath directly, this allows live in-place editing of
|
||||||
|
* resources. Duplicate resources are removed from {@code target/classes} to prevent
|
||||||
|
* them to appear twice if {@code ClassLoader.getResources()} is called.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "run.addResources", defaultValue = "true")
|
||||||
|
private boolean addResources;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Path to agent jar. NOTE: the use of agents means that processes will be started by
|
||||||
|
* forking a new JVM.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "run.agent")
|
||||||
|
private File[] agent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to say that the agent requires -noverify.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "run.noverify")
|
||||||
|
private Boolean noverify;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* JVM arguments that should be associated with the forked process used to run the
|
||||||
|
* application. On command line, make sure to wrap multiple values between quotes.
|
||||||
|
* @since 1.1
|
||||||
|
*/
|
||||||
|
@Parameter(property = "run.jvmArguments")
|
||||||
|
private String jvmArguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Arguments that should be passed to the application. On command line use commas to
|
||||||
|
* separate multiple arguments.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(property = "run.arguments")
|
||||||
|
private String[] arguments;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the main class. If not specified the first compiled class found that
|
||||||
|
* contains a 'main' method will be used.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private String mainClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Additional folders besides the classes directory that should be added to the
|
||||||
|
* classpath.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter
|
||||||
|
private String[] folders;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Directory containing the classes and resource files that should be packaged into
|
||||||
|
* the archive.
|
||||||
|
* @since 1.0
|
||||||
|
*/
|
||||||
|
@Parameter(defaultValue = "${project.build.outputDirectory}", required = true)
|
||||||
|
private File classesDirectory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flag to indicate if the run processes should be forked. By default process forking
|
||||||
|
* is only used if an agent or jvmArguments are specified.
|
||||||
|
* @since 1.2
|
||||||
|
*/
|
||||||
|
@Parameter(property = "fork")
|
||||||
|
private Boolean fork;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void runWithForkedJvm(List<String> args) throws MojoExecutionException {
|
public void execute() throws MojoExecutionException, MojoFailureException {
|
||||||
|
final String startClassName = getStartClass();
|
||||||
|
run(startClassName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void findAgent() {
|
||||||
try {
|
try {
|
||||||
new RunProcess(new JavaExecutable().toString()).run(true, args
|
if (this.agent == null || this.agent.length == 0) {
|
||||||
|
Class<?> loaded = Class.forName(SPRING_LOADED_AGENT_CLASSNAME);
|
||||||
|
if (loaded != null) {
|
||||||
|
if (this.noverify == null) {
|
||||||
|
this.noverify = true;
|
||||||
|
}
|
||||||
|
CodeSource source = loaded.getProtectionDomain().getCodeSource();
|
||||||
|
if (source != null) {
|
||||||
|
this.agent = new File[] { new File(source.getLocation().getFile()) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (ClassNotFoundException ex) {
|
||||||
|
// ignore;
|
||||||
|
}
|
||||||
|
if (this.noverify == null) {
|
||||||
|
this.noverify = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void run(String startClassName) throws MojoExecutionException {
|
||||||
|
findAgent();
|
||||||
|
boolean hasAgent = (this.agent != null && this.agent.length > 0);
|
||||||
|
boolean hasJvmArgs = (this.jvmArguments != null && this.jvmArguments.length() > 0);
|
||||||
|
if (Boolean.TRUE.equals(this.fork)
|
||||||
|
|| (this.fork == null && (hasAgent || hasJvmArgs))) {
|
||||||
|
runWithForkedJvm(startClassName);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (hasAgent) {
|
||||||
|
getLog().warn("Fork mode disabled, ignoring agent");
|
||||||
|
}
|
||||||
|
if (hasJvmArgs) {
|
||||||
|
getLog().warn(
|
||||||
|
"Fork mode disabled, ignoring JVM argument(s) ["
|
||||||
|
+ this.jvmArguments + "]");
|
||||||
|
}
|
||||||
|
runWithMavenJvm(startClassName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runWithForkedJvm(String startClassName) throws MojoExecutionException {
|
||||||
|
List<String> args = new ArrayList<String>();
|
||||||
|
addAgents(args);
|
||||||
|
addJvmArgs(args);
|
||||||
|
addClasspath(args);
|
||||||
|
args.add(startClassName);
|
||||||
|
addArgs(args);
|
||||||
|
try {
|
||||||
|
new RunProcess(new JavaExecutable().toString()).run(args
|
||||||
.toArray(new String[args.size()]));
|
.toArray(new String[args.size()]));
|
||||||
}
|
}
|
||||||
catch (Exception ex) {
|
catch (Exception ex) {
|
||||||
|
@ -49,16 +204,131 @@ public class RunMojo extends AbstractRunMojo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void runWithMavenJvm(String startClassName, String... arguments) throws MojoExecutionException {
|
private void runWithMavenJvm(String startClassName) throws MojoExecutionException {
|
||||||
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
|
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
|
||||||
Thread launchThread = new Thread(threadGroup, new LaunchRunner(startClassName,
|
Thread launchThread = new Thread(threadGroup, new LaunchRunner(startClassName,
|
||||||
arguments), startClassName + ".main()");
|
this.arguments), startClassName + ".main()");
|
||||||
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
|
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
|
||||||
launchThread.start();
|
launchThread.start();
|
||||||
join(threadGroup);
|
join(threadGroup);
|
||||||
threadGroup.rethrowUncaughtException();
|
threadGroup.rethrowUncaughtException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void addAgents(List<String> args) {
|
||||||
|
if (this.agent != null) {
|
||||||
|
getLog().info("Attaching agents: " + Arrays.asList(this.agent));
|
||||||
|
for (File agent : this.agent) {
|
||||||
|
args.add("-javaagent:" + agent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (this.noverify) {
|
||||||
|
args.add("-noverify");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addJvmArgs(List<String> args) {
|
||||||
|
RunArguments jvmArguments = new RunArguments(this.jvmArguments);
|
||||||
|
Collections.addAll(args, jvmArguments.asArray());
|
||||||
|
logArguments("JVM argument(s): ", jvmArguments.asArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addClasspath(List<String> args) throws MojoExecutionException {
|
||||||
|
try {
|
||||||
|
StringBuilder classpath = new StringBuilder();
|
||||||
|
for (URL ele : getClassPathUrls()) {
|
||||||
|
classpath = classpath.append((classpath.length() > 0 ? File.pathSeparator
|
||||||
|
: "") + new File(ele.toURI()));
|
||||||
|
}
|
||||||
|
getLog().debug("Classpath for forked process: " + classpath);
|
||||||
|
args.add("-cp");
|
||||||
|
args.add(classpath.toString());
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new MojoExecutionException("Could not build classpath", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addArgs(List<String> args) {
|
||||||
|
Collections.addAll(args, this.arguments);
|
||||||
|
logArguments("Application argument(s): ", this.arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private final String getStartClass() throws MojoExecutionException {
|
||||||
|
String mainClass = this.mainClass;
|
||||||
|
if (mainClass == null) {
|
||||||
|
try {
|
||||||
|
mainClass = MainClassFinder.findSingleMainClass(this.classesDirectory);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new MojoExecutionException(ex.getMessage(), ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (mainClass == null) {
|
||||||
|
throw new MojoExecutionException("Unable to find a suitable main class, "
|
||||||
|
+ "please add a 'mainClass' property");
|
||||||
|
}
|
||||||
|
return mainClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
private URL[] getClassPathUrls() throws MojoExecutionException {
|
||||||
|
try {
|
||||||
|
List<URL> urls = new ArrayList<URL>();
|
||||||
|
addUserDefinedFolders(urls);
|
||||||
|
addResources(urls);
|
||||||
|
addProjectClasses(urls);
|
||||||
|
addDependencies(urls);
|
||||||
|
return urls.toArray(new URL[urls.size()]);
|
||||||
|
}
|
||||||
|
catch (MalformedURLException ex) {
|
||||||
|
throw new MojoExecutionException("Unable to build classpath", ex);
|
||||||
|
}
|
||||||
|
catch (IOException ex) {
|
||||||
|
throw new MojoExecutionException("Unable to build classpath", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addUserDefinedFolders(List<URL> urls) throws MalformedURLException {
|
||||||
|
if (this.folders != null) {
|
||||||
|
for (String folder : this.folders) {
|
||||||
|
urls.add(new File(folder).toURI().toURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addResources(List<URL> urls) throws IOException {
|
||||||
|
if (this.addResources) {
|
||||||
|
for (Resource resource : this.project.getResources()) {
|
||||||
|
File directory = new File(resource.getDirectory());
|
||||||
|
urls.add(directory.toURI().toURL());
|
||||||
|
FileUtils.removeDuplicatesFromOutputDirectory(this.classesDirectory,
|
||||||
|
directory);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addProjectClasses(List<URL> urls) throws MalformedURLException {
|
||||||
|
urls.add(this.classesDirectory.toURI().toURL());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addDependencies(List<URL> urls) throws MalformedURLException,
|
||||||
|
MojoExecutionException {
|
||||||
|
FilterArtifacts filters = getFilters(new TestArtifactFilter());
|
||||||
|
Set<Artifact> artifacts = filterDependencies(this.project.getArtifacts(), filters);
|
||||||
|
for (Artifact artifact : artifacts) {
|
||||||
|
if (artifact.getFile() != null) {
|
||||||
|
urls.add(artifact.getFile().toURI().toURL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logArguments(String message, String[] args) {
|
||||||
|
StringBuffer sb = new StringBuffer(message);
|
||||||
|
for (String arg : args) {
|
||||||
|
sb.append(arg).append(" ");
|
||||||
|
}
|
||||||
|
getLog().debug(sb.toString().trim());
|
||||||
|
}
|
||||||
|
|
||||||
private void join(ThreadGroup threadGroup) {
|
private void join(ThreadGroup threadGroup) {
|
||||||
boolean hasNonDaemonThreads;
|
boolean hasNonDaemonThreads;
|
||||||
do {
|
do {
|
||||||
|
@ -80,4 +350,84 @@ public class RunMojo extends AbstractRunMojo {
|
||||||
while (hasNonDaemonThreads);
|
while (hasNonDaemonThreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class TestArtifactFilter extends AbstractArtifactFeatureFilter {
|
||||||
|
public TestArtifactFilter() {
|
||||||
|
super("", Artifact.SCOPE_TEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getArtifactFeature(Artifact artifact) {
|
||||||
|
return artifact.getScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Isolated {@link ThreadGroup} to capture uncaught exceptions.
|
||||||
|
*/
|
||||||
|
class IsolatedThreadGroup extends ThreadGroup {
|
||||||
|
|
||||||
|
private Throwable exception;
|
||||||
|
|
||||||
|
public IsolatedThreadGroup(String name) {
|
||||||
|
super(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void uncaughtException(Thread thread, Throwable ex) {
|
||||||
|
if (!(ex instanceof ThreadDeath)) {
|
||||||
|
synchronized (this) {
|
||||||
|
this.exception = (this.exception == null ? ex : this.exception);
|
||||||
|
}
|
||||||
|
getLog().warn(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void rethrowUncaughtException() throws MojoExecutionException {
|
||||||
|
if (this.exception != null) {
|
||||||
|
throw new MojoExecutionException("An exception occured while running. "
|
||||||
|
+ this.exception.getMessage(), this.exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runner used to launch the application.
|
||||||
|
*/
|
||||||
|
class LaunchRunner implements Runnable {
|
||||||
|
|
||||||
|
private final String startClassName;
|
||||||
|
private final String[] args;
|
||||||
|
|
||||||
|
public LaunchRunner(String startClassName, String... args) {
|
||||||
|
this.startClassName = startClassName;
|
||||||
|
this.args = (args != null ? args : new String[] {});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
Thread thread = Thread.currentThread();
|
||||||
|
ClassLoader classLoader = thread.getContextClassLoader();
|
||||||
|
try {
|
||||||
|
Class<?> startClass = classLoader.loadClass(this.startClassName);
|
||||||
|
Method mainMethod = startClass.getMethod("main",
|
||||||
|
new Class[] { String[].class });
|
||||||
|
if (!mainMethod.isAccessible()) {
|
||||||
|
mainMethod.setAccessible(true);
|
||||||
|
}
|
||||||
|
mainMethod.invoke(null, new Object[] { this.args });
|
||||||
|
}
|
||||||
|
catch (NoSuchMethodException ex) {
|
||||||
|
Exception wrappedEx = new Exception(
|
||||||
|
"The specified mainClass doesn't contain a "
|
||||||
|
+ "main method with appropriate signature.", ex);
|
||||||
|
thread.getThreadGroup().uncaughtException(thread, wrappedEx);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
thread.getThreadGroup().uncaughtException(thread, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,123 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.maven;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import javax.management.AttributeNotFoundException;
|
|
||||||
import javax.management.InstanceNotFoundException;
|
|
||||||
import javax.management.MBeanException;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.MalformedObjectNameException;
|
|
||||||
import javax.management.ObjectName;
|
|
||||||
import javax.management.ReflectionException;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
import javax.management.remote.JMXConnectorFactory;
|
|
||||||
import javax.management.remote.JMXServiceURL;
|
|
||||||
|
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A JMX client for the {@code SpringApplicationLifecycle} mbean. Permits to obtain
|
|
||||||
* information about the lifecycle of a given Spring application.
|
|
||||||
*
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.3.0
|
|
||||||
*/
|
|
||||||
class SpringApplicationLifecycleClient {
|
|
||||||
|
|
||||||
//Note: see org.springframework.boot.autoconfigure.test.SpringApplicationLifecycleAutoConfiguration
|
|
||||||
static final String DEFAULT_OBJECT_NAME =
|
|
||||||
"org.springframework.boot:type=Lifecycle,name=springApplicationLifecycle";
|
|
||||||
|
|
||||||
private final MBeanServerConnection mBeanServerConnection;
|
|
||||||
|
|
||||||
private final ObjectName objectName;
|
|
||||||
|
|
||||||
public SpringApplicationLifecycleClient(MBeanServerConnection mBeanServerConnection, String jmxName) {
|
|
||||||
this.mBeanServerConnection = mBeanServerConnection;
|
|
||||||
this.objectName = toObjectName(jmxName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a connector for an {@link javax.management.MBeanServer} exposed on the
|
|
||||||
* current machine and the current port. Security should be disabled.
|
|
||||||
* @param port the port on which the mbean server is exposed
|
|
||||||
* @return a connection
|
|
||||||
* @throws IOException if the connection to that server failed
|
|
||||||
*/
|
|
||||||
public static JMXConnector createLocalJmxConnector(int port) throws IOException {
|
|
||||||
String url = "service:jmx:rmi:///jndi/rmi://127.0.0.1:" + port + "/jmxrmi";
|
|
||||||
JMXServiceURL serviceUrl = new JMXServiceURL(url);
|
|
||||||
return JMXConnectorFactory.connect(serviceUrl, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the spring application managed by this instance is ready.
|
|
||||||
* <p>Returns {@code false} if the mbean is not yet deployed so this method
|
|
||||||
* should be repeatedly called until a timeout is reached.
|
|
||||||
* @return {@code true} if the application is ready to service requests
|
|
||||||
* @throws MojoExecutionException if the JMX service could not be contacted
|
|
||||||
*/
|
|
||||||
public boolean isReady() throws MojoExecutionException {
|
|
||||||
try {
|
|
||||||
return (Boolean) this.mBeanServerConnection.getAttribute(this.objectName, "Ready");
|
|
||||||
}
|
|
||||||
catch (InstanceNotFoundException e) {
|
|
||||||
return false; // Instance not available yet
|
|
||||||
}
|
|
||||||
catch (AttributeNotFoundException e) {
|
|
||||||
throw new IllegalStateException("Unexpected: attribute 'Ready' not available", e);
|
|
||||||
}
|
|
||||||
catch (ReflectionException e) {
|
|
||||||
throw new MojoExecutionException("Failed to retrieve Ready attribute", e.getCause());
|
|
||||||
}
|
|
||||||
catch (MBeanException e) {
|
|
||||||
throw new MojoExecutionException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new MojoExecutionException(e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the application managed by this instance.
|
|
||||||
* @throws MojoExecutionException if the JMX service could not be contacted
|
|
||||||
* @throws IOException if an I/O error occurs
|
|
||||||
* @throws InstanceNotFoundException if the lifecycle mbean cannot be found
|
|
||||||
*/
|
|
||||||
public void stop() throws MojoExecutionException, IOException, InstanceNotFoundException {
|
|
||||||
try {
|
|
||||||
this.mBeanServerConnection.invoke(this.objectName, "shutdown", null, null);
|
|
||||||
}
|
|
||||||
catch (ReflectionException e) {
|
|
||||||
throw new MojoExecutionException("Shutdown failed", e.getCause());
|
|
||||||
}
|
|
||||||
catch (MBeanException e) {
|
|
||||||
throw new MojoExecutionException("Could not invoke shutdown operation", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private ObjectName toObjectName(String name) {
|
|
||||||
try {
|
|
||||||
return new ObjectName(name);
|
|
||||||
}
|
|
||||||
catch (MalformedObjectNameException ex) {
|
|
||||||
throw new IllegalArgumentException("Invalid jmx name '" + name + "'");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,287 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.maven;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import java.net.ConnectException;
|
|
||||||
import java.net.URLClassLoader;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.ReflectionException;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
|
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
|
||||||
import org.apache.maven.plugin.MojoFailureException;
|
|
||||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
|
||||||
import org.apache.maven.plugins.annotations.Mojo;
|
|
||||||
import org.apache.maven.plugins.annotations.Parameter;
|
|
||||||
import org.apache.maven.plugins.annotations.ResolutionScope;
|
|
||||||
|
|
||||||
import org.springframework.boot.loader.tools.JavaExecutable;
|
|
||||||
import org.springframework.boot.loader.tools.RunProcess;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a spring application. Contrary to the {@code run} goal, this does not
|
|
||||||
* block and allows other goal to operate on the application. This goal is typically
|
|
||||||
* used in integration test scenario where the application is started before a test
|
|
||||||
* suite and stopped after.
|
|
||||||
*
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.3.0
|
|
||||||
* @see StopMojo
|
|
||||||
*/
|
|
||||||
@Mojo(name = "start", requiresProject = true, defaultPhase = LifecyclePhase.PRE_INTEGRATION_TEST, requiresDependencyResolution = ResolutionScope.TEST)
|
|
||||||
public class StartMojo extends AbstractRunMojo {
|
|
||||||
|
|
||||||
private static final String ENABLE_MBEAN_PROPERTY = "--spring.application.lifecycle.enabled=true";
|
|
||||||
|
|
||||||
private static final String JMX_NAME_PROPERTY_PREFIX = "--spring.application.lifecycle.jmx-name=";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The JMX name of the automatically deployed MBean managing the lifecycle
|
|
||||||
* of the spring application.
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private String jmxName = SpringApplicationLifecycleClient.DEFAULT_OBJECT_NAME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port to use to expose the platform MBeanServer if the application
|
|
||||||
* needs to be forked.
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private int jmxPort = 9001;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The number of milli-seconds to wait between each attempt to check if the
|
|
||||||
* spring application is ready.
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private long wait = 500;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum number of attempts to check if the spring application is
|
|
||||||
* ready. Combined with the "wait" argument, this gives a global timeout
|
|
||||||
* value (30 sec by default)
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private int maxAttempts = 60;
|
|
||||||
|
|
||||||
private final Object lock = new Object();
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void runWithForkedJvm(List<String> args) throws MojoExecutionException, MojoFailureException {
|
|
||||||
RunProcess runProcess;
|
|
||||||
try {
|
|
||||||
runProcess = new RunProcess(new JavaExecutable().toString());
|
|
||||||
runProcess.run(false, args.toArray(new String[args.size()]));
|
|
||||||
}
|
|
||||||
catch (Exception ex) {
|
|
||||||
throw new MojoExecutionException("Could not exec java", ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
waitForSpringApplication();
|
|
||||||
}
|
|
||||||
catch (MojoExecutionException e) {
|
|
||||||
runProcess.kill();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (MojoFailureException e) {
|
|
||||||
runProcess.kill();
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RunArguments resolveApplicationArguments() {
|
|
||||||
RunArguments applicationArguments = super.resolveApplicationArguments();
|
|
||||||
applicationArguments.getArgs().addLast(ENABLE_MBEAN_PROPERTY);
|
|
||||||
if (isFork()) {
|
|
||||||
applicationArguments.getArgs().addLast(JMX_NAME_PROPERTY_PREFIX + this.jmxName);
|
|
||||||
}
|
|
||||||
return applicationArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected RunArguments resolveJvmArguments() {
|
|
||||||
RunArguments jvmArguments = super.resolveJvmArguments();
|
|
||||||
if (isFork()) {
|
|
||||||
List<String> remoteJmxArguments = new ArrayList<String>();
|
|
||||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote");
|
|
||||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.port=" + jmxPort);
|
|
||||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.authenticate=false");
|
|
||||||
remoteJmxArguments.add("-Dcom.sun.management.jmxremote.ssl=false");
|
|
||||||
jvmArguments.getArgs().addAll(remoteJmxArguments);
|
|
||||||
}
|
|
||||||
return jvmArguments;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void runWithMavenJvm(String startClassName, String... arguments) throws MojoExecutionException {
|
|
||||||
IsolatedThreadGroup threadGroup = new IsolatedThreadGroup(startClassName);
|
|
||||||
Thread launchThread = new Thread(threadGroup, new LaunchRunner(startClassName,
|
|
||||||
arguments), startClassName + ".main()");
|
|
||||||
launchThread.setContextClassLoader(new URLClassLoader(getClassPathUrls()));
|
|
||||||
launchThread.start();
|
|
||||||
|
|
||||||
waitForSpringApplication(this.wait, this.maxAttempts);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForSpringApplication(long wait, int maxAttempts) throws MojoExecutionException {
|
|
||||||
SpringApplicationLifecycleClient helper = new SpringApplicationLifecycleClient(
|
|
||||||
ManagementFactory.getPlatformMBeanServer(), this.jmxName);
|
|
||||||
getLog().debug("Waiting for spring application to start...");
|
|
||||||
for (int i = 0; i < maxAttempts; i++) {
|
|
||||||
if (helper.isReady()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
getLog().debug("Spring application is not ready yet, waiting " + wait + "ms (attempt " + (i + 1) + ")");
|
|
||||||
synchronized (this.lock) {
|
|
||||||
try {
|
|
||||||
this.lock.wait(wait);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
throw new IllegalStateException("Interrupted while waiting for Spring Boot app to start.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new MojoExecutionException("Spring application did not start before the configured " +
|
|
||||||
"timeout (" + (wait * maxAttempts) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForSpringApplication() throws MojoFailureException, MojoExecutionException {
|
|
||||||
try {
|
|
||||||
if (Boolean.TRUE.equals(isFork())) {
|
|
||||||
waitForForkedSpringApplication();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
doWaitForSpringApplication(ManagementFactory.getPlatformMBeanServer());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw new MojoFailureException("Could not contact Spring Boot application", e);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new MojoExecutionException("Could not figure out if the application has started", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitForForkedSpringApplication() throws IOException, MojoFailureException, MojoExecutionException {
|
|
||||||
final JMXConnector jmxConnector;
|
|
||||||
try {
|
|
||||||
getLog().debug("Connecting to local MBeanServer at port " + this.jmxPort);
|
|
||||||
jmxConnector = execute(wait, maxAttempts, new RetryCallback<JMXConnector>() {
|
|
||||||
@Override
|
|
||||||
public JMXConnector retry() throws Exception {
|
|
||||||
try {
|
|
||||||
return SpringApplicationLifecycleClient.createLocalJmxConnector(jmxPort);
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
if (hasCauseWithType(e, ConnectException.class)) { // Not there yet
|
|
||||||
getLog().debug("MBean server at port " + jmxPort + " is not up yet...");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (jmxConnector == null) {
|
|
||||||
throw new MojoExecutionException("JMX MBean server was not reachable before the configured " +
|
|
||||||
"timeout (" + (this.wait * this.maxAttempts) + "ms");
|
|
||||||
}
|
|
||||||
getLog().debug("Connected to local MBeanServer at port " + this.jmxPort);
|
|
||||||
try {
|
|
||||||
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
|
|
||||||
doWaitForSpringApplication(mBeanServerConnection);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jmxConnector.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new MojoExecutionException("Failed to connect to MBean server at port " + this.jmxPort, e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doWaitForSpringApplication(MBeanServerConnection connection)
|
|
||||||
throws IOException, MojoExecutionException, MojoFailureException {
|
|
||||||
|
|
||||||
final SpringApplicationLifecycleClient client =
|
|
||||||
new SpringApplicationLifecycleClient(connection, this.jmxName);
|
|
||||||
try {
|
|
||||||
execute(this.wait, this.maxAttempts, new RetryCallback<Boolean>() {
|
|
||||||
@Override
|
|
||||||
public Boolean retry() throws Exception {
|
|
||||||
boolean ready = client.isReady();
|
|
||||||
// Wait until the app is ready
|
|
||||||
return (ready ? true : null);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (ReflectionException e) {
|
|
||||||
throw new MojoExecutionException("Unable to retrieve Ready attribute", e.getCause());
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
throw new MojoFailureException("Could not invoke shutdown operation", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T execute(long wait, int maxAttempts, RetryCallback<T> callback) throws Exception {
|
|
||||||
getLog().debug("Waiting for spring application to start...");
|
|
||||||
for (int i = 0; i < maxAttempts; i++) {
|
|
||||||
T result = callback.retry();
|
|
||||||
if (result != null) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
getLog().debug("Spring application is not ready yet, waiting " + wait + "ms (attempt " + (i + 1) + ")");
|
|
||||||
synchronized (this.lock) {
|
|
||||||
try {
|
|
||||||
this.lock.wait(wait);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
throw new IllegalStateException("Interrupted while waiting for Spring Boot app to start.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw new MojoExecutionException("Spring application did not start before the configured " +
|
|
||||||
"timeout (" + (wait * maxAttempts) + "ms");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static boolean hasCauseWithType(Throwable t, Class<? extends Exception> type) {
|
|
||||||
return type.isAssignableFrom(t.getClass()) || t.getCause() != null && hasCauseWithType(t.getCause(), type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
interface RetryCallback<T> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempt to execute an operation. Throws an exception in case of fatal
|
|
||||||
* exception, returns {@code null} to indicate another attempt should be
|
|
||||||
* made if possible.
|
|
||||||
*/
|
|
||||||
T retry() throws Exception;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,108 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.maven;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.lang.management.ManagementFactory;
|
|
||||||
import javax.management.InstanceNotFoundException;
|
|
||||||
import javax.management.MBeanServerConnection;
|
|
||||||
import javax.management.remote.JMXConnector;
|
|
||||||
|
|
||||||
import org.apache.maven.plugin.AbstractMojo;
|
|
||||||
import org.apache.maven.plugin.MojoExecutionException;
|
|
||||||
import org.apache.maven.plugin.MojoFailureException;
|
|
||||||
import org.apache.maven.plugins.annotations.LifecyclePhase;
|
|
||||||
import org.apache.maven.plugins.annotations.Mojo;
|
|
||||||
import org.apache.maven.plugins.annotations.Parameter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop a spring application that has been started by the "start" goal. Typically invoked
|
|
||||||
* once a test suite has completed.
|
|
||||||
*
|
|
||||||
* @author Stephane Nicoll
|
|
||||||
* @since 1.3.0
|
|
||||||
*/
|
|
||||||
@Mojo(name = "stop", requiresProject = true, defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)
|
|
||||||
public class StopMojo extends AbstractMojo {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Flag to indicate if the run processes should be forked. Must be aligned to the value
|
|
||||||
* used to {@link StartMojo start} the process
|
|
||||||
* @since 1.2
|
|
||||||
*/
|
|
||||||
@Parameter(property = "fork")
|
|
||||||
private Boolean fork;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The JMX name of the automatically deployed MBean managing the lifecycle
|
|
||||||
* of the application.
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private String jmxName = SpringApplicationLifecycleClient.DEFAULT_OBJECT_NAME;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The port to use to lookup the platform MBeanServer if the application
|
|
||||||
* has been forked.
|
|
||||||
*/
|
|
||||||
@Parameter
|
|
||||||
private int jmxPort = 9001;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void execute() throws MojoExecutionException, MojoFailureException {
|
|
||||||
getLog().info("Stopping application...");
|
|
||||||
try {
|
|
||||||
if (Boolean.TRUE.equals(this.fork)) {
|
|
||||||
stopForkedProcess();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (IOException e) {
|
|
||||||
// The response won't be received as the server has died - ignoring
|
|
||||||
getLog().debug("Service is not reachable anymore (" + e.getMessage() + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stop() throws IOException, MojoFailureException, MojoExecutionException {
|
|
||||||
doStop(ManagementFactory.getPlatformMBeanServer());
|
|
||||||
}
|
|
||||||
|
|
||||||
private void stopForkedProcess() throws IOException, MojoFailureException, MojoExecutionException {
|
|
||||||
JMXConnector jmxConnector = SpringApplicationLifecycleClient.createLocalJmxConnector(this.jmxPort);
|
|
||||||
try {
|
|
||||||
MBeanServerConnection mBeanServerConnection = jmxConnector.getMBeanServerConnection();
|
|
||||||
doStop(mBeanServerConnection);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
jmxConnector.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void doStop(MBeanServerConnection connection)
|
|
||||||
throws IOException, MojoExecutionException {
|
|
||||||
SpringApplicationLifecycleClient helper = new SpringApplicationLifecycleClient(connection, this.jmxName);
|
|
||||||
try {
|
|
||||||
helper.stop();
|
|
||||||
}
|
|
||||||
catch (InstanceNotFoundException e) {
|
|
||||||
throw new MojoExecutionException("Spring application lifecycle JMX bean not found (fork is " +
|
|
||||||
"" + this.fork + "). Could not stop application gracefully", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,82 +0,0 @@
|
||||||
-----
|
|
||||||
Random port for integration tests
|
|
||||||
-----
|
|
||||||
Stephane Nicoll
|
|
||||||
-----
|
|
||||||
2015-04-16
|
|
||||||
-----
|
|
||||||
|
|
||||||
One nice feature of the Spring Boot test integration is that it can allocate a free
|
|
||||||
port for the web application. When the <<<start>>> goal of the plugin is used, the
|
|
||||||
Spring Boot application is started separately, making it difficult to pass the actual
|
|
||||||
port to the integration test itself.
|
|
||||||
|
|
||||||
The example below showcases how you could achieve the same feature using the
|
|
||||||
{{{http://mojo.codehaus.org/build-helper-maven-plugin/}build-helper-plugin}}:
|
|
||||||
|
|
||||||
---
|
|
||||||
<project>
|
|
||||||
...
|
|
||||||
<build>
|
|
||||||
...
|
|
||||||
<plugins>
|
|
||||||
...
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.codehaus.mojo</groupId>
|
|
||||||
<artifactId>build-helper-maven-plugin</artifactId>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>reserve-tomcat-port</id>
|
|
||||||
<goals>
|
|
||||||
<goal>reserve-network-port</goal>
|
|
||||||
</goals>
|
|
||||||
<phase>process-resources</phase>
|
|
||||||
<configuration>
|
|
||||||
<portNames>
|
|
||||||
<portName>tomcat.http.port</portName>
|
|
||||||
</portNames>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
|
||||||
<artifactId>${project.artifactId}</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>pre-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>start</goal>
|
|
||||||
</goals>
|
|
||||||
<configuration>
|
|
||||||
<arguments>
|
|
||||||
<argument>--server.port=${tomcat.http.port}</argument>
|
|
||||||
</arguments>
|
|
||||||
</configuration>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>post-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>stop</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
<plugin>
|
|
||||||
<groupId>org.apache.maven.plugins</groupId>
|
|
||||||
<artifactId>maven-failsafe-plugin</artifactId>
|
|
||||||
<configuration>
|
|
||||||
<systemPropertyVariables>
|
|
||||||
<test.server.port>${tomcat.http.port}</test.server.port>
|
|
||||||
</systemPropertyVariables>
|
|
||||||
</configuration>
|
|
||||||
</plugin>
|
|
||||||
...
|
|
||||||
</plugins>
|
|
||||||
...
|
|
||||||
</build>
|
|
||||||
---
|
|
||||||
|
|
||||||
You can now retrieve the <<<test.server.port>>> system property in any of your integration test
|
|
||||||
to create a proper <<<URL>>> to the server.
|
|
|
@ -19,10 +19,6 @@ Spring Boot Maven Plugin
|
||||||
|
|
||||||
* {{{./repackage-mojo.html}spring-boot:repackage}} repackages your jar/war to be executable.
|
* {{{./repackage-mojo.html}spring-boot:repackage}} repackages your jar/war to be executable.
|
||||||
|
|
||||||
* {{{./start-mojo.html}spring-boot:start}} and {{{./stop-mojo.html}spring-boot:stop}} to manage
|
|
||||||
the lifecycle of your Spring Boot application (i.e. for integration tests).
|
|
||||||
|
|
||||||
|
|
||||||
* Usage
|
* Usage
|
||||||
|
|
||||||
General instructions on how to use the Spring Boot Plugin can be found on the {{{./usage.html}usage page}}. Some
|
General instructions on how to use the Spring Boot Plugin can be found on the {{{./usage.html}usage page}}. Some
|
||||||
|
@ -46,8 +42,6 @@ Spring Boot Maven Plugin
|
||||||
|
|
||||||
* {{{./examples/run-debug.html}Debug the application}}
|
* {{{./examples/run-debug.html}Debug the application}}
|
||||||
|
|
||||||
* {{{./examples/it-random-port.html}Random port for integration tests}}
|
|
||||||
|
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -9,19 +9,8 @@
|
||||||
|
|
||||||
Usage
|
Usage
|
||||||
|
|
||||||
The plugin provides several goals to work with a Spring Boot application:
|
The plugin offers a goal for repackaging an application as an executable JAR/WAR as well as a goal
|
||||||
|
for running the application.
|
||||||
* <<<repackage>>>: create a jar or war file that is auto-executable. It can replace the regular artifact or can be
|
|
||||||
attached to the build lifecyle with a separate <<classifier>>.
|
|
||||||
|
|
||||||
* <<<run>>>: run your Spring Boot application with several options to pass parameters to it.
|
|
||||||
|
|
||||||
* <<<start>>> and <<<stop>>>: integrate your Spring Boot application to the <<<integration-test>>> phase so that
|
|
||||||
the application starts before it.
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
||||||
Each goal is further described below.
|
|
||||||
|
|
||||||
* Repackaging an application
|
* Repackaging an application
|
||||||
|
|
||||||
|
@ -151,49 +140,3 @@ mvn spring-boot:run
|
||||||
in such a way that any dependency that is excluded in the plugin's configuration gets excluded
|
in such a way that any dependency that is excluded in the plugin's configuration gets excluded
|
||||||
from the classpath as well. See {{{./examples/exclude-dependency.html}Exclude a dependency}} for
|
from the classpath as well. See {{{./examples/exclude-dependency.html}Exclude a dependency}} for
|
||||||
more details.
|
more details.
|
||||||
|
|
||||||
* Working with integration tests
|
|
||||||
|
|
||||||
While you may start your Spring Boot application very easily from your test (or test suite) itself,
|
|
||||||
it may be desirable to handle that in the build itself. To make sure that the lifecycle of you Spring
|
|
||||||
Boot application is properly managed <around> your integration tests, you can use the <<<start>>> and
|
|
||||||
<<<stop>>> goals as desribed below:
|
|
||||||
|
|
||||||
---
|
|
||||||
<build>
|
|
||||||
...
|
|
||||||
<plugins>
|
|
||||||
...
|
|
||||||
<plugin>
|
|
||||||
<groupId>${project.groupId}</groupId>
|
|
||||||
<artifactId>${project.artifactId}</artifactId>
|
|
||||||
<version>${project.version}</version>
|
|
||||||
<executions>
|
|
||||||
<execution>
|
|
||||||
<id>pre-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>start</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
<execution>
|
|
||||||
<id>post-integration-test</id>
|
|
||||||
<goals>
|
|
||||||
<goal>stop</goal>
|
|
||||||
</goals>
|
|
||||||
</execution>
|
|
||||||
</executions>
|
|
||||||
</plugin>
|
|
||||||
...
|
|
||||||
</plugins>
|
|
||||||
...
|
|
||||||
</build>
|
|
||||||
---
|
|
||||||
|
|
||||||
Such setup can now use the {{{http://maven.apache.org/surefire/maven-failsafe-plugin/}failsafe-plugin}} to
|
|
||||||
run your integration tests as you would expect.
|
|
||||||
|
|
||||||
For more detailed examples of how to configure this goal see:
|
|
||||||
|
|
||||||
* {{{./examples/it-random-port.html}Random port for integration tests}}
|
|
||||||
|
|
||||||
[]
|
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
<item name="Custom repackage classifier" href="examples/repackage-classifier.html"/>
|
<item name="Custom repackage classifier" href="examples/repackage-classifier.html"/>
|
||||||
<item name="Exclude a dependency" href="examples/exclude-dependency.html"/>
|
<item name="Exclude a dependency" href="examples/exclude-dependency.html"/>
|
||||||
<item name="Debug the application" href="examples/run-debug.html"/>
|
<item name="Debug the application" href="examples/run-debug.html"/>
|
||||||
<item name="Random port for integration tests" href="examples/it-random-port.html"/>
|
|
||||||
</menu>
|
</menu>
|
||||||
<menu ref="reports"/>
|
<menu ref="reports"/>
|
||||||
</body>
|
</body>
|
||||||
|
|
Loading…
Reference in New Issue