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 com.sun.enterprise.universal.io.SmartFile;
044    import java.io.File;
045    import java.util.*;
046    import java.util.logging.Logger;
047    
048    import org.glassfish.api.admin.*;
049    import org.glassfish.api.admin.CommandModel.ParamModel;
050    
051    import com.sun.enterprise.admin.util.CommandModelData.ParamModelData;
052    import com.sun.enterprise.util.HostAndPort;
053    import com.sun.enterprise.universal.i18n.LocalStringsImpl;
054    import org.glassfish.common.util.admin.AsadminInput;
055    import org.glassfish.common.util.admin.AuthTokenManager;
056    
057    /**
058     * Representation of the options known to the asadmin program.
059     * These options control the overall behavior of asadmin, e.g.,
060     * the server to contact, and aren't specific to any of the
061     * commands supported by asadmin.
062     * <p>
063     * In GlassFish v3, asadmin program options are normally specified
064     * before the asadmin command name, with command options after the
065     * command name (although intermixed program and command options
066     * are still supported for compatibility).
067     */
068    public class ProgramOptions {
069    
070        public enum PasswordLocation {
071            DEFAULT, USER, PASSWORD_FILE, LOGIN_FILE, LOCAL_PASSWORD
072        };
073    
074        private static final Set<ParamModel> programOptions;
075    
076        // the known program option names
077        public static final String HOST             = "host";
078        public static final String PORT             = "port";
079        public static final String USER             = "user";
080        public static final String PASSWORDFILE     = "passwordfile";
081        public static final String TERSE            = "terse";
082        public static final String ECHO             = "echo";
083        public static final String INTERACTIVE      = "interactive";
084        public static final String SECURE           = "secure";
085        public static final String HELP             = "help";
086        public static final String AUTHTOKEN        = AuthTokenManager.AUTH_TOKEN_OPTION_NAME;
087        public static final String AUXINPUT         = AsadminInput.CLI_INPUT_OPTION_NAME;
088    
089        private static final Logger logger =
090            Logger.getLogger(ProgramOptions.class.getPackage().getName());
091    
092        private static final LocalStringsImpl strings =
093                new LocalStringsImpl(ProgramOptions.class);
094    
095        private ParameterMap                    options;
096        private Environment                     env;
097        private boolean                         optionsSet;
098        private String                          password;
099        private PasswordLocation                location;
100    
101        /*
102         * Information passed in from AsadminMain and used by start-domain.
103         * XXX - this is somewhat of a kludge but this seems the best place
104         * to put it for now
105         */
106        private String                          classPath;
107        private String                          className;
108    
109        /*
110         * Define the meta-options known by the asadmin command.
111         */
112        static {
113            Set<ParamModel> opts = new HashSet<ParamModel>();
114            addMetaOption(opts, HOST, 'H', String.class, false,
115                    CLIConstants.DEFAULT_HOSTNAME);
116            addMetaOption(opts, PORT, 'p', String.class, false,
117                    "" + CLIConstants.DEFAULT_ADMIN_PORT);
118            addMetaOption(opts, USER, 'u', String.class, false, null);
119            addMetaOption(opts, PASSWORDFILE, 'W', File.class, false, null);
120            addMetaOption(opts, SECURE, 's', Boolean.class, false, "false");
121            addMetaOption(opts, TERSE, 't', Boolean.class, false, "false");
122            addMetaOption(opts, ECHO, 'e', Boolean.class, false, "false");
123            addMetaOption(opts, INTERACTIVE, 'I', Boolean.class, false, "false");
124            addMetaOption(opts, HELP, '?', Boolean.class, false, "false");
125            addMetaOption(opts, AUXINPUT, '\0', String.class, false, null);
126            addMetaOption(opts, AUTHTOKEN, '\0', String.class, false, null);
127            programOptions = Collections.unmodifiableSet(opts);
128        }
129    
130        /**
131         * Helper method to define a meta-option.
132         *
133         * @param name  long option name
134         * @param sname short option name
135         * @param type  option type (String.class, Boolean.class, etc.)
136         * @param req   is option required?
137         * @param def   default value for option
138         */
139        private static void addMetaOption(Set<ParamModel> opts, String name,
140                char sname, Class type, boolean req, String def) {
141            ParamModel opt = new ParamModelData(name, type, !req, def, 
142                                                    Character.toString(sname));
143            opts.add(opt);
144        }
145    
146        /**
147         * Initialize program options based only on environment defaults,
148         * with no options from the command line.
149         */
150        public ProgramOptions(Environment env) throws CommandException {
151            this(new ParameterMap(), env);
152            optionsSet = false;
153        }
154    
155        /**
156         * Initialize the programoptions based on parameters parsed
157         * from the command line, with defaults supplied by the
158         * environment.
159         */
160        public ProgramOptions(ParameterMap options, Environment env)
161                throws CommandException {
162            this.env = env;
163            updateOptions(options);
164        }
165    
166        /**
167         * Copy constructor.  Create a new ProgramOptions with the same
168         * options as the specified ProgramOptions.
169         */
170        public ProgramOptions(ProgramOptions other) {
171            this.options = new ParameterMap(other.options);
172            this.env = other.env;
173            this.password = other.password;
174            this.classPath = other.classPath;
175            this.className = other.className;
176        }
177    
178        /**
179         * Update the program options based on the specified
180         * options from the command line.
181         */
182        public void updateOptions(ParameterMap newOptions)
183                throws CommandException {
184            if (options == null)
185                options = newOptions;
186            else {
187                // merge in the new options
188                for (Map.Entry<String, List<String>> e : newOptions.entrySet())
189                    options.set(e.getKey(), e.getValue());
190            }
191            optionsSet = true;
192    
193            // have to verify port value now
194            String sport = options.getOne(PORT);
195            if (ok(sport)) {
196                String badPortMsg = strings.get("InvalidPortNumber", sport);
197                try {
198                    int port = Integer.parseInt(sport);
199                    if (port < 1 || port > 65535)
200                        throw new CommandException(badPortMsg);
201                } catch (NumberFormatException e) {
202                    throw new CommandException(badPortMsg);
203                }
204            }
205        }
206    
207        private static boolean ok(String s) {
208            return s != null && s.length() > 0;
209        }
210    
211        /**
212         * Return a set of all the valid program options.
213         *
214         * @return the valid program options
215         */
216        public static Collection<ParamModel> getValidOptions() {
217            return programOptions;
218        }
219    
220        /**
221         * Copy the program options that were specified on the
222         * command line into the corresponding environment variables.
223         */
224        public void toEnvironment(Environment env) {
225            // copy all the parameters into corresponding environment variables
226            putEnv(env, ECHO);
227            putEnv(env, TERSE);
228            putEnv(env, INTERACTIVE);
229            putEnv(env, HOST);
230            putEnv(env, PORT);
231            putEnv(env, SECURE);
232            putEnv(env, USER);
233            putEnv(env, PASSWORDFILE);
234            putEnv(env, AUTHTOKEN);
235            putEnv(env, AUXINPUT);
236            // XXX - HELP?
237        }
238    
239        private void putEnv(Environment env, String name) {
240            String value = options.getOne(name);
241            if (value != null)
242                env.putOption(name, value);
243        }
244    
245        /**
246         * @return the host
247         */
248        public String getHost() {
249            String host = options.getOne(HOST);
250            if (!ok(host))
251                host = env.getStringOption(HOST);
252            if (!ok(host))
253                host = CLIConstants.DEFAULT_HOSTNAME;
254            return host;
255        }
256    
257        /**
258         * @param host the host to set
259         */
260        public void setHost(String host) {
261            options.set(HOST, host);
262        }
263    
264        /**
265         * @return the port
266         */
267        public int getPort() {
268            int port;
269            String sport = options.getOne(PORT);
270            if (!ok(sport))
271                sport = env.getStringOption(PORT);
272            if (ok(sport)) {
273                try {
274                    port = Integer.parseInt(sport);
275                    if (port < 1 || port > 65535)
276                        port = -1;  // should've been verified in constructor
277                } catch (NumberFormatException e) {
278                    port = -1;  // should've been verified in constructor
279                }
280            } else
281                port = CLIConstants.DEFAULT_ADMIN_PORT; // the default port
282            return port;
283        }
284    
285        /**
286         * @param port the port to set
287         */
288        public void setPort(int port) {
289            options.set(PORT, Integer.toString(port));
290        }
291    
292        /**
293         * Convenience method to set the host and port (and secure)
294         * attributes from a HostAndPort object.
295         *
296         * @param   address the HostAndPort object from which to set the attributes
297         */
298        public void setHostAndPort(HostAndPort address) {
299            setHost(address.getHost());
300            setPort(address.getPort());
301            setSecure(address.isSecure());
302        }
303    
304        /**
305         * @return the user
306         */
307        public String getUser() {
308            String user = options.getOne(USER);
309            if (!ok(user))
310                user = env.getStringOption(USER);
311            if (!ok(user))
312                user = null; // distinguish between specify the default explicitly
313            return user;
314        }
315    
316        /**
317         * @param user the user to set
318         */
319        public void setUser(String user) {
320            logger.finer("Setting user to: " + user);
321            options.set(USER, user);
322        }
323    
324        /**
325         * @return the password
326         */
327        public String getPassword() {
328            return password;
329        }
330    
331        /**
332         * @return the password location
333         */
334        public PasswordLocation getPasswordLocation() {
335            return location;
336        }
337    
338        /**
339         * @param password the password to set
340         */
341        public void setPassword(String password, PasswordLocation location) {
342            logger.finer("Setting password to: " +
343                                        (ok(password) ? "<non-null>" : "<null>"));
344            this.password = password;
345            this.location = location;
346        }
347    
348        /**
349         * @return the passwordFile
350         */
351        public String getPasswordFile() {
352            String passwordFile = options.getOne(PASSWORDFILE);
353    
354            if (!ok(passwordFile))
355                passwordFile = env.getStringOption(PASSWORDFILE);
356    
357            if (!ok(passwordFile))
358                return null;        // no default
359    
360            // weird, huh?  This means use standard input
361            if (!passwordFile.equals("-"))
362                passwordFile = SmartFile.sanitize(passwordFile);
363    
364            return passwordFile;
365        }
366    
367        /**
368         * @param passwordFile the passwordFile to set
369         */
370        public void setPasswordFile(String passwordFile) {
371            options.set(PASSWORDFILE, passwordFile);
372        }
373    
374        /**
375         * @return the secure
376         */
377        public boolean isSecure() {
378            boolean secure;
379            if (options.containsKey(SECURE)) {
380                String value = options.getOne(SECURE);
381                if (ok(value))
382                    secure = Boolean.parseBoolean(value);
383                else
384                    secure = true;
385            } else
386                secure = env.getBooleanOption(SECURE);
387            return secure;
388        }
389    
390        /**
391         * @param secure the secure to set
392         */
393        public void setSecure(boolean secure) {
394            options.set(SECURE, Boolean.toString(secure));
395        }
396    
397        public void setAuthToken(final String token) {
398            options.set(AUTHTOKEN, token);
399        }
400        
401        public String getAuthToken() {
402            return getString(AUTHTOKEN);
403        }
404        
405        public void setAuxInput(final String authInput) {
406            options.set(AUXINPUT, authInput);
407        }
408        
409        public String getAuxInput() {
410            return getString(AUXINPUT);
411        }
412        
413        private String getString(final String optionName) {
414            String result;
415            result = options.getOne(optionName);
416            if ( ! ok(result)) {
417                result = env.getStringOption(optionName);
418                if ( ! ok(result)) {
419                    result = null;
420                }
421            }
422            return result;
423        }
424    
425        /**
426         * @return the terse
427         */
428        public boolean isTerse() {
429            boolean terse;
430            if (options.containsKey(TERSE)) {
431                String value = options.getOne(TERSE);
432                if (ok(value))
433                    terse = Boolean.parseBoolean(value);
434                else
435                    terse = true;
436            } else
437                terse = env.getBooleanOption(TERSE);
438            return terse;
439        }
440    
441        /**
442         * @param terse the terse to set
443         */
444        public void setTerse(boolean terse) {
445            options.set(TERSE, Boolean.toString(terse));
446        }
447    
448        /**
449         * @return the echo
450         */
451        public boolean isEcho() {
452            boolean echo;
453            if (options.containsKey(ECHO)) {
454                String value = options.getOne(ECHO);
455                if (ok(value))
456                    echo = Boolean.parseBoolean(value);
457                else
458                    echo = true;
459            } else
460                echo = env.getBooleanOption(ECHO);
461            return echo;
462        }
463    
464        /**
465         * @param echo the echo to set
466         */
467        public void setEcho(boolean echo) {
468            options.set(ECHO, Boolean.toString(echo));
469        }
470    
471        /**
472         * @return the interactive
473         */
474        public boolean isInteractive() {
475            boolean interactive;
476            if (options.containsKey(INTERACTIVE)) {
477                String value = options.getOne(INTERACTIVE);
478                if (ok(value))
479                    interactive = Boolean.parseBoolean(value);
480                else
481                    interactive = true;
482            } else if (env.hasOption(INTERACTIVE)) {
483                interactive = env.getBooleanOption(INTERACTIVE);
484            } else
485                interactive = System.console() != null;
486            return interactive;
487        }
488    
489        /**
490         * @param interactive the interactive to set
491         */
492        public void setInteractive(boolean interactive) {
493            options.set(INTERACTIVE, Boolean.toString(interactive));
494        }
495    
496        /**
497         * @return the help
498         */
499        public boolean isHelp() {
500            boolean help = false;
501            if (options.containsKey(HELP)) {
502                String value = options.getOne(HELP);
503                if (ok(value))
504                    help = Boolean.parseBoolean(value);
505                else
506                    help = true;
507            } else
508                help = env.getBooleanOption(HELP);
509            return help;
510        }
511    
512        /**
513         * @param help the help to set
514         */
515        public void setHelp(boolean help) {
516            options.set(HELP, Boolean.toString(help));
517        }
518    
519        /**
520         * @return were options set on the command line?
521         */
522        public boolean isOptionsSet() {
523            return optionsSet;
524        }
525    
526        /**
527         * Set whether the program options have already been set.
528         */
529        public void setOptionsSet(boolean optionsSet) {
530            this.optionsSet = optionsSet;
531        }
532    
533        /**
534         * Return an array of asadmin command line options that specify
535         * all the options of this ProgramOptions instance.
536         */
537        public String[] getProgramArguments() {
538            List<String> args = new ArrayList<String>(15);
539            if (ok(getHost())) {
540                args.add("--host");
541                args.add(getHost());
542            }
543            if (getPort() > 0) {
544                args.add("--port");
545                args.add(String.valueOf(getPort()));
546            }
547            if (ok(getUser())) {
548                args.add("--user");
549                args.add(getUser());
550            }
551            if (ok(getPasswordFile())) {
552                args.add("--passwordfile");
553                args.add(getPasswordFile());
554            }
555            if (ok(getAuxInput())) {
556                args.add("--" + AUXINPUT);
557                args.add(getAuxInput());
558            }
559            args.add("--secure=" + String.valueOf(isSecure()));
560            args.add("--terse=" + String.valueOf(isTerse()));
561            args.add("--echo=" + String.valueOf(isEcho()));
562            args.add("--interactive=" + String.valueOf(isInteractive()));
563            String[] a = new String[args.size()];
564            args.toArray(a);
565            return a;
566        }
567    
568        /**
569         * @return the classPath
570         */
571        public String getClassPath() {
572            return classPath;
573        }
574    
575        /**
576         * @param classPath the classPath to set
577         */
578        public void setClassPath(String classPath) {
579            this.classPath = classPath;
580        }
581    
582        /**
583         * @return the className
584         */
585        public String getClassName() {
586            return className;
587        }
588    
589        /**
590         * @param className the className to set
591         */
592        public void setClassName(String className) {
593            this.className = className;
594        }
595    
596        /**
597         * String representation of the asadmin program options.
598         * Included in the --echo output.
599         */
600        public String toString() {
601            StringBuilder sb = new StringBuilder();
602            if (ok(getHost()))
603                sb.append("--host ").append(getHost()).append(' ');
604            if (getPort() > 0)
605                sb.append("--port ").append(getPort()).append(' ');
606            if (ok(getUser()))
607                sb.append("--user ").append(getUser()).append(' ');
608            if (ok(getPasswordFile()))
609                sb.append("--passwordfile ").
610                    append(getPasswordFile()).append(' ');
611            if (isSecure())
612                sb.append("--secure ");
613            sb.append("--interactive=").
614                append(Boolean.toString(isInteractive())).append(' ');
615            sb.append("--echo=").
616                append(Boolean.toString(isEcho())).append(' ');
617            sb.append("--terse=").
618                append(Boolean.toString(isTerse())).append(' ');
619            sb.setLength(sb.length() - 1);  // strip trailing space
620            return sb.toString();
621        }
622    }