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    }