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
041 package com.sun.enterprise.admin.cli;
042
043 import java.io.*;
044 import java.util.*;
045 import java.util.logging.*;
046
047 import org.jvnet.hk2.annotations.*;
048 import org.jvnet.hk2.component.*;
049 import org.glassfish.api.Param;
050 import org.glassfish.api.admin.*;
051 import static com.sun.enterprise.admin.cli.CLIConstants.*;
052 import com.sun.enterprise.admin.launcher.GFLauncher;
053 import com.sun.enterprise.admin.launcher.GFLauncherException;
054 import com.sun.enterprise.admin.launcher.GFLauncherFactory;
055 import com.sun.enterprise.admin.launcher.GFLauncherInfo;
056 import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
057 import com.sun.enterprise.universal.i18n.LocalStringsImpl;
058 import com.sun.enterprise.universal.process.ProcessStreamDrainer;
059 import com.sun.enterprise.universal.xml.MiniXmlParserException;
060 import org.glassfish.security.common.FileRealmHelper;
061
062 /**
063 * The start-domain command.
064 *
065 * @author bnevins
066 * @author Bill Shannon
067 */
068 @Service(name = "start-domain")
069 @Scoped(PerLookup.class)
070 public class StartDomainCommand extends LocalDomainCommand implements StartServerCommand {
071
072 private GFLauncherInfo info;
073 private GFLauncher launcher;
074 @Param(optional = true, shortName = "v", defaultValue = "false")
075 private boolean verbose;
076 @Param(optional = true, defaultValue = "false")
077 private boolean upgrade;
078 @Param(optional = true, shortName = "d", defaultValue = "false")
079 private boolean debug;
080 @Param(name = "domain_name", primary = true, optional = true)
081 private String domainName0;
082 @Param(name = "_dry-run", shortName = "n", optional = true,
083 defaultValue = "false")
084 private boolean dry_run;
085 private static final LocalStringsImpl strings =
086 new LocalStringsImpl(StartDomainCommand.class);
087 // the name of the master password option
088 private StartServerHelper helper;
089 private static final String newpwName = Environment.AS_ADMIN_ENV_PREFIX + "NEWPASSWORD";
090
091 @Override
092 public RuntimeType getType() {
093 return RuntimeType.DAS;
094 }
095
096 @Override
097 protected void validate()
098 throws CommandException, CommandValidationException {
099 setDomainName(domainName0);
100 super.validate();
101 }
102
103 @Override
104 protected int executeCommand() throws CommandException {
105 try {
106 // createLauncher needs to go before the helper is created!!
107 createLauncher();
108 final String mpv = getMasterPassword();
109
110 helper = new StartServerHelper(
111 logger,
112 programOpts.isTerse(),
113 getServerDirs(),
114 launcher,
115 mpv,
116 debug);
117
118 if (helper.prepareForLaunch() == false)
119 return ERROR;
120
121 if (!upgrade && launcher.needsManualUpgrade()) {
122 logger.info(strings.get("manualUpgradeNeeded"));
123 return ERROR;
124 }
125 doAutoUpgrade(mpv);
126
127 if (dry_run) {
128 logger.fine(Strings.get("dry_run_msg"));
129 List<String> cmd = launcher.getCommandLine();
130 StringBuilder sb = new StringBuilder();
131 for (String s : cmd) {
132 sb.append(s);
133 sb.append('\n');
134 }
135 logger.info(sb.toString());
136 return SUCCESS;
137 }
138
139 doAdminPasswordCheck();
140
141 // launch returns very quickly if verbose is not set
142 // if verbose is set then it returns after the domain dies
143 launcher.launch();
144
145 if (verbose || upgrade) { // we can potentially loop forever here...
146 while (true) {
147 int returnValue = launcher.getExitValue();
148
149 switch (returnValue) {
150 case RESTART_NORMAL:
151 logger.info(strings.get("restart"));
152 break;
153 case RESTART_DEBUG_ON:
154 logger.info(strings.get("restartChangeDebug", "on"));
155 info.setDebug(true);
156 break;
157 case RESTART_DEBUG_OFF:
158 logger.info(strings.get("restartChangeDebug", "off"));
159 info.setDebug(false);
160 break;
161 default:
162 return returnValue;
163 }
164
165 if (CLIConstants.debugMode)
166 System.setProperty(CLIConstants.WALL_CLOCK_START_PROP,
167 "" + System.currentTimeMillis());
168
169 launcher.relaunch();
170 }
171
172 }
173 else {
174 helper.waitForServer();
175 helper.report();
176 return SUCCESS;
177 }
178 }
179 catch (GFLauncherException gfle) {
180 throw new CommandException(gfle.getMessage());
181 }
182 catch (MiniXmlParserException me) {
183 throw new CommandException(me);
184 }
185 }
186
187 /**
188 * Create a launcher for the domain specified by arguments to
189 * this command. The launcher is for a server of the specified type.
190 * Sets the launcher and info fields.
191 * It has to be public because it is part of an interface
192 */
193 @Override
194 public void createLauncher()
195 throws GFLauncherException, MiniXmlParserException {
196 launcher = GFLauncherFactory.getInstance(getType());
197 info = launcher.getInfo();
198
199 info.setDomainName(getDomainName());
200 info.setDomainParentDir(getDomainsDir().getPath());
201 info.setVerbose(verbose || upgrade);
202 info.setDebug(debug);
203 info.setUpgrade(upgrade);
204
205 info.setRespawnInfo(programOpts.getClassName(),
206 programOpts.getClassPath(),
207 respawnArgs());
208
209 launcher.setup();
210 }
211
212 /**
213 * Return the asadmin command line arguments necessar to start
214 * this domain admin server.
215 */
216 private String[] respawnArgs() {
217 List<String> args = new ArrayList<String>(15);
218 args.addAll(Arrays.asList(programOpts.getProgramArguments()));
219
220 // now the start-domain specific arguments
221 args.add(getName()); // the command name
222 args.add("--verbose=" + String.valueOf(verbose));
223 args.add("--debug=" + String.valueOf(debug));
224 args.add("--domaindir");
225 args.add(getDomainsDir().toString());
226 if (ok(getDomainName()))
227 args.add(getDomainName()); // the operand
228
229 if (logger.isLoggable(Level.FINER))
230 logger.log(Level.FINER, "Respawn args: {0}", args.toString());
231 String[] a = new String[args.size()];
232 args.toArray(a);
233 return a;
234 }
235
236 /*
237 * This is useful for debugging restart-domain problems.
238 * In that case the Server process will run this class and it is fairly
239 * involved to attach a debugger (though not bad -- see RestartDomain on
240 * the server to see how). Standard output disappears. This is a
241 * generally useful method. Feel free to copy & paste!
242 */
243 private void debug(String s) {
244 try {
245 PrintStream ps = new PrintStream(
246 new FileOutputStream("startdomain.txt", true));
247 ps.println(new Date().toString() + ": " + s);
248 }
249 catch (FileNotFoundException ex) {
250 //
251 }
252 }
253
254 /*
255 * If this domain needs to be upgraded and --upgrade wasn't
256 * specified, first start the domain to do the upgrade and
257 * then start the domain again for real.
258 */
259 private void doAutoUpgrade(String mpv) throws GFLauncherException, MiniXmlParserException, CommandException {
260 if (upgrade || !launcher.needsAutoUpgrade())
261 return;
262
263 logger.info(strings.get("upgradeNeeded"));
264 info.setUpgrade(true);
265 launcher.setup();
266 launcher.launch();
267 Process p = launcher.getProcess();
268 int exitCode = -1;
269 try {
270 exitCode = p.waitFor();
271 }
272 catch (InterruptedException ex) {
273 // should never happen
274 }
275 if (exitCode != SUCCESS) {
276 ProcessStreamDrainer psd =
277 launcher.getProcessStreamDrainer();
278 String output = psd.getOutErrString();
279 if (ok(output))
280 throw new CommandException(
281 strings.get("upgradeFailedOutput",
282 info.getDomainName(), exitCode, output));
283 else
284 throw new CommandException(strings.get("upgradeFailed",
285 info.getDomainName(), exitCode));
286 }
287 logger.info(strings.get("upgradeSuccessful"));
288
289 // need a new launcher to start the domain for real
290 createLauncher();
291 // continue with normal start...
292 }
293
294 /*
295 * Check to make sure that at least one admin user is able to login.
296 * If none is found, then prompt for an admin password.
297 *
298 * NOTE: this depends on launcher.setup having already been called.
299 */
300 private void doAdminPasswordCheck() throws CommandException {
301 String arfile = launcher.getAdminRealmKeyFile();
302 if (arfile != null) {
303 try {
304 FileRealmHelper ar = new FileRealmHelper(arfile);
305 if (!ar.hasAuthenticatableUser()) {
306 // Prompt for the password for the first user and set it
307 Set<String> names = ar.getUserNames();
308 if (names == null || names.isEmpty()) {
309 throw new CommandException("no admin users");
310 }
311 String auser = names.iterator().next();
312 ParamModelData npwo = new ParamModelData(newpwName, String.class, false, null);
313 npwo.description = strings.get("new.adminpw", auser);
314 npwo.param._password = true;
315 logger.info(strings.get("new.adminpw.prompt"));
316 String npw = super.getPassword(npwo, null, true);
317 if (npw == null) {
318 throw new CommandException(strings.get("no.console"));
319 }
320 ar.updateUser(auser, auser, npw.toCharArray(), null);
321 ar.persist();
322 }
323 } catch (IOException ioe) {
324 throw new CommandException(ioe);
325 }
326 }
327 }
328 }