001/*
002 * Copyright (c) 2008-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2017-07-21 16:59:52 +0000 (Fri, 21 Jul 2017) $' 
007 * '$Revision: 34591 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler;
031
032import java.io.File;
033import java.io.InputStream;
034import java.lang.reflect.Constructor;
035import java.net.URL;
036import java.util.Iterator;
037import java.util.LinkedList;
038import java.util.List;
039import java.util.Map;
040
041import org.apache.commons.io.FileUtils;
042import org.apache.commons.logging.Log;
043import org.apache.commons.logging.LogFactory;
044import org.kepler.build.util.Version;
045import org.kepler.gui.kar.ImportModuleDependenciesAction;
046import org.kepler.kar.KAREntry;
047import org.kepler.kar.KARFile;
048import org.kepler.kar.handlers.ActorMetadataKAREntryHandler;
049import org.kepler.objectmanager.lsid.KeplerLSID;
050import org.kepler.util.ParseWorkflowUtil;
051
052import ptolemy.actor.Manager;
053import ptolemy.actor.gui.Configuration;
054import ptolemy.actor.gui.ConfigurationApplication;
055import ptolemy.actor.gui.Effigy;
056import ptolemy.actor.gui.ModelDirectory;
057import ptolemy.actor.gui.PtolemyEffigy;
058import ptolemy.actor.gui.PtolemyPreferences;
059import ptolemy.actor.gui.TableauFrame;
060import ptolemy.data.expr.StringParameter;
061import ptolemy.kernel.attributes.VersionAttribute;
062import ptolemy.kernel.util.Attribute;
063import ptolemy.kernel.util.IllegalActionException;
064import ptolemy.kernel.util.InternalErrorException;
065import ptolemy.kernel.util.KernelException;
066import ptolemy.kernel.util.NamedObj;
067import ptolemy.kernel.util.StringAttribute;
068import ptolemy.kernel.util.Workspace;
069import ptolemy.moml.MoMLParser;
070import ptolemy.moml.filter.BackwardCompatibility;
071import ptolemy.util.MessageHandler;
072import ptolemy.util.StringUtilities;
073
074/**
075 * This class is the startup class for all non-gui applications of kepler. It
076 * handles the command line interface and executes the proper base class in
077 * ptolemy.
078 * 
079 * This application supports executing kepler when the -nogui AND -cache command
080 * line flags are set. See org.kepler.Kepler.parseArgsAndRun() to see how this
081 * application is launched.
082 *
083 * The reason this application needs to exist is so Kepler can startup a
084 * headless runtime with the configuration system in place. Without the
085 * configuration, the cache cannot be started. This is working around a
086 * limitation in Ptolemy where the GUI generally needs to be instantiated in
087 * order to get an instance of Configuration. This class could possibly go away
088 * after the new configuration system comes on line if we can pull the
089 * configuration options needed for the cache.
090 * 
091 * You can execute this class with the following command: ./kepler.sh -runwf
092 * -nogui -cache workflow.xml
093 *
094 * To generate the kepler.sh script, run 'ant startup-script' from the
095 * build-area.
096 * 
097 * @author Chad Berkley, Christopher Brooks, Dan Crawl
098 * @version $Id: KeplerConfigurationApplication.java 33533 2015-07-13 20:22:49Z
099 *          crawl $
100 */
101public class KeplerConfigurationApplication extends ConfigurationApplication {
102    private static final Log log = LogFactory.getLog(KeplerConfigurationApplication.class.getName());
103    private static final boolean isDebugging = log.isDebugEnabled();
104
105    /**
106     * constructor. @See ptolemy.actor.gui.ConfigurationApplication for more
107     * information.
108     * 
109     * @param args
110     */
111    public KeplerConfigurationApplication(String[] args) throws Exception {
112        if (isDebugging)
113            log.debug("construct: " + args);
114        _initializeApplication();
115        _basePath = "ptolemy/configs";
116
117        // Create a parser to use.
118        _parser = new MoMLParser();
119
120        // We set the list of MoMLFilters to handle Backward Compatibility.
121        MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters());
122
123        MessageHandler.setMessageHandler(new /* Graphical */MessageHandler());
124
125        try {
126            java.util.Locale.setDefault(java.util.Locale.US);
127        } catch (java.security.AccessControlException accessControl) {
128            // FIXME: If the application is run under Web Start, then this
129            // exception will be thrown.
130        }
131
132        try {
133            _configuration = readConfiguration(specToURL(args[0]));
134
135            // replace kepler display actors into Discard if they are set in
136            // configuration xml.
137            _setGUIConfiguration(_configuration, "_keplerDisplayClassesWithRedirect");
138            _setGUIConfiguration(_configuration, "_keplerDisplayClassesNoRedirect");
139
140            /*
141             * List momlFilters = MoMLParser.getMoMLFilters(); if (momlFilters
142             * != null) { Iterator filters = momlFilters.iterator(); while
143             * (filters.hasNext()) { MoMLFilter filter = (MoMLFilter)
144             * filters.next(); if (filter instanceof RemoveGraphicalClasses) {
145             * RemoveGraphicalClasses rgc = (RemoveGraphicalClasses) filter; //
146             * rgc.put("org.geon.BrowserDisplay", //
147             * "ptolemy.actor.lib.Discard"); break; } } }
148             */
149
150            // initialize the KAR entries
151            String fileName = args[args.length - 1];
152            if (isDebugging)
153                log.debug(fileName);
154
155            // parse the command-line arguments
156            parseArgs(args);
157
158            // add special parameters to configuration
159
160            if (_server) {
161                // if the server flag is set, kepler is running on a server so
162                // the _server configuration attribute will be set so that other
163                // processes can behave accordingly
164                StringAttribute sa = new StringAttribute(_configuration, "_server");
165                sa.setExpression("true");
166
167            }
168
169            if (_repository != null) {
170                StringAttribute sa = new StringAttribute(_configuration, "_repository");
171                sa.setExpression(_repository);
172            }
173            if (_domain != null) {
174                StringAttribute sa = new StringAttribute(_configuration, "_domain");
175                sa.setExpression(_domain);
176            }
177            if (_username != null) {
178                StringAttribute sa = new StringAttribute(_configuration, "_username");
179                sa.setExpression(_username);
180            }
181            if (_password != null) {
182                StringAttribute sa = new StringAttribute(_configuration, "_password");
183                sa.setExpression(_password);
184            }
185
186            // extract the workflow moml from the kar
187
188            if (fileName.trim().endsWith(".kar")) {
189                if (isDebugging) {
190                    log.debug("Extracting workflow from KAR");
191                }
192                String karFileName = fileName;
193                File file = new File(karFileName);
194                KARFile karf = null;
195                try {
196                    karf = new KARFile(file);
197
198                    // see if KAR is openable
199                    if (!karf.isOpenable()) {
200
201                        // check dependencies.
202                        Map<String, Version> missingDeps = karf.getMissingDependencies();
203                        if (!missingDeps.isEmpty()) {
204
205                            // print out the missing dependencies
206                            System.out.println("WARNING: Missing module dependencies:");
207                            for (Map.Entry<String, Version> entry : missingDeps.entrySet()) {
208                                System.out.println("   " + entry.getKey());
209                            }
210
211                            if (!Kepler.getForceOpen()) {
212                                if (Kepler.getRunWithGUI()) {
213                                    ImportModuleDependenciesAction action = new ImportModuleDependenciesAction(
214                                            new TableauFrame());
215                                    action.setArchiveFile(file);
216                                    ImportModuleDependenciesAction.ImportChoice choice = action.checkDependencies();
217                                    if (choice == ImportModuleDependenciesAction.ImportChoice.DO_NOTHING) {
218                                        _exit();
219                                        return;
220                                    } else
221                                        if (choice == ImportModuleDependenciesAction.ImportChoice.DOWNLOADING_AND_RESTARTING) {
222                                        action.waitForDownloadAndRestart();
223                                    }
224                                } else {
225                                    System.out.println("ERROR: Cannot execute due to missing dependencies. "
226                                            + "Either add missing modules or run with -force.");
227                                    _exit();
228                                    return;
229                                }
230                            }
231                        } else {
232                            MessageHandler.error("ERROR: this KAR cannot be opened.");
233                            _exit();
234                            return;
235                        }
236                    }
237
238                    // For each Actor in the KAR open the MOML
239                    for (KAREntry entry : karf.karEntries()) {
240                        KeplerLSID lsid = entry.getLSID();
241                        if (isDebugging) {
242                            log.debug("Processing entry, LSID=" + lsid + ", type=" + entry.getType());
243                        }
244                        if (!ActorMetadataKAREntryHandler.handlesClass(entry.getType())) {
245                            if (isDebugging) {
246                                log.debug("Opening entry, LSID=" + lsid + ", type=" + entry.getType());
247                            }
248                            // WARNING - using null TableauFrame here
249                            karf.open(entry, null);
250                            continue;
251                        }
252
253                        // extract MOML to temp file
254                        File tmpFile = File.createTempFile("moml", ".xml");
255                        tmpFile.deleteOnExit();
256                        try(InputStream inputStream = karf.getInputStream(entry)) {
257                            FileUtils.copyInputStreamToFile(inputStream, tmpFile);
258                        }
259
260                        // _openModel here so it's found later. This avoids
261                        // dropping
262                        // listeners added before the next _openModel call.
263                        // see
264                        // http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5434
265                        URL url = specToURL(tmpFile.toString());
266                        if (_configuration != null) {
267                            ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory");
268                            if (directory == null) {
269                                throw new InternalErrorException("No model directory!");
270                            }
271                            _openModel(url, url, url.toString());
272                        }
273
274                        // set for MOML application to run the workflow
275                        args[args.length - 1] = tmpFile.getAbsolutePath();
276                        System.out.println("Running workflow " + tmpFile.getAbsolutePath() + " extracted from kar file "
277                                + karFileName);
278                    }
279                } finally {
280                    if (karf != null) {
281                        karf.close();
282                    }
283                }
284            } else {
285                // the file is not a KAR, so try to read it directly.
286                _readFile(fileName);
287            }
288
289            // set any parameters in the workflow that were specified on the
290            // command-line.
291            _setParameters();
292
293            if (_configuration == null) {
294                throw new IllegalActionException("No configuration provided.");
295            }
296
297            // show the configuration after we've loaded the workflow
298            _configuration.showAll();
299
300            // set additional parameters in the configuration
301
302            if (_run) {
303                /*
304                 * FIXME these values not used StringAttribute sa =
305                 * (StringAttribute) _configuration .getAttribute("_server"); if
306                 * (sa != null) { String value = sa.getExpression();
307                 * System.out.println("Running in server mode: " + true); }
308                 * 
309                 * sa = (StringAttribute) _configuration
310                 * .getAttribute("_repository"); if (sa != null) { String value
311                 * = sa.getExpression(); System.out.println(
312                 * "Default save repository is set to: " + _repository); }
313                 */
314
315                if (_printPDF) {
316                    // Need to set background
317                    PtolemyPreferences preferences = PtolemyPreferences
318                            .getPtolemyPreferencesWithinConfiguration(_configuration);
319                    preferences.backgroundColor.setExpression("{1.0, 1.0, 1.0, 1.0}");
320                }
321
322                // run the workflow
323                runModels();
324
325                if (_exit) {
326                    _exit();
327                }
328            } else {
329                if (_printPDF) {
330                    _printPDF();
331                }
332            }
333
334        } catch (Throwable ex) {
335            // Make sure that we do not eat the exception if there are
336            // problems parsing. For example, "ptolemy -FOO bar bif.xml"
337            // will crash if bar is not a variable. Instead, use
338            // "ptolemy -FOO \"bar\" bif.xml"
339            throwArgsException(ex, args);
340        }
341    }
342
343    /** Start a new thread that calls Kepler.shutdown() and exits the JVM. */
344    protected void _exit() {
345        // In vergil, this gets called in the
346        // swing thread, which hangs everything
347        // if we call waitForFinish() directly.
348        // So instead, we create a new thread to
349        // do it.
350        Thread waitThread = new Thread() {
351            @Override
352            public void run() {
353                waitForFinish();
354                if (_printPDF) {
355                    try {
356                        _printPDF();
357                    } catch (Exception ex) {
358                        ex.printStackTrace();
359                    }
360                }
361                Kepler.shutdown();
362                StringUtilities.exit(0);
363            }
364        };
365
366        // Note that we start the thread here, which could
367        // be risky when we subclass, since the thread will be
368        // started before the subclass constructor finishes
369        // (FindBugs)
370        waitThread.start();
371    }
372
373    /**
374     * Parse the command-line arguments.
375     * 
376     * @param args
377     *            The command-line arguments to be parsed.
378     * @exception Exception
379     *                If an argument is not understood or triggers an error.
380     */
381    protected void parseArgs(String[] args) throws Exception {
382
383        _parameterNames.clear();
384        _parameterValues.clear();
385
386        // NOTE: do not try to parse the last argument since that is
387        // the kar file.
388        // Do not parse the first argument since it is the configuration file.
389        for (int i = 1; i < args.length - 1; i++) {
390            String arg = args[i];
391
392            if (parseArg(arg) == false) {
393                if (arg.trim().startsWith("-")) {
394                    if (i >= (args.length - 1)) {
395                        throw new IllegalActionException(
396                                "Cannot set " + "parameter " + arg + " when no value is " + "given.");
397                    }
398
399                    // Save in case this is a parameter name and value.
400                    _parameterNames.add(arg.substring(1));
401                    _parameterValues.add(args[i + 1]);
402                    i++;
403                } else {
404                    // Unrecognized option.
405                    throw new IllegalActionException("Unrecognized option: " + arg);
406                }
407            }
408        }
409
410        if (_expectingClass) {
411            throw new IllegalActionException("Missing classname.");
412        }
413    }
414
415    /**
416     * Parse a command-line argument.
417     * 
418     * @param arg
419     *            The command-line argument to be parsed.
420     * @return True if the argument is understood, false otherwise.
421     * @exception Exception
422     *                If something goes wrong.
423     */
424    protected boolean parseArg(String arg) throws Exception {
425        if (arg.equals("-class")) {
426            _expectingClass = true;
427        } else if (arg.equals("-exit")) {
428            _exit = true;
429        } else if (arg.equals("-help")) {
430            System.out.println(_usage());
431
432            // NOTE: This means the test suites cannot test -help
433            StringUtilities.exit(0);
434        } else if (arg.equals("-printPDF")) {
435            _printPDF = true;
436        } else if (arg.equals("-run")) {
437            _run = true;
438        } else if (arg.equals("-runThenExit")) {
439            _run = true;
440            _exit = true;
441        } else if (arg.equals("-statistics")) {
442            _statistics = true;
443        } else if (arg.equals("-server")) {
444            _server = true;
445        } else if (arg.startsWith("-repository=")) {
446            int equalsIndex = arg.indexOf("=");
447            if (equalsIndex != -1) {
448                _repository = arg.substring(equalsIndex + 1, arg.length());
449            } else {
450                System.out.println("The -repository argument must be followed by '=<repository'");
451                StringUtilities.exit(0);
452            }
453        } else if (arg.startsWith("-domain=")) {
454            int equalsIndex = arg.indexOf("=");
455            if (equalsIndex != -1) {
456                _domain = arg.substring(equalsIndex + 1, arg.length());
457            } else {
458                System.out.println("The -domain argument must be followed by '=<domain>'");
459                StringUtilities.exit(0);
460            }
461        } else if (arg.startsWith("-username=")) {
462            int equalsIndex = arg.indexOf("=");
463            if (equalsIndex != -1) {
464                _username = arg.substring(equalsIndex + 1, arg.length());
465            } else {
466                System.out.println("The -username argument must be followed by '=<username>'");
467                StringUtilities.exit(0);
468            }
469        } else if (arg.startsWith("-password=")) {
470            int equalsIndex = arg.indexOf("=");
471            if (equalsIndex != -1) {
472                _password = arg.substring(equalsIndex + 1, arg.length());
473            } else {
474                System.out.println("The -password argument must be followed by '=<password>'");
475                StringUtilities.exit(0);
476            }
477        } else if (arg.equals("-test")) {
478            _test = true;
479        } else if (arg.equals("-version")) {
480            System.out.println("Version " + VersionAttribute.CURRENT_VERSION.getExpression()
481                    + ", Build $Id: KeplerConfigurationApplication.java 34591 2017-07-21 16:59:52Z crawl $");
482
483            // NOTE: This means the test suites cannot test -version
484            StringUtilities.exit(0);
485        } else if (arg.equals("")) {
486            // Ignore blank argument.
487        } else {
488            if (_expectingClass) {
489                // $PTII/bin/ptolemy -class
490                // ptolemy.domains.sdf.demo.Butterfly.Butterfly
491                // Previous argument was -class
492                _expectingClass = false;
493
494                // Create the class.
495                Class<?> newClass = Class.forName(arg);
496
497                // Instantiate the specified class in a new workspace.
498                Workspace workspace = new Workspace();
499
500                // Workspace workspace = _configuration.workspace();
501                // Get the constructor that takes a Workspace argument.
502                Class<?>[] argTypes = new Class[1];
503                argTypes[0] = workspace.getClass();
504
505                Constructor<?> constructor = newClass.getConstructor(argTypes);
506
507                Object[] args = new Object[1];
508                args[0] = workspace;
509
510                NamedObj newModel = (NamedObj) constructor.newInstance(args);
511
512                if (_configuration != null) {
513                    _openModel(newModel);
514
515                } else {
516                    System.err.println("No configuration found.");
517                    throw new IllegalActionException(newModel, "No configuration found.");
518                }
519            } else {
520                if (!arg.startsWith("-")) {
521                    // Assume the argument is a file name or URL.
522                    // Attempt to read it.
523                    _readFile(arg);
524                } else {
525                    // Argument not recognized.
526                    return false;
527                }
528            }
529        }
530
531        return true;
532    }
533
534    /** Attempt to read a file name. */
535    private void _readFile(String fileName) throws Exception {
536
537        URL inURL;
538
539        try {
540            inURL = specToURL(fileName);
541        } catch (Exception ex) {
542            try {
543                // Create a File and get the URL so that commands
544                // like
545                // $PTII/bin/vergil $PTII/doc/index.htm#in_browser
546                // work.
547                File inFile = new File(fileName);
548                inURL = inFile.toURI().toURL();
549            } catch (Exception ex2) {
550                // FIXME: This is a fall back for relative
551                // filenames,
552                // I'm not sure if it will ever be called.
553                inURL = new URL(new URL("file://./"), fileName);
554            }
555        }
556
557        // Strangely, the XmlParser does not want as base the
558        // directory containing the file, but rather the
559        // file itself.
560        URL base = inURL;
561
562        // If a configuration has been found, then
563        // defer to it to read the model. Otherwise,
564        // assume the file is an XML file.
565        if (_configuration != null) {
566            ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory");
567
568            if (directory == null) {
569                throw new InternalErrorException("No model directory!");
570            }
571
572            String key = inURL.toExternalForm();
573
574            _openModel(base, inURL, key);
575
576        } else {
577
578            _parser.reset();
579
580        }
581    }
582
583    /**
584     * Set any parameters specified by the command-line arguments in the model.
585     */
586    private void _setParameters() throws IllegalActionException {
587
588        // Check saved options to see whether any is setting an attribute.
589        Iterator<String> names = _parameterNames.iterator();
590        Iterator<String> values = _parameterValues.iterator();
591
592        Map<String, String> parameterFileMap = null;
593
594        while (names.hasNext() && values.hasNext()) {
595            String name = names.next();
596            String value = values.next();
597
598            boolean match = false;
599            ModelDirectory directory = (ModelDirectory) _configuration.getEntity("directory");
600
601            if (directory == null) {
602                throw new InternalErrorException("No model directory!");
603            }
604
605            Iterator<Effigy> effigies = directory.entityList(Effigy.class).iterator();
606
607            while (effigies.hasNext()) {
608                Effigy effigy = effigies.next();
609
610                if (effigy instanceof PtolemyEffigy) {
611                    NamedObj model = ((PtolemyEffigy) effigy).getModel();
612
613                    match = ParseWorkflowUtil.setModelParameter(model, name, value);
614
615                }
616            }
617
618            if (!match) {
619                // there was no match for this argument.
620                // see if it is the parameter file argument
621                if (name.equals(PARAMETER_FILE_ARG_NAME)) {
622                    // parse the parameter file
623                    parameterFileMap = ParseWorkflowUtil.parseParameterFile(value);
624                } else {
625                    // Unrecognized option.
626                    throw new IllegalActionException("Unrecognized option: " + "No parameter exists with name " + name);
627                }
628            }
629        }
630
631        // see if the parameter file argument was specified and the
632        // file was successfully parsed.
633        if (parameterFileMap != null) {
634            // clear the parameter names and values that we've already
635            // set and then set the ones in the parameter file.
636            _parameterNames.clear();
637            _parameterValues.clear();
638            for (Map.Entry<String, String> entry : parameterFileMap.entrySet()) {
639                _parameterNames.add(entry.getKey());
640                _parameterValues.add(entry.getValue());
641            }
642            if (!_parameterNames.isEmpty()) {
643                _setParameters();
644            }
645        }
646    }
647
648    /** Print a stack trace of the error. */
649    @Override
650    public synchronized void executionError(Manager manager, Throwable throwable) {
651        MessageHandler.error("Command failed.", throwable);
652        super.executionError(manager, throwable);
653    }
654
655    /**
656     * main method
657     */
658    public static void main(String[] args) {
659        try {
660            new KeplerConfigurationApplication(args);
661        } catch (Throwable throwable) {
662            MessageHandler.error("Command failed", throwable);
663            // Be sure to print the stack trace so that
664            // "$PTII/bin/moml -foo" prints something.
665            System.err.print(KernelException.stackTraceToString(throwable));
666            System.exit(1);
667        }
668    }
669
670    // ////////////////////////////////////////////////////////////////////
671    // // private methods ////
672
673    /**
674     * read xml and set GUI filter based on classes set in xml file
675     * 
676     * @throws IllegalActionException
677     */
678    private static void _setGUIConfiguration(Configuration configuration, String attName)
679            throws IllegalActionException {
680        Attribute displayAtt = configuration.getAttribute(attName);
681        if (displayAtt != null) {
682            String displayValue = ((StringParameter) displayAtt).stringValue();
683            String[] displayArray = displayValue.split(",");
684            ptolemy.moml.filter.ClassChanges changes = new ptolemy.moml.filter.ClassChanges();
685            for (String displayClass : displayArray) {
686                changes.put(displayClass, "ptolemy.actor.lib.Discard");
687            }
688        }
689    }
690
691    // /////////////////////////////////////////////////////////////////
692    // // private variables ////
693
694    // Flag indicating that the previous argument was -class.
695    private boolean _expectingClass = false;
696
697    // List of parameter names seen on the command line.
698    private List<String> _parameterNames = new LinkedList<String>();
699
700    // List of parameter values seen on the command line.
701    private List<String> _parameterValues = new LinkedList<String>();
702
703    // URL from which the configuration was read.
704    // private static URL _initialSpecificationURL;
705
706    private boolean _server = false;
707
708    private String _repository;
709
710    private String _domain;
711
712    private String _username;
713
714    private String _password;
715
716    /** Name of argument for parameter file. */
717    private static final String PARAMETER_FILE_ARG_NAME = "paramFile";
718
719}