001/*
002 * Copyright 2012-2016 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.boot.maven;
018
019import java.io.IOException;
020import java.lang.management.ManagementFactory;
021
022import javax.management.InstanceNotFoundException;
023import javax.management.MBeanServerConnection;
024import javax.management.remote.JMXConnector;
025
026import org.apache.maven.plugin.AbstractMojo;
027import org.apache.maven.plugin.MojoExecutionException;
028import org.apache.maven.plugin.MojoFailureException;
029import org.apache.maven.plugins.annotations.LifecyclePhase;
030import org.apache.maven.plugins.annotations.Mojo;
031import org.apache.maven.plugins.annotations.Parameter;
032import org.apache.maven.project.MavenProject;
033
034/**
035 * Stop a spring application that has been started by the "start" goal. Typically invoked
036 * once a test suite has completed.
037 *
038 * @author Stephane Nicoll
039 * @since 1.3.0
040 */
041@Mojo(name = "stop", requiresProject = true, defaultPhase = LifecyclePhase.POST_INTEGRATION_TEST)
042public class StopMojo extends AbstractMojo {
043
044        /**
045         * The Maven project.
046         * @since 1.4.1
047         */
048        @Parameter(defaultValue = "${project}", readonly = true, required = true)
049        private MavenProject project;
050
051        /**
052         * Flag to indicate if process to stop was forked. By default, the value is inherited
053         * from the {@link MavenProject}. If it is set, it must match the value used to
054         * {@link StartMojo start} the process.
055         * @since 1.3
056         */
057        @Parameter(property = "fork")
058        private Boolean fork;
059
060        /**
061         * The JMX name of the automatically deployed MBean managing the lifecycle of the
062         * application.
063         */
064        @Parameter
065        private String jmxName = SpringApplicationAdminClient.DEFAULT_OBJECT_NAME;
066
067        /**
068         * The port to use to lookup the platform MBeanServer if the application has been
069         * forked.
070         */
071        @Parameter
072        private int jmxPort = 9001;
073
074        /**
075         * Skip the execution.
076         * @since 1.3.2
077         */
078        @Parameter(property = "skip", defaultValue = "false")
079        private boolean skip;
080
081        @Override
082        public void execute() throws MojoExecutionException, MojoFailureException {
083                if (this.skip) {
084                        getLog().debug("skipping stop as per configuration.");
085                        return;
086                }
087                getLog().info("Stopping application...");
088                try {
089                        if (isForked()) {
090                                stopForkedProcess();
091                        }
092                        else {
093                                stop();
094                        }
095                }
096                catch (IOException ex) {
097                        // The response won't be received as the server has died - ignoring
098                        getLog().debug("Service is not reachable anymore (" + ex.getMessage() + ")");
099                }
100        }
101
102        private boolean isForked() {
103                if (this.fork != null) {
104                        return this.fork;
105                }
106                String property = this.project.getProperties()
107                                .getProperty("_spring.boot.fork.enabled");
108                return Boolean.valueOf(property);
109        }
110
111        private void stopForkedProcess()
112                        throws IOException, MojoFailureException, MojoExecutionException {
113                JMXConnector connector = SpringApplicationAdminClient.connect(this.jmxPort);
114                try {
115                        MBeanServerConnection connection = connector.getMBeanServerConnection();
116                        doStop(connection);
117                }
118                finally {
119                        connector.close();
120                }
121        }
122
123        private void stop() throws IOException, MojoFailureException, MojoExecutionException {
124                doStop(ManagementFactory.getPlatformMBeanServer());
125        }
126
127        private void doStop(MBeanServerConnection connection)
128                        throws IOException, MojoExecutionException {
129                try {
130                        new SpringApplicationAdminClient(connection, this.jmxName).stop();
131                }
132                catch (InstanceNotFoundException ex) {
133                        throw new MojoExecutionException(
134                                        "Spring application lifecycle JMX bean not found (fork is " + ""
135                                                        + this.fork + "). Could not stop application gracefully",
136                                        ex);
137                }
138        }
139
140}