001 /*
002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003 *
004 * Copyright (c) 1997-2010 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 org.jvnet.hk2.annotations.*;
046 import org.jvnet.hk2.component.*;
047 import org.glassfish.api.Param;
048 import org.glassfish.api.admin.*;
049 import org.glassfish.api.admin.CommandModel.ParamModel;
050 import com.sun.enterprise.admin.util.*;
051 import com.sun.enterprise.universal.i18n.LocalStringsImpl;
052
053 /**
054 * A scaled-down implementation of multi-mode command.
055 *
056 * @author केदार(km@dev.java.net)
057 * @author Bill Shannon
058 */
059 @Service(name = "multimode")
060 @Scoped(PerLookup.class)
061 public class MultimodeCommand extends CLICommand {
062 @Inject
063 private Habitat habitat;
064
065 @Param(optional = true, shortName = "f")
066 private File file;
067
068 @Param(name = "printprompt", optional = true)
069 private Boolean printPromptOpt;
070 private boolean printPrompt;
071
072 @Param(optional = true)
073 private String encoding;
074
075 private boolean echo; // saved echo flag
076
077 private static final LocalStringsImpl strings =
078 new LocalStringsImpl(MultimodeCommand.class);
079
080 /**
081 * The validate method validates that the type and quantity of
082 * parameters and operands matches the requirements for this
083 * command. The validate method supplies missing options from
084 * the environment.
085 */
086 @Override
087 protected void validate()
088 throws CommandException, CommandValidationException {
089 if (printPromptOpt != null)
090 printPrompt = printPromptOpt.booleanValue();
091 else
092 printPrompt = programOpts.isInteractive();
093 /*
094 * Save value of --echo because CLICommand will reset it
095 * before calling our executeCommand method but we want it
096 * to also apply to all commands in multimode.
097 */
098 echo = programOpts.isEcho();
099 }
100
101 /**
102 * In the usage message modify the --printprompt option to have a
103 * default based on the --interactive option.
104 */
105 protected Collection<ParamModel> usageOptions() {
106 Collection<ParamModel> opts = commandModel.getParameters();
107 Set<ParamModel> uopts = new LinkedHashSet<ParamModel>();
108 ParamModel p = new CommandModelData.ParamModelData("printprompt",
109 boolean.class, true, Boolean.toString(programOpts.isInteractive()));
110 for (ParamModel pm : opts) {
111 if (pm.getName().equals("printprompt"))
112 uopts.add(p);
113 else
114 uopts.add(pm);
115 }
116 return uopts;
117 }
118
119 @Override
120 protected int executeCommand()
121 throws CommandException, CommandValidationException {
122 BufferedReader reader = null;
123 programOpts.setEcho(echo); // restore echo flag, saved in validate
124 try {
125 if (file == null) {
126 System.out.println(strings.get("multimodeIntro"));
127 if (encoding != null)
128 reader = new BufferedReader(
129 new InputStreamReader(System.in, encoding));
130 reader = new BufferedReader(
131 new InputStreamReader(System.in));
132 } else {
133 printPrompt = false;
134 if (!file.canRead()) {
135 throw new CommandException("File: " + file +
136 " can not be read");
137 }
138 if (encoding != null)
139 reader = new BufferedReader(new InputStreamReader(
140 new FileInputStream(file), encoding));
141 else
142 reader = new BufferedReader(new FileReader(file));
143 }
144 return executeCommands(reader);
145 } catch(IOException e) {
146 throw new CommandException(e);
147 } finally {
148 try {
149 if (file != null && reader != null)
150 reader.close();
151 } catch (IOException e) {
152 // ignore it
153 }
154 }
155 }
156
157 /**
158 * Read commands from the specified BufferedReader
159 * and execute them. If printPrompt is set, prompt first.
160 *
161 * @return the exit code of the last command executed
162 */
163 private int executeCommands(BufferedReader reader)
164 throws CommandException, CommandValidationException, IOException {
165 String line = null;
166 int rc = 0;
167
168 /*
169 * Any program options we start with are copied to the environment
170 * to serve as defaults for commands we run, and then we give each
171 * command an empty program options.
172 */
173 programOpts.toEnvironment(env);
174 for (;;) {
175 if (printPrompt) {
176 System.out.print("asadmin> ");
177 System.out.flush();
178 }
179 if ((line = reader.readLine()) == null) {
180 if (printPrompt)
181 System.out.println();
182 break;
183 }
184
185 if (line.trim().startsWith("#")) // ignore comment lines
186 continue;
187
188 String[] args = null;
189 try {
190 args = getArgs(line);
191 } catch (ArgumentTokenizer.ArgumentException ex) {
192 logger.info(ex.getMessage());
193 continue;
194 }
195
196 if (args.length == 0)
197 continue;
198
199 String command = args[0];
200 if (command.length() == 0)
201 continue;
202
203 // handle built-in exit and quit commands
204 // XXX - care about their arguments?
205 if (command.equals("exit") || command.equals("quit"))
206 break;
207
208 CLICommand cmd = null;
209 ProgramOptions po = null;
210 try {
211 /*
212 * Every command gets its own copy of program options
213 * so that any program options specified in its
214 * command line options don't effect other commands.
215 * But all commands share the same environment.
216 */
217 po = new ProgramOptions(env);
218 // copy over AsadminMain info
219 po.setClassPath(programOpts.getClassPath());
220 po.setClassName(programOpts.getClassName());
221 // remove the old one and replace it
222 habitat.remove(
223 habitat.getInhabitantByType(ProgramOptions.class));
224 habitat.addComponent("program-options", po);
225 cmd = CLICommand.getCommand(habitat, command);
226 rc = cmd.execute(args);
227 } catch (CommandValidationException cve) {
228 logger.severe(cve.getMessage());
229 logger.severe(cmd.getUsage());
230 rc = ERROR;
231 } catch (InvalidCommandException ice) {
232 // find closest match with local or remote commands
233 logger.severe(ice.getMessage());
234 try {
235 CLIUtil.displayClosestMatch(command,
236 CLIUtil.getAllCommands(habitat, po, env),
237 strings.get("ClosestMatchedLocalAndRemoteCommands"), logger);
238 } catch (InvalidCommandException e) {
239 // not a big deal if we cannot help
240 }
241 } catch (CommandException ce) {
242 if (ce.getCause() instanceof java.net.ConnectException) {
243 // find closest match with local commands
244 logger.severe(ce.getMessage());
245 try {
246 CLIUtil.displayClosestMatch(command,
247 CLIUtil.getLocalCommands(habitat),
248 strings.get("ClosestMatchedLocalCommands"), logger);
249 } catch (InvalidCommandException e) {
250 logger.info(
251 strings.get("InvalidRemoteCommand", command));
252 }
253 } else
254 logger.severe(ce.getMessage());
255 rc = ERROR;
256 } finally {
257 // restore the original program options
258 // XXX - is this necessary?
259 habitat.remove(
260 habitat.getInhabitantByType(ProgramOptions.class));
261 habitat.addComponent("program-options", programOpts);
262 }
263
264 // XXX - this duplicates code in AsadminMain, refactor it
265 switch (rc) {
266 case SUCCESS:
267 if (!programOpts.isTerse())
268 logger.fine(
269 strings.get("CommandSuccessful", command));
270 break;
271
272 case ERROR:
273 logger.fine(
274 strings.get("CommandUnSuccessful", command));
275 break;
276
277 case INVALID_COMMAND_ERROR:
278 logger.fine(
279 strings.get("CommandUnSuccessful", command));
280 break;
281
282 case CONNECTION_ERROR:
283 logger.fine(
284 strings.get("CommandUnSuccessful", command));
285 break;
286 }
287 CLIUtil.writeCommandToDebugLog(args, rc);
288 }
289 return rc;
290 }
291
292 private String[] getArgs(String line)
293 throws ArgumentTokenizer.ArgumentException {
294 List<String> args = new ArrayList<String>();
295 ArgumentTokenizer t = new ArgumentTokenizer(line);
296 while (t.hasMoreTokens())
297 args.add(t.nextToken());
298 return args.toArray(new String[args.size()]);
299 }
300 }