001/*
002 * Copyright (c) 2008-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-04-21 05:45:00 +0000 (Thu, 21 Apr 2016) $' 
007 * '$Revision: 34479 $'
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.FileReader;
034import java.io.IOException;
035import java.lang.reflect.InvocationTargetException;
036import java.lang.reflect.Method;
037import java.net.URISyntaxException;
038import java.net.URL;
039import java.util.ArrayList;
040import java.util.Arrays;
041import java.util.Collection;
042import java.util.Enumeration;
043import java.util.Hashtable;
044import java.util.Iterator;
045import java.util.LinkedList;
046import java.util.List;
047import java.util.ResourceBundle;
048import java.util.StringTokenizer;
049
050import javax.swing.UIManager;
051
052import org.apache.commons.lang.exception.ExceptionUtils;
053import org.apache.commons.logging.Log;
054import org.apache.commons.logging.LogFactory;
055import org.apache.tools.ant.Project;
056import org.kepler.build.MakeKars;
057import org.kepler.build.modules.Module;
058import org.kepler.build.modules.ModuleTree;
059import org.kepler.build.project.ProjectLocator;
060import org.kepler.build.project.RepositoryLocations;
061import org.kepler.configuration.ConfigurationManager;
062import org.kepler.configuration.ConfigurationProperty;
063import org.kepler.gui.SplashWindow;
064import org.kepler.loader.PermissionManager;
065import org.kepler.loader.SystemPropertyLoader;
066import org.kepler.loader.util.Screenshot;
067import org.kepler.module.ModuleHSQLManager;
068import org.kepler.module.ModuleInitializer;
069import org.kepler.modulemanager.gui.patch.PatchChecker;
070import org.kepler.moml.NamedObjId;
071import org.kepler.sms.util.OntologyConfiguration;
072import org.kepler.util.DotKeplerManager;
073import org.kepler.util.FileUtil;
074import org.kepler.util.RunnableExecutionQueue;
075import org.kepler.util.ShutdownListener;
076import org.kepler.util.ShutdownNotifier;
077import org.kepler.util.StatusListener;
078import org.kepler.util.StatusNotifier;
079
080import ptolemy.actor.injection.ActorModuleInitializer.Initializer;
081import ptolemy.actor.injection.PtolemyInjector;
082import ptolemy.actor.injection.PtolemyModule;
083import ptolemy.kernel.util.IllegalActionException;
084import ptolemy.moml.filter.BackwardCompatibility;
085import ptolemy.util.FileUtilities;
086import ptolemy.vergil.VergilApplication;
087
088/**
089 * A class to initialize the build system, then start the Kepler GUI or execute
090 * workflows from the command line. In the latter case, command line arguments
091 * specify different run time configurations.
092 * 
093 * @author Chad Berkely, Daniel Crawl, David Welker
094 * @version $Id: Kepler.java 34479 2016-04-21 05:45:00Z crawl $
095 */
096
097public class Kepler {
098
099        public static void restart() {
100                main(_args);
101        }
102        
103        public static void main(String[] args) {
104            
105                // print status updates to stdout
106                StatusNotifier.addStatusListener(new StatusListener() {
107                        @Override
108                        public void log(String message) {
109                                System.out.println(message);
110                        }
111                });
112                
113            //System.out.println("java.library.path = " + System.getProperty("java.library.path"));
114
115        // log4j uses the first log4j.properties file found on the classpath. 
116        // since kepler's classpath is complicated, it is not always obvious
117        // which log4j.properties is used; it may be hidden in a jar file.
118        // this prints the log4j.properties file found on the classpath.
119        File log4jFile = FileUtilities.nameToFile("$CLASSPATH/log4j.properties", null);
120        if(log4jFile == null) {
121            System.out.println("log4j.properties not found in CLASSPATH.");
122        } else {
123            System.out.println("log4j.properties found in CLASSPATH: " + log4jFile);
124        }
125
126            
127        // run command line arguments module initializers to set command
128        // line arguments specific to modules.
129        try {
130            _initializeCommandLineArguments();
131        } catch (Exception e) {
132            System.out.println("Error initializing module command line arguments: " + e.getMessage());
133            return;
134        }
135
136                // Save the args in case they are needed later.
137                // long startTime = System.currentTimeMillis();
138                _args = args;
139                CommandLineArgs.store(args);
140                List<String> argList = Arrays.asList(args);
141                
142                // parse the command line arguments
143                if(!parseArgs(args)) {
144                    // an error occurred, so exit.
145                    return;
146                }
147
148                // set the repository location from the module manager configuration file.
149                RepositoryLocations
150                                .setReleaseLocation(org.kepler.modulemanager.RepositoryLocations
151                                                .getReleaseLocation());
152                        
153                ModuleTree.init();
154                
155                // use headless mode if kepler is running without a gui.
156                // this is necessary since the DISPLAY environment variable
157                // may be set to an incorrect value, and by default java does
158                // not use headless mode if DISPLAY is set.
159                if(!_runWithGui) {
160                    System.setProperty("java.awt.headless", "true");
161                } else if(UIManager.getLookAndFeel().getName().startsWith("Mac OS")) {
162                    System.setProperty("apple.laf.useScreenMenuBar", "true");
163                    System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Kepler");
164                }
165
166
167                /* Do not check for patches if running workflows from the command
168                 * line since this can occur many times and checking slows down
169                 * startup time. 
170        
171                // if we are executing a workflow from the command line, check now for
172                // patches.
173                if(_action == Action.RunKAR || _action == Action.RunWf) {
174                    // if we are running headless, do not display a dialog if patches
175                    // are available
176                    if (!_runWithGui || _displayRedirectOutputPath != null) {
177                        PatchChecker.check(true);
178                    } else {
179                        PatchChecker.check(false);
180                    }
181                // show the splash if the Kepler UI is starting
182                } else*/
183                if(_action == Action.Kepler && _showSplash) {
184                    _showSplash();
185                }
186                
187                ShutdownNotifier.addShutdownListener(new Shutdown());
188                try {
189
190                    setJavaPropertiesAndCopyModuleDirectories();
191                    
192                        // System.out.println("os: " + System.getProperty("os.name"));
193                        String OSName = System.getProperty("os.name");
194
195                        // Hashtable properties = getProject().getProperties();
196                        ModuleTree moduleTree = ModuleTree.instance();
197                        
198                        String classpath = System.getProperty("java.class.path");
199                        String[] classpathItems = classpath.split(File.pathSeparator);
200                        
201                        for (Module module : moduleTree) {
202                                
203                                // XXX since Module dir variables can be wrong, utilizing classpath
204                                // to determine module location on disk. Use below line instead when 
205                                // that's fixed.
206                                //File osextension = new File(module.getModuleInfoDir() + File.separator + "osextension.txt");
207                                File osextension = null;
208                                String sought = File.separator + module.getName() + File.separator;                             
209                                for (String path: classpathItems){
210                                        // must check each match since parent path could possibly have a module name
211                                        // in it (don't break on first):
212                                        if (path.contains(sought)){
213                                                int lastIndex = path.lastIndexOf(sought);
214                                                String p = path.substring(0, lastIndex);
215                                                p = p.concat(sought + "module-info" + 
216                                                        File.separator + "osextension.txt");
217                                                osextension = new File(p);
218                                                if (osextension.exists()){
219                                                        break;
220                                                }
221                                        }
222                                }
223                                if (osextension == null || !osextension.exists()){
224                                        continue;
225                                }
226                                
227                                if(_isDebugging) {
228                                    log.debug("Found OS Extension file: "
229                                                + osextension.getAbsolutePath());
230                                }
231                                Hashtable<String, String> properties = readOSExtensionFile(osextension);
232                                Enumeration<String> keys = properties.keys();
233                                while (keys.hasMoreElements()) {
234                                        String extClass = keys.nextElement();
235                                        String os = properties.get(extClass);
236                                        if (OSName.trim().equals(os.trim())) {
237                                                // if we're in an OS that an extension
238                                                // needs to be loaded for attempt to load
239                                                // the OSExtension via reflection
240                                                // and run the addOSExtension method
241
242                                                Class<?> c = Class.forName(extClass);
243                                                try {
244                                                        OSExtension extension = (OSExtension) c
245                                                                        .newInstance();
246                                                        extension.addOSExtensions();
247                                             if(_isDebugging) {
248                                                 log.debug("loading OS extensions for OS "
249                                                                        + os + " with class " + extClass);
250                                             }
251                                                } catch (ClassCastException cce) {
252                                                        // System.out.println(extClass +
253                                                        // " is not an instance of OSExtension");
254                                                }
255                                        }
256                                }
257                        }
258                                                
259                        Project project = new Project();
260                        File projDir = ProjectLocator.getProjectDir();
261                        project.setBaseDir(projDir);
262
263                        // set the ProjectLocator ant project if it's not already set.
264                        // this prevents ProjectLocator.getAntProject() from returning null
265                        if(ProjectLocator.getAntProject() == null) {
266                            ProjectLocator.setAntProject(project);
267                        }
268                                                
269                        //_setOntologyIndexFile();
270                
271                        
272                        if (!argList.contains("-runwf")) {
273                                // Allow developers to turn off MakeKars by creating
274                                // a file called "skipMakeKars" in the project root
275                                File skipMakeKars = new File(projDir, "skipMakeKars");
276                                if (!skipMakeKars.exists()) {
277                                        MakeKars kar = new MakeKars();
278                                        kar.setProject(project);
279                                        kar.init();
280                                        kar.run();
281                                }
282                        }
283
284                        PermissionManager.makeNativeLibsExecutable();
285                        // CreateIntroFileTask createIntroFileTask = new
286                        // CreateIntroFileTask();
287                        // createIntroFileTask.execute();
288
289                } catch (URISyntaxException e) {
290                        e.printStackTrace();
291                } catch (IOException e) {
292                        e.printStackTrace();
293                } catch (Exception e) {
294                        e.printStackTrace();
295                }
296
297                int rc = runApplication();
298                
299                if(rc != 0) {
300                    System.exit(rc);
301                }
302                
303                // System.exit(0);
304
305                // long endTime = System.currentTimeMillis();
306                // System.out.println( (endTime-startTime)/1000.0 );
307                
308                        
309        RunnableExecutionQueue.execute();
310        
311        }
312
313        public static void setOntologyIndexFile() {
314                OntologyConfiguration oc = OntologyConfiguration.instance();
315                File f = FileUtil
316                                .getHighestRankedFile("configs/ptolemy/configs/kepler/ontologies/ontology_catalog.xml");
317                oc.setIndexFile(f);
318                oc.initialize();
319        }
320    
321        /**
322         * Parse the command line arguments and run the appropriate application.
323         * @return If true, no error occurred parsing the arguments.
324         */
325        public static boolean parseArgs(String[] args) {
326
327                try {
328                        // parse the switches and remaining arguments
329                        for (int i = 0; i < args.length; i++) {
330                                if (args[i].equals("-runwf")) {
331                                        _action = Action.RunWf;
332                                } else if (args[i].equals("-runkar")) {
333                                        _action = Action.RunKAR;
334                                } else if (args[i].equals("-nogui")) {
335                                        _assertRunWF(args[i]);
336                                        _runWithGui = false;
337                                } else if (args[i].equals("-nocache")) {
338                                        _assertRunWF(args[i]);
339                                        _runWithCache = false;
340                                } else if (args[i].equals("-nosplash")) {
341                                        _showSplash = false;
342                                } else if (args[i].equals("-noexit")) {
343                                    _runThenExitNoGui = false;
344                                } else if (args[i].equals("-gui")) {
345                                        _assertRunWF(args[i]);
346                                        _runWithGui = true;
347                                } else if (args[i].equals("-cache")) {
348                                        _assertRunWF(args[i]);
349                                        _runWithCache = true;
350                                } else if (args[i].equals("-vergil")) {
351                                        _action = Action.Vergil;
352                } else if (args[i].equals("-createActorXML")) {
353                    _action = Action.CreateActorXML;
354                    _runWithGui = false;
355                                } else if (args[i].equals("-hsql")) {
356                                    if(_action != Action.Kepler) {
357                                        throw new IllegalArgumentException("ERROR: -hsql may not be used with " +
358                                            _action.getArg());
359                                    }
360                                    
361                                    i++;
362                                    if(i == args.length) {
363                        throw new IllegalArgumentException("ERROR: must specify -hsql start or -hsql stop.");                                   
364                                    }
365                                    
366                                    if(args[i].equals("start")) {
367                            _action = Action.HSQLStart;                                 
368                                    } else if(args[i].equals("stop")) {
369                            _action = Action.HSQLStop;
370                                    } else {
371                                        throw new IllegalArgumentException("ERROR: must specify -hsql start or -hsql stop.");
372                                    }
373                                } else if (args[i].equals("-force")) {
374                                        _forceOpen = true;
375                } else if (args[i].equals("-noilwc")) {
376                    NamedObjId.incrementLSIDOnWorkflowChange(false);
377                } else if (args[i].equals("-updateActorDocs")) {
378                        _action = Action.UpdateActorDocs;
379                    _runWithGui = false;
380                } else if (args[i].equals("-h") || args[i].equals("-help")) {
381                                        _showHelp();
382                                        return false;
383                                }
384                // NOTE: -redirectdisplay is kept for backwards-compatibility 
385                else if(args[i].equals("-redirectgui") || args[i].equals("-redirectdisplay")){
386                                        _assertRunWF(args[i]);
387                        _runWithGui = false;
388                        if (i >= args.length) {
389                                throw new IllegalArgumentException("ERROR: cannot set "
390                                        + "redirectgui argument because no redirect dir for display actors is "
391                                + "given.");
392                        }
393                        _displayRedirectOutputPath = new String(args[++i]);
394                        if (!(new File(_displayRedirectOutputPath)).isDirectory()){
395                                throw new IllegalArgumentException("ERROR: cannot set "
396                                        + "redirectdisplay argument because the argument after '-redirectdisplay' is not a directory.");
397                        }
398                } else if(args[i].equals("-screenshot")) {
399                    _action = Action.MakeScreenShot;
400                    i++;
401                    _parseScreenShotArgs(args, i);
402                } else {
403                    
404                    boolean foundParser = false;
405                    for(CommandLineArgument arg: CommandLineArgument.getAll()) {
406                        // see if we've already parsed it.
407                        if(!arg.wasParsed()) {
408                                int next = arg.parseRemainingArgs(args, i);
409                                if(next >= 0) {
410                                    foundParser = true;
411                                    if(arg.isAction()) {
412                                        _action = Action.RunCustomAction;
413                                        _customActionClass = arg.getActionClass();
414                                    _customActionMethod = arg.getActionMethod();
415                                    }
416                                    // i is next - 1 since the for loop increments i.
417                                    i = next - 1;
418                                    // see if there are any more arguments left.
419                                    if(i >= args.length) {
420                                        break;
421                                    }
422                                }
423                        }
424                    }
425                    
426                    if(!foundParser) {
427                        _applicationArgsList.add(args[i]);
428                    }
429                                }
430                        }
431                } catch (IllegalArgumentException e) {
432                        System.out.print(e.getMessage());
433                        return false;
434                }
435                
436                return true;
437                
438        }
439        
440        /** Parse the command-line arguments for creating screenshots of workflows.
441         *  @param args an array of command-line arguments
442         *  @param i the index into command-line arguments array that points to the
443         *  argument after "-screenshot"
444         */
445        private static void _parseScreenShotArgs(String[] args, int i) {    
446            
447            // command-line usage for screenshots is:
448            //
449        // kepler -screenshot [-type png | jpg | gif] [-force]
450            //        [-o output [output2 ...] | -odir directory]
451            //        workflow.xml [workflow2.xml ...]");
452
453
454        while(i < args.length) {
455
456            if(args[i].equals("-type")) {
457                i++;
458                if(i == args.length) {
459                    throw new IllegalArgumentException("ERROR: must specify screen shot image type with -type argument.");
460                }
461                _screenShotType = args[i];
462            } else if(args[i].equals("-force")) {
463                _screenShotForce = true;
464            } else if(args[i].equals("-odir")) {
465                if(_screenShotOutputs != null) {
466                    throw new IllegalArgumentException("ERROR: cannot specify both -dir and -o with -screenshot.");
467                }
468                
469                i++;
470                if(i == args.length) {
471                    throw new IllegalArgumentException("ERROR: must specify output directory with -dir.");
472                }
473                
474                _screenShotOutputDir = args[i];
475                
476            } else if(args[i].equals("-o")) {
477                if(_screenShotOutputDir != null) {
478                    throw new IllegalArgumentException("ERROR: cannot specify both -dir and -o with -screenshot.");
479                }
480                
481                i++;
482                if(i == args.length) {
483                    throw new IllegalArgumentException("ERROR: must specify at least one output name with -o.");
484                }
485                
486                _screenShotOutputs = new LinkedList<String>();
487                while(i < args.length && !args[i].startsWith("-") &&
488                        // make sure it is not a workflow
489                        !args[i].toLowerCase().endsWith(".xml") &&
490                        !args[i].toLowerCase().endsWith(".kar")) {
491                    _screenShotOutputs.add(args[i]);
492                    i++;
493                }
494                // decrement i since it will be incremented again before
495                // the loop restarts.
496                i--;
497                
498            } else {
499                String name = args[i];
500                File file = new File(name);
501                if(!file.exists()) {
502                    throw new IllegalArgumentException("ERROR: workflow " + name + " does not exist.");
503                }
504                _screenShotWorkflows.add(name);
505            }
506            
507            i++;
508        }
509        
510        if(_screenShotWorkflows.isEmpty()) {
511            throw new IllegalArgumentException("ERROR: must specify at least one workflow with -screenshot.");
512        }
513        }
514        
515        /** Run the appropriate application based on command line arguments.
516         *  @return Returns 0 if success, 1 if there was an error. 
517         */
518        public static int runApplication() {
519            
520                try {
521                    
522                    if(_runWithCache) {
523                        setOntologyIndexFile();
524                    }
525                    
526                        // see if we're running the gui editor
527                        if (_action == Action.Kepler) {
528                                addActorModule("org/kepler/ActorModuleDefault");
529                                initialize();
530                                _applicationArgsList.addFirst("-kepler");
531                                String[] loadArgs = _applicationArgsList.toArray(new String[0]);
532                                if(_showSplash) {
533                                        VergilApplication.main(loadArgs);
534                                SplashWindow.disposeSplash();
535                                StatusNotifier.clearStatusListeners();
536                                } else {
537                                        VergilApplication.main(loadArgs);
538                                }
539                                                                                
540                                // check for patches now that the UI has started.
541                                PatchChecker.check(false);
542                                
543                        } else if (_action == Action.Vergil) {
544                                addActorModule("org/kepler/ActorModuleDefault");
545                                String[] loadArgs = _applicationArgsList.toArray(new String[0]);
546                                VergilApplication.main(loadArgs);
547                        } else if (_action == Action.RunWf || _action == Action.RunKAR) {
548                                final String className = "org.kepler.KeplerConfigurationApplication";                           
549
550                                // if we are running a workflow from the cmd line and have
551                                // the gui enabled, the program does not quit until the user
552                                // enters control-c.
553                                if(_runWithGui) {
554                                        _mustManuallyQuit = true;
555                                }
556                                addActorModule("org/kepler/ActorModuleDefault");
557                                if (_runWithGui && _runWithCache) {
558                                                _applicationArgsList.addFirst("-run");
559                                                _applicationArgsList.addFirst("ptolemy/configs/kepler/ConfigGUIAndCache.xml");
560                                } else if (_runWithGui && !_runWithCache) {
561                    _applicationArgsList.addFirst("-run");
562                                        _applicationArgsList.addFirst("ptolemy/configs/kepler/ConfigGUINoCache.xml");
563                                } else if (!_runWithGui && _runWithCache) {
564                                        String spec;
565                                        if (_displayRedirectOutputPath != null){
566                                                //add display redirection filter
567                                                BackwardCompatibility.addFilter(new au.edu.jcu.kepler.hydrant.DisplayRedirectFilter(_displayRedirectOutputPath));
568                                                addActorModule("org/kepler/ActorModuleBatch");
569                                                spec = "ptolemy/configs/kepler/ConfigRedirectGUIWithCache.xml";
570                                        } else {
571                                                spec = "ptolemy/configs/kepler/ConfigNoGUIWithCache.xml";
572                                        }
573                                        if(_runThenExitNoGui) {
574                                            _applicationArgsList.addFirst("-runThenExit");
575                                        }
576                                        _applicationArgsList.addFirst(spec);
577                                        // ConfigurationApplication.readConfiguration(ConfigurationApplication.specToURL(spec));
578                                } else { // if(!gui && !cache)
579                                        String spec;
580                                if (_displayRedirectOutputPath != null)                                 
581                                //enter into display redirection mode
582                                {
583                                        BackwardCompatibility.addFilter(
584                                                new au.edu.jcu.kepler.hydrant.DisplayRedirectFilter(_displayRedirectOutputPath));
585                                        addActorModule("org/kepler/ActorModuleBatch");
586//                                      DisplayRediectClassChanges.classChanges();
587//                                      BackwardCompatibility.addFilter(new DisplayRedirectFilter(outputPath));                                         
588                                                spec = "ptolemy/configs/kepler/ConfigRedirectGUINoCache.xml";
589
590                                } else {
591                                        spec = "ptolemy/configs/kepler/ConfigNoGUINoCache.xml";
592                                }
593                                    if(_runThenExitNoGui) {
594                                        _applicationArgsList.addFirst("-runThenExit");
595                                    }
596                                        _applicationArgsList.addFirst(spec);
597//                                              else{
598//                                                      className = "ptolemy.moml.MoMLCommandLineApplication";
599//                                              }
600                                }
601
602                                if (className != null) {
603                                        // initialize and run the class.
604                                        String[] loadArgs = _applicationArgsList.toArray(new String[0]);
605                                        load(className, loadArgs);
606                                }
607            } else if (_action == Action.CreateActorXML) {
608                String[] loadArgs = _applicationArgsList.toArray(new String[0]);
609                load("org.kepler.loader.util.UpdateActorTreeFiles", "buildXMLs", loadArgs);
610            } else if (_action == Action.UpdateActorDocs) {
611                String[] loadArgs = _applicationArgsList.toArray(new String[0]);
612                load("org.kepler.loader.util.UpdateActorTreeFiles", "updateKarXMLDocsForFile", loadArgs);
613                        } else if (_action == Action.HSQLStart) {
614                            System.out.println("going to start hsql servers.");
615                _runHSQLServers(true);
616                        } else if (_action == Action.HSQLStop) {
617                System.out.println("going to stop hsql servers.");              
618                _runHSQLServers(false);
619                        } else if (_action == Action.MakeScreenShot) {
620                            initialize();
621                        Screenshot.makeScreenshot(_screenShotWorkflows, _screenShotType, _screenShotOutputs,
622                                _screenShotOutputDir, _screenShotForce);
623                        shutdown();
624                        // TODO: why is System.exit() necessary?
625                        System.exit(0);
626                        } else if(_action == Action.RunCustomAction) {
627                            load(_customActionClass, _customActionMethod, null);
628                        } else {
629                            System.err.println("Unknown execution action: " + _action);
630                        }
631                } catch (IllegalArgumentException e) {
632                        System.out.print(e.getMessage());
633                        return 1;
634                } catch (Exception e) {
635                        System.out.println(e.getClass() + ": " + e.getMessage());
636                        e.printStackTrace();
637                        return 1;
638                }
639                
640                if(!_runWithGui && !_runThenExitNoGui) {
641                    System.out.println("Ctrl-c to exit.");
642                }
643
644                return 0;
645        }
646
647        /** Show the splash screen and start the kepler GUI. */
648        private static void _showSplash() {
649                try {
650
651                        ConfigurationProperty commonProperty = ConfigurationManager
652                                        .getInstance().getProperty(
653                                                        ConfigurationManager.getModule("common"));
654                        ConfigurationProperty splashscreenProp = commonProperty
655                                        .getProperty("splash.image");
656                        String splashname = splashscreenProp.getValue();
657
658                        final URL splashURL = ClassLoader.getSystemClassLoader()
659                                        .getResource(splashname);
660
661                        SplashWindow.splash(splashURL);
662                        
663                } catch (Exception ex) {
664                        System.err.println("Failed to find splash screen image."
665                                        + "Ignoring, use the Java coffee cup");
666                        ex.printStackTrace();
667                }
668        }
669
670        /**
671         * This method provides a generic loader. It first updates the classpath
672         * with all the jars in $KEPLER/lib/jar, and then uses reflection to invoke
673         * the method. NOTE: the method must be static.
674         */
675        public static void load(String className, String methodName, String[] args)
676                        throws IllegalActionException {
677                // sanity checks
678                if (className == null || className.equals("")) {
679                        throw new IllegalActionException("Must supply class to load.");
680                } else if (methodName == null || methodName.equals("")) {
681                        throw new IllegalActionException("Must supply method to invoke.");
682                }
683
684                try {
685                    initialize();
686                    
687                        if (className.equals("ptolemy.actor.gui.PtExecuteApplication")) {
688                                ptolemy.actor.gui.PtExecuteApplication application = new ptolemy.actor.gui.PtExecuteApplication(
689                                                args);
690                                application.runModels();
691                                application.waitForFinish();
692                        } else if(className.equals("org.kepler.KeplerConfigurationApplication")) {
693                                KeplerConfigurationApplication application = new KeplerConfigurationApplication(args);
694                                application.waitForFinish();
695                        } else {
696                                System.out.print("loading: " + className);
697                                if(args != null) {
698                                    System.out.println(" args: ");
699                                for (String arg : args) {
700                                        System.out.print(arg + " ");
701                                }
702                                }
703                                System.out.println();
704
705                                // invoke the class's method
706                                Class<?> cl = Class.forName(className);
707                                Method mthd = cl.getMethod(methodName, String[].class);
708                                System.out.println("invoking: " + className + "." + methodName);
709                                mthd.invoke(null, new Object[] { args });
710                        }
711                                                
712                        if (_mustManuallyQuit) {
713                                System.out.println("Done.");
714                                System.out.println("Ctrl-c to exit.");
715                        }
716                        
717                        // clean up modules unless we ran KeplerConfigurationApplication
718                        // since that class calls shutdown() after the models have been
719                        // executed.
720                        if(!className.equals("org.kepler.KeplerConfigurationApplication")) {
721                            _initializeModules(false);
722                        }
723
724                } catch (ClassNotFoundException e) {
725                        throw new IllegalActionException("ERROR: could not find start-up class: "
726                                        + className);
727                } catch (NoSuchMethodException e) {
728                    throw new IllegalActionException("ERROR: class " + className
729                                        + " does not have a method called " + methodName);
730                } catch (Throwable throwable) {
731                    throw new IllegalActionException("Unable to start application: " +
732                            ExceptionUtils.getStackTrace(throwable));
733                }
734        }
735
736        /** Load classes and invoke "main" method. */
737        public static void load(String className, String[] args)
738                        throws IllegalActionException {
739                load(className, "main", args);
740        }
741
742        /** Perform initialization. */
743        public static void initialize() throws Exception {
744        _initializeCommandLineArguments();
745                if (!_haveInitialized) {
746                        _initializeModules(true);
747                }
748        }
749        
750        /** Perform module cleanup. */
751        public static void shutdown() {
752            _initializeModules(false);
753        }
754
755        /** Returns true if -force was specified on the command line. */
756        public static boolean getForceOpen() {
757                return _forceOpen;
758        }
759
760        /** Returns true if -nogui was specified on the command line. */
761        public static boolean getRunWithGUI() {
762                return _runWithGui;
763        }
764
765        /** Set Kepler java properties and copy the module directories into KeplerData/. */
766        public static void setJavaPropertiesAndCopyModuleDirectories() throws URISyntaxException, IOException {
767        
768                StatusNotifier.log("Setting Java Properties.");
769                
770            SystemPropertyLoader.load();
771        System.setProperty("KEPLER", ProjectLocator.getProjectDir()
772                .getAbsolutePath());
773        String persistentDir = DotKeplerManager.getInstance()
774                .getPersistentDirString();
775        System.setProperty("KEPLERDATA", persistentDir);
776
777        String dotKepler = DotKeplerManager.getDotKeplerPath();
778        System.setProperty(".kepler", dotKepler);
779
780        String keplerUserData = DotKeplerManager.getInstance()
781                .getPersistentUserDataDirString();
782        System.setProperty("KEPLERUSERDATA", keplerUserData);
783
784        String personalModuleWorkflowDirStr = DotKeplerManager
785                .getInstance().getPersistentModuleWorkflowsDirString();
786        String docDirStr = DotKeplerManager.getInstance()
787                .getPersistentDocumentationDirString();
788
789        StatusNotifier.log("Copying Module Files.");
790
791        List<Module> modules = ModuleTree.instance().getModuleList();
792        Iterator<Module> moduleItr = modules.iterator();
793        while (moduleItr.hasNext()) {
794            Module m = moduleItr.next();
795            File applicationModuleWorkflowsDir = m.getWorkflowsDir();
796            File applicationModuleDocDir = m.getDocumentationDir();
797            // we want the full name, use getName not getStemName:
798            File moduleWorkflowDir = new File(personalModuleWorkflowDirStr
799                    + File.separator + m.getName());
800            File personalModuleDocDir = new File(docDirStr + File.separator
801                    + m.getName());
802
803            System.setProperty(m.getStemName() + ".workflowdir",
804                    moduleWorkflowDir.toString() + File.separator);
805
806            if (applicationModuleWorkflowsDir.exists()) {
807                FileUtil.copyDirectory(applicationModuleWorkflowsDir,
808                        moduleWorkflowDir, false);
809            } else {
810                log.debug(m + " workflow dir does not exist.");
811            }
812
813            if (applicationModuleDocDir.exists()) {
814                if (!personalModuleDocDir.exists()) {
815                    log.warn(personalModuleDocDir + " does not exist.");
816                    log.warn("copy(" + applicationModuleDocDir + ","
817                            + personalModuleDocDir + ")");
818                    FileUtil.copyDirectory(applicationModuleDocDir,
819                            personalModuleDocDir, false);
820                }
821            }
822        
823        }
824        }
825
826    /** Set the class and method to invoke instead of starting Kepler. */
827        public static void setRunApplication(String className, String method) {
828            _action = Action.RunCustomAction;
829            _customActionClass = className;
830            _customActionMethod = method;
831        }
832        
833        // ////////////////////////////////////////////////////////////////////
834        // // private methods ////
835
836        /** Make sure that -runwf was specified. */
837        private static void _assertRunWF(String arg)
838                        throws IllegalArgumentException {
839                if (_action != Action.RunWf && _action != Action.RunKAR) {
840                        throw new IllegalArgumentException("ERROR: " + arg
841                                        + " may only be used with -runwf");
842                }
843        }
844
845        /** Run Kepler startup or shutdown module intializers. */
846        private static void _initializeModules(boolean start) {
847            _runModuleInitializers(start,
848            start ? "Initialize" : "Shutdown",
849            start ? "initializeModule" : "shutdownModule");
850
851            // update whether we initialized or shut down.
852        _haveInitialized = start;
853        }
854        
855        /** Initialize command line arguments. */
856        private static void _initializeCommandLineArguments() {
857            if(!_haveInitializedCommandLineArguments) {
858            _runModuleInitializers(true, "CommandLineArguments",
859                    "initializeCommandLineArguments");
860            _haveInitializedCommandLineArguments = true;
861            }
862        }
863        
864        /** Run any module initializers with specific class and method names.
865         * @param start If true, the modules are called starting from the bottom
866         * of the suite. 
867         * @param className The name of the module initializer class to run.
868         * @param methodName The name of the method in the class to invoke.
869         */
870        private static void _runModuleInitializers(boolean start, String className, String methodName) {
871                ModuleTree tree = ModuleTree.instance();
872
873                Iterable<Module> moduleList = null;
874                
875                // on startup, call initializers starting at lowest dependency,
876                // i.e., at the bottom of modules.txt.
877                // on shutdown, start at top of modules.txt
878                if(start) {
879                    moduleList = tree.reverse();
880                } else {
881                    moduleList = tree;
882                }
883                
884                for (Module module : moduleList) {
885                        String name = module.getName();
886                        name = module.getStemName();
887
888                        // construct the class name
889                        
890                        // dashes and periods are illegal characters in package names
891                        name = name.replaceAll("-", "");
892                        name = name.replaceAll("\\.", "");
893                        String fullClassName = "org.kepler.module." + name + "." + className;
894                        //System.out.println("looking for class: " + fullClassName);
895
896                        try {
897                                // attempt to find and instantiate it
898                                Class<?> moduleClass = Class.forName(fullClassName);
899                                
900                // call the initializer
901                                Object instance = moduleClass.newInstance();
902                                
903                if (className.equals("Initialize")) {
904                    StatusNotifier.log("Initializing Module: " + name + ".");
905                    ((ModuleInitializer)instance).initializeModule();
906                    
907                    if(_isDebugging) {
908                        log.debug("Ran additional initialization for module "
909                            + name + " from class " + fullClassName);
910                    }
911                } else {
912                    Method method = moduleClass.getMethod(methodName);
913                    method.invoke(instance);
914                }
915                                
916                        } catch (ClassNotFoundException e) {
917                                // it's not required that every module have an initializer.
918                                // System.out.println("initializer class not found for " +
919                                // module);
920                        } catch (InstantiationException e) {
921                                System.out.println("ERROR instantiating " + fullClassName + ": "
922                                                + e.getMessage());
923                        } catch (IllegalAccessException e) {
924                                System.out.println(e.getMessage());
925                        } catch(NoSuchMethodException | InvocationTargetException e) {
926                            System.out.println(e.getMessage());
927                        }
928                }               
929        }
930
931    /**
932     * read the osextension.txt file and return a hashtable of the properties
933     * 
934     * NOTE this method is duplicated in CompileModules.java. Change both if you
935     * change one.
936     */
937    private static Hashtable<String, String> readOSExtensionFile(File f)
938            throws Exception {
939        // String newline = System.getProperty("line.separator");
940        Hashtable<String, String> properties = new Hashtable<String, String>();
941        FileReader fr = new FileReader(f);
942        StringBuffer sb = new StringBuffer();
943        char[] c = new char[1024];
944        int numread = fr.read(c, 0, 1024);
945        while (numread != -1) {
946            sb.append(c, 0, numread);
947            numread = fr.read(c, 0, 1024);
948        }
949        fr.close();
950    
951        String propertiesStr = sb.toString();
952        // String[] props = propertiesStr.split(newline);
953        String[] props = propertiesStr.split(";");
954        for (int i = 0; i < props.length; i++) {
955            String token1 = props[i];
956            StringTokenizer st2 = new StringTokenizer(token1, ",");
957            String key = st2.nextToken();
958            String val = st2.nextToken();
959            properties.put(key, val);
960        }
961    
962        return properties;
963    }   
964
965    /** Start or stop any module HSQL servers. */
966    private static void _runHSQLServers(boolean start) {
967        
968        org.kepler.util.sql.HSQL.setForkServers(true);              
969
970        ModuleTree tree = ModuleTree.instance();
971
972        Iterable<Module> moduleList = null;
973        
974        // if start, call at lowest dependency, i.e., at the bottom of modules.txt.
975        // if stop, call at top of modules.txt
976        if(start) {
977            moduleList = tree.reverse();
978        } else {
979            moduleList = tree;
980        }
981        
982        for (Module module : moduleList) {
983            String name = module.getName();
984            name = module.getStemName();
985
986            // construct the class name
987            if (name.indexOf("-") != -1) { // dashes are illegal characters in
988                                            // package names so we need to
989                                            // remove them
990                name = name.replaceAll("-", "");
991            }
992            String fullClassName = "org.kepler.module." + name + ".HSQLManager";
993            //System.out.println("looking for class: " + className);
994
995            try {
996                // attempt to find and instantiate it
997                Class<?> moduleClass = Class.forName(fullClassName);
998                ModuleHSQLManager manager = (ModuleHSQLManager) moduleClass.newInstance();
999                
1000                if (start) {
1001                    manager.start();
1002                } else {
1003                    manager.stop();
1004                }
1005                
1006            } catch (ClassNotFoundException e) {
1007                // it's not required that every module have an hsql manager.
1008            } catch (InstantiationException e) {
1009                System.out.println("ERROR instantiating " + fullClassName + ": "
1010                        + e.getMessage());
1011            } catch (IllegalAccessException e) {
1012                System.out.println(e.getMessage());
1013            }
1014        }
1015    }
1016
1017    /** Add the display related actor implementation information, different ActorModule properties 
1018     * file specify different display implementation.
1019     * The two ActorModule files in Kepler are located at display-redirect/src/org/kepler/.
1020     * The ActorModule file in ptolemy is located at ptolemy/src/ptolemy/actor/.
1021     * More info can be found at ptolemy.actor.injection.ActorModuleInitializer 
1022     */
1023    private static void addActorModule(String bundleFile){
1024                final ArrayList<PtolemyModule> actorModules = new ArrayList<PtolemyModule>();
1025            actorModules.add(new PtolemyModule(ResourceBundle
1026                        .getBundle(bundleFile)));
1027                Initializer _defaultInitializer = new Initializer() {
1028                @Override
1029            public void initialize() {
1030                    PtolemyInjector.createInjector(actorModules);
1031                }
1032                };
1033                ptolemy.actor.injection.ActorModuleInitializer.setInitializer(_defaultInitializer);
1034    }
1035    
1036        /** Print usage and exit. */
1037        private static void _showHelp() {
1038        System.out.println("USAGE:");
1039        System.out.println();
1040        System.out.println("To run the Kepler GUI:");
1041        System.out.println("kepler [-nosplash] [workflow.xml | workflow.kar]");
1042        System.out.println("-nosplash               start without showing splash screen.");
1043        System.out.println();
1044        System.out.println("To run the Ptolemy GUI:");
1045        System.out.println("kepler -vergil [workflow.xml]");
1046        System.out.println();
1047        System.out.println("To run a workflow XML from the command line:");
1048        System.out.println("kepler -runwf [-nogui | -redirectgui dir] [-nocache] [-noilwc] "
1049                + "[-paramFile file] [-param1 value1 ...] workflow.xml");
1050        System.out.println("-nogui                  run without GUI support.");
1051        System.out.println("-nocache                run without kepler cache.");
1052        System.out.println("-noilwc                 run without incrementing LSIDs when the workflow changes.");
1053        System.out.println("-noexit                 do not exit after execution when running with -nogui");
1054        System.out.println("-paramFile file         read parameters from file.");
1055        System.out.println("-redirectgui dir        redirect the contents of GUI actors to the specified directory.");
1056        System.out.println();
1057        System.out.println("To run a workflow KAR from the command line:");
1058        System.out.println("kepler -runkar [-nogui | -redirectgui dir] [-force] [-paramFile file] [-param1 value1 ...] workflow.kar");
1059        System.out.println("-force                  attempt to run ignoring missing module dependencies.");
1060        System.out.println("-nogui                  run without GUI support.");
1061        System.out.println("-paramFile file         read parameters from file.");
1062        System.out.println("-redirectgui dir        redirect the contents of GUI actors to the specified directory.");
1063        System.out.println();
1064        System.out.println("To start or stop the HSQL database servers:");
1065        System.out.println("kepler -hsql start");
1066        System.out.println("kepler -hsql stop");
1067        System.out.println();
1068        System.out.println("To create a workflow screen shot:");
1069        System.out.println("kepler -screenshot [-type png | jpg | gif] [-force] [-o output [output2 [...]] | -odir directory] workflow.xml [workflow2.xml [...]]");
1070        System.out.println("-force                      if true, always create the screenshot.");
1071        System.out.println("-o output [output2 [...]]   the output file name(s).");
1072        System.out.println("-odir dir                   the output directory.");
1073        System.out.println("-type png|jpg|gif           the image type.");
1074        System.out.println();
1075        System.out.println("The following options are for actor developers:");
1076        System.out.println();
1077        System.out.println("To create XML file(s) describing an actor using either the source file or class name:");
1078        System.out.println("kepler -createActorXML file1.java|class1 [file2.java|class2 ...]");
1079        System.out.println();
1080        System.out.println("To create the documentation for an item in the actor tree:");
1081        System.out.println("kepler -updateActorDocs [-overwrite] file1.xml [file2.xml ...]");
1082        System.out.println("-overwrite              overwrite any existing documentation.");
1083        
1084        Collection<CommandLineArgument> args = CommandLineArgument.getAll();
1085        if(!args.isEmpty()) {
1086            System.out.println();
1087            System.out.println("Additional command-line arguments:");
1088            System.out.println();
1089            for(CommandLineArgument arg: args) {
1090                System.out.println(arg.getUsage());
1091            }
1092        }
1093        }
1094
1095        ////////////////////////////////////////////////////////////////////
1096    // private classes                                              ////
1097
1098        /** A class that implements ShutdownListener. The class 
1099         *  org.kepler.Kepler cannot be used since it is never
1100         *  instantiated.
1101         */
1102        private static class Shutdown implements ShutdownListener
1103        {
1104        /** Perform any module cleanup tasks. */
1105        @Override
1106        public void shutdown() {
1107            Kepler._initializeModules(false);
1108        }
1109        }
1110        
1111        ////////////////////////////////////////////////////////////////////
1112        // private variables                                            ////
1113
1114        /** The types of actions. */
1115        private enum Action {
1116                Kepler(""),
1117                RunWf("-runwf"),
1118                RunKAR("-runkar"),
1119        CreateActorXML("-createActorXML"),
1120                Vergil("-vergil"),
1121                HSQLStart("-hsql"),
1122                HSQLStop("-hsql"),
1123                UpdateActorDocs("-updateActorDocs"),
1124                MakeScreenShot("-screenshot"),
1125                RunCustomAction("");
1126                
1127                Action(String arg) {
1128                    _arg = arg;
1129                }
1130                
1131                public String getArg() {
1132                    return _arg;
1133                }
1134                
1135                private String _arg;
1136        };
1137
1138        /** The action to perform. By default, start kepler GUI. */
1139        private static Action _action = Action.Kepler;
1140
1141        /** Boolean to see if we've initialized. */
1142        private static boolean _haveInitialized = false;
1143
1144        /** A copy of the command line arguments. */
1145        private static String[] _args;
1146
1147        /** Logging. */
1148        private final static Log log = LogFactory.getLog(Kepler.class);
1149        
1150        /** True if log level is set to DEBUG. */
1151        private final static boolean _isDebugging = log.isDebugEnabled();
1152        
1153        /** If true, the program must be manually quit by the user. */
1154        private static boolean _mustManuallyQuit = false;
1155
1156        /** If true, -nogui was specified on the command line. */
1157        private static boolean _runWithGui = true;
1158        
1159        /** If true, start the Kepler cache. If -nocache specified on
1160         *  the command line, this is false.
1161         */
1162    private static boolean _runWithCache = true;
1163        
1164    /** If true, show the splash screen. */
1165    private static boolean _showSplash = true;
1166    
1167    /** The output path for redirectdisplay option. */
1168    private static String _displayRedirectOutputPath = null;
1169
1170        /** If true, -force was specified on the command line. */
1171        private static boolean _forceOpen = false;
1172        
1173    private static LinkedList<String> _applicationArgsList = new LinkedList<String>();
1174
1175    /** The image type for the screenshot.*/
1176    private static String _screenShotType;
1177    
1178    /** A set of output screenshot file names. */ 
1179    private static  List<String> _screenShotOutputs;
1180    
1181    /** The output directory for screenshots. */
1182    private static String _screenShotOutputDir;
1183    
1184    /** If true, always create screenshots. Otherwise, screenshots are only taken if
1185     *  either the screenshot image does not exist or is older than the workflow.
1186     */
1187    private static boolean _screenShotForce = false;
1188    
1189    /** A set of workflows to take screenshots of. */
1190    private static List<String> _screenShotWorkflows = new LinkedList<String>();
1191
1192    /** If true, then call System.exit() after running with no gui. */
1193    private static boolean _runThenExitNoGui = true;
1194
1195    /** The class name containing _customActionMethod to run instead of starting Kepler. */
1196    private static String _customActionClass;
1197
1198    /** The method in _customActionClass to run instead of starting Kepler. */
1199    private static String _customActionMethod;
1200    
1201    /** If true, command line arguments have been initialized. */
1202    private static boolean _haveInitializedCommandLineArguments = false;
1203    
1204}