001 /*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 1997-2011 Oracle and/or its affiliates. All rights reserved.
005 *
006 * The contents of this file are subject to the terms of either the GNU
007 * General Public License Version 2 only ("GPL") or the Common Development
008 * and Distribution License("CDDL") (collectively, the "License"). You
009 * may not use this file except in compliance with the License. You can
010 * obtain a copy of the License at
011 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
012 * or packager/legal/LICENSE.txt. See the License for the specific
013 * language governing permissions and limitations under the License.
014 *
015 * When distributing the software, include this License Header Notice in each
016 * file and include the License file at packager/legal/LICENSE.txt.
017 *
018 * GPL Classpath Exception:
019 * Oracle designates this particular file as subject to the "Classpath"
020 * exception as provided by Oracle in the GPL Version 2 section of the License
021 * file that accompanied this code.
022 *
023 * Modifications:
024 * If applicable, add the following below the License Header, with the fields
025 * enclosed by brackets [] replaced by your own identifying information:
026 * "Portions Copyright [year] [name of copyright owner]"
027 *
028 * Contributor(s):
029 * If you wish your version of this file to be governed by only the CDDL or
030 * only the GPL Version 2, indicate your decision by adding "[Contributor]
031 * elects to include this software in this distribution under the [CDDL or GPL
032 * Version 2] license." If you don't indicate a single choice of license, a
033 * recipient has the option to distribute your version of this file under
034 * either the CDDL, the GPL Version 2 or to extend the choice of license to
035 * its licensees as provided above. However, if you add GPL Version 2 code
036 * and therefore, elected the GPL Version 2 license, then the option applies
037 * only if the new code is made subject to such option by the copyright
038 * holder.
039 */
040 package com.sun.enterprise.admin.cli;
041
042 import com.sun.enterprise.universal.process.ProcessManager;
043 import com.sun.enterprise.universal.process.ProcessManagerException;
044 import com.sun.enterprise.universal.process.ProcessUtils;
045 import com.sun.enterprise.util.OS;
046 import com.sun.enterprise.util.io.FileUtils;
047 import java.io.File;
048 import java.io.IOException;
049 import org.jvnet.hk2.annotations.*;
050 import org.jvnet.hk2.component.*;
051 import org.glassfish.api.Param;
052 import org.glassfish.api.admin.*;
053 import com.sun.enterprise.admin.cli.remote.RemoteCommand;
054 import com.sun.enterprise.admin.cli.remote.DASUtils;
055
056 /**
057 * The stop-domain command.
058 *
059 * @author Byron Nevins
060 * @author Bill Shannon
061 */
062 @Service(name = "stop-domain")
063 @Scoped(PerLookup.class)
064 public class StopDomainCommand extends LocalDomainCommand {
065
066 @Param(name = "domain_name", primary = true, optional = true)
067 private String userArgDomainName;
068 @Param(name = "force", optional = true, defaultValue = "true")
069 Boolean force;
070 @Param(optional = true, defaultValue = "false")
071 Boolean kill;
072 private boolean local;
073 private static final long WAIT_FOR_DAS_TIME_MS = 60000; // 1 minute
074
075 @Override
076 protected void validate()
077 throws CommandException {
078 setDomainName(userArgDomainName);
079 super.validate(); // which calls initDomain() !!
080 local = getServerDirs().getServerName() != null;
081 }
082
083 /**
084 * Override initDomain in LocalDomainCommand to only initialize
085 * the local domain information (name, directory) in the local
086 * case, when no --host has been specified.
087 */
088 @Override
089 protected void initDomain() throws CommandException {
090 // only initialize local domain information if it's a local operation
091 if (programOpts.getHost().equals(CLIConstants.DEFAULT_HOSTNAME))
092 super.initDomain();
093 else if (userArgDomainName != null) // remote case
094 throw new CommandException(
095 Strings.get("StopDomain.noDomainNameAllowed"));
096 }
097
098 @Override
099 protected int executeCommand()
100 throws CommandException {
101
102 if (local) {
103 // if the local password isn't available, the domain isn't running
104 // (localPassword is set by initDomain)
105 if (getServerDirs().getLocalPassword() == null)
106 return dasNotRunning(local);
107
108 programOpts.setHostAndPort(getAdminAddress());
109 logger.finer("Stopping local domain on port "
110 + programOpts.getPort());
111
112 /*
113 * If we're using the local password, we don't want to prompt
114 * for a new password. If the local password doesn't work it
115 * most likely means we're talking to the wrong server.
116 */
117 programOpts.setInteractive(false);
118
119 // in the local case, make sure we're talking to the correct DAS
120 if (!isThisDAS(getDomainRootDir()))
121 return dasNotRunning(local);
122
123 logger.finer("It's the correct DAS");
124 }
125 else { // remote
126 // Verify that the DAS is running and reachable
127 if (!DASUtils.pingDASQuietly(programOpts, env))
128 return dasNotRunning(local);
129
130 logger.finer("DAS is running");
131 programOpts.setInteractive(false);
132 }
133
134 /*
135 * At this point any options will have been prompted for, and
136 * the password will have been prompted for by pingDASQuietly,
137 * so even if the password is wrong we don't want any more
138 * prompting here.
139 */
140
141 doCommand();
142 return 0;
143 }
144
145 /**
146 * Print message and return exit code when
147 * we detect that the DAS is not running.
148 */
149 protected int dasNotRunning(boolean local) throws CommandException {
150 if (kill) {
151 if (local)
152 return kill();
153 else // remote. We can NOT kill and we can't ask it to kill itself.
154 throw new CommandException(Strings.get("StopDomain.dasNotRunningRemotely"));
155 }
156
157 // by definition this is not an error
158 // https://glassfish.dev.java.net/issues/show_bug.cgi?id=8387
159 if (local)
160 logger.warning(Strings.get("StopDomain.dasNotRunning", getDomainRootDir()));
161 else
162 logger.warning(Strings.get("StopDomain.dasNotRunningRemotely"));
163 return 0;
164 }
165
166 /**
167 * Execute the actual stop-domain command.
168 */
169 protected void doCommand() throws CommandException {
170 // run the remote stop-domain command and throw away the output
171 RemoteCommand cmd = new RemoteCommand(getName(), programOpts, env);
172 cmd.executeAndReturnOutput("stop-domain", "--force", force.toString());
173 waitForDeath();
174
175 if (kill && local) {
176 kill();
177 }
178 }
179
180 /**
181 * Wait for the server to die.
182 */
183 protected void waitForDeath() throws CommandException {
184 if (!programOpts.isTerse()) {
185 // use stdout because logger always appends a newline
186 System.out.print(Strings.get("StopDomain.WaitDASDeath") + " ");
187 }
188 long startWait = System.currentTimeMillis();
189 boolean alive = true;
190 int count = 0;
191
192 while (!timedOut(startWait)) {
193 if (!isRunning()) {
194 alive = false;
195 break;
196 }
197 try {
198 Thread.sleep(100);
199 if (!programOpts.isTerse() && count++ % 10 == 0)
200 System.out.print(".");
201 }
202 catch (InterruptedException ex) {
203 // don't care
204 }
205 }
206
207 if (!programOpts.isTerse())
208 System.out.println();
209
210 if (alive) {
211 throw new CommandException(Strings.get("StopDomain.DASNotDead",
212 (WAIT_FOR_DAS_TIME_MS / 1000)));
213 }
214 }
215
216 private boolean timedOut(long startTime) {
217 return (System.currentTimeMillis() - startTime) > WAIT_FOR_DAS_TIME_MS;
218 }
219
220 private int kill() throws CommandException {
221 File prevPid = null;
222 String pids = null;
223
224 try {
225 prevPid = new File(getServerDirs().getPidFile().getPath() + ".prev");
226
227 if (!prevPid.canRead())
228 throw new CommandException(Strings.get("StopDomain.nopidprev", prevPid));
229
230 pids = FileUtils.readSmallFile(prevPid).trim();
231 String s = ProcessUtils.kill(Integer.parseInt(pids));
232
233 if(s != null)
234 logger.finer(s);
235 }
236 catch (CommandException ce) {
237 throw ce;
238 }
239 catch (Exception ex) {
240 throw new CommandException(Strings.get("StopDomain.pidprevreaderror",
241 prevPid, ex.getMessage()));
242 }
243 return 0;
244 }
245 }