001/*
002 * Copyright (c) 2012 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-11-23 20:26:17 +0000 (Mon, 23 Nov 2015) $' 
007 * '$Revision: 34244 $'
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.ddp;
031
032import java.io.File;
033import java.lang.management.ManagementFactory;
034import java.util.ArrayList;
035import java.util.Arrays;
036import java.util.LinkedList;
037import java.util.List;
038import java.util.ResourceBundle;
039
040import org.apache.commons.logging.Log;
041import org.apache.commons.logging.LogFactory;
042import org.kepler.configuration.ConfigurationManager;
043import org.kepler.configuration.ConfigurationProperty;
044import org.kepler.ddp.actor.pattern.stub.StubBaseActor;
045import org.kepler.ddp.director.DDPEngine;
046import org.kepler.provenance.ProvenanceRecorder;
047import org.kepler.reporting.ReportingListener;
048
049import au.edu.jcu.kepler.hydrant.DisplayRedirectFilter;
050import ptolemy.actor.CompositeActor;
051import ptolemy.actor.Director;
052import ptolemy.actor.ExecutionListener;
053import ptolemy.actor.IORelation;
054import ptolemy.actor.Manager;
055import ptolemy.actor.TypedIOPort;
056import ptolemy.actor.injection.ActorModuleInitializer;
057import ptolemy.actor.injection.ActorModuleInitializer.Initializer;
058import ptolemy.actor.injection.PtolemyInjector;
059import ptolemy.actor.injection.PtolemyModule;
060import ptolemy.data.DoubleToken;
061import ptolemy.data.IntToken;
062import ptolemy.data.LongToken;
063import ptolemy.data.RecordToken;
064import ptolemy.data.StringToken;
065import ptolemy.data.Token;
066import ptolemy.data.type.BaseType;
067import ptolemy.data.type.RecordType;
068import ptolemy.data.type.Type;
069import ptolemy.domains.ddf.kernel.DDFDirector;
070import ptolemy.domains.sdf.kernel.SDFDirector;
071import ptolemy.kernel.util.Attribute;
072import ptolemy.kernel.util.IllegalActionException;
073import ptolemy.kernel.util.Nameable;
074import ptolemy.kernel.util.Workspace;
075import ptolemy.moml.MoMLParser;
076import ptolemy.moml.filter.BackwardCompatibility;
077import ptolemy.moml.filter.RemoveGraphicalClasses;
078
079/** A collection of utilities for DDP classes. The methods and fields in this
080 *  class are not specific to a DDP engine.
081 * 
082 * @author Daniel Crawl and Jianwu Wang
083 * @version $Id: Utilities.java 34244 2015-11-23 20:26:17Z crawl $
084 * 
085 */
086
087public class Utilities {
088            
089    /** Check the director and its iterations with the runWorkflowLifecyclePerInput value.
090     *  If the director is SDF, this method changes the iterations based on the
091     *  value in runWorkflowLifecyclePerInput. If the director is DDF, then
092     *  runWorkflowLifecyclePerInput is changed to true.
093     *  @param model the model to execute in the stub.
094     *  @param  runWorkflowLifecyclePerInput if true, the full lifecycle of the
095     *  model will be executed for each input. If false, only one iteration of the
096     *  model will be execute for each input.
097     *  @return the (possibly changed) runWorkflowLifecyclePerInput value.
098     */
099    public static boolean checkDirectorIterations(CompositeActor model, boolean runWorkflowLifecyclePerInput) {
100        
101        Director director = model.getDirector();
102        if(director instanceof SDFDirector) {
103            Token iterations;
104            // change the iterations based on runWorkflowLifecyclePerInput
105            if(runWorkflowLifecyclePerInput) {
106                iterations = IntToken.ONE;
107            } else {
108                iterations = SDFDirector.UNBOUNDED_INTTOKEN;
109            }
110            try {
111                ((SDFDirector)director).iterations.setToken(iterations);
112            } catch (IllegalActionException e) {
113                throw new RuntimeException("Error changing iterations for SDF director: " +
114                        e.getMessage());
115            }
116        } else if((director instanceof DDFDirector) && !runWorkflowLifecyclePerInput) {
117            runWorkflowLifecyclePerInput = true;
118            String message = "Sub-workflow " + model.getName() + 
119                    " uses DDF director, but runWorkflowLifecyclePerInput is false;" +
120                    " this is not supported so changing runWorkflowLifecyclePerInput to true.";
121            System.out.println("WARNING: " + message);
122            LOG.warn(message);
123        } else {
124            String message = "Unexpected director in sub-workflow " +
125                    model.getName() + ": " + director.getClass().getName() +
126                    ". The sub-workflow may not execute correctly.";
127            System.out.println("WARNING: " + message);
128            LOG.warn(message);
129        }
130
131        return runWorkflowLifecyclePerInput;
132    }
133    
134    /** Create a Manager for a model and optionally start execution.
135     *  @param model the workflow
136     *  @param listener an ExecutionListener to register with the Manager to
137     *  receive error messages.
138     *  @param stubSource the source actor in the model
139     *  @param stubSink the sink actor in the model
140     *  @param  runWorkflowLifecyclePerInput if true, execute the full lifecycle of the
141     *  model for each input and the stub must tell the manager to execute the
142     *  model. If false, this method starts execution.
143     *  @param enablePrintTimeAndMemory if true, print an execution summary each time
144     *  the model executes.  
145     */
146    public static Manager createManagerForModel(CompositeActor model, ExecutionListener listener,
147            StubBaseActor stubSource, StubBaseActor stubSink, boolean runWorkflowLifecyclePerInput,
148            boolean enablePrintTimeAndMemory) {
149       
150        Manager manager = null;
151        
152        // create manager and add model
153        try {
154            manager = new Manager(model.workspace(), "Manager");
155        } catch(IllegalActionException e) {
156            throw new RuntimeException("Error creating Manager: " + e.getMessage());
157        }
158
159        try {
160            model.setManager(manager);
161        } catch(IllegalActionException e) {
162            throw new RuntimeException("Error setting Manager for sub-workflow: " + e.getMessage());
163        }
164
165        manager.enablePrintTimeAndMemory(enablePrintTimeAndMemory);
166        manager.addExecutionListener(listener);
167                
168        if(runWorkflowLifecyclePerInput) {
169            stubSource.setRunWorkflowLifecyclePerInput(runWorkflowLifecyclePerInput);
170            stubSink.setRunWorkflowLifecyclePerInput(runWorkflowLifecyclePerInput);
171        } else {
172            try {
173                manager.startRun();
174            } catch (Exception e) {
175                throw new RuntimeException("Error starting sub-workflow.", e);
176            }
177        }
178        
179        return manager;
180    }
181
182    /** Load the model for a stub from a Nephele Configuration. The 
183     *  top-level ports and connected relations are removed.
184     */
185    public static synchronized CompositeActor getModel(String modelName,
186            String modelString, String modelFile, boolean sameJVM, String redirectDir) {
187        
188        CompositeActor model = null;
189
190        if(modelName == null) {
191            throw new RuntimeException("Subworkflow name was not set in configuration.");
192        }
193        
194        if(sameJVM) {
195            
196            CompositeActor originalModel = DDPEngine.getModel(modelName);
197            try {
198                model = (CompositeActor) originalModel.clone(new Workspace());
199            } catch (CloneNotSupportedException e) {
200                throw new RuntimeException("Error cloning subworkflow: " + e.getMessage());
201            }
202            
203            Utilities.removeModelPorts(model);          
204            
205            // create an effigy for the model so that gui actors can open windows.
206            DDPEngine.createEffigy(model);
207                        
208        } else {
209            
210            List<?> filters = MoMLParser.getMoMLFilters();
211    
212            Workspace workspace = new Workspace();
213            final MoMLParser parser = new MoMLParser(workspace);
214            
215            //parser.resetAll();
216            
217            MoMLParser.setMoMLFilters(null);
218            MoMLParser.setMoMLFilters(BackwardCompatibility.allFilters(), workspace);
219    
220            if (redirectDir.isEmpty()) {
221                MoMLParser.addMoMLFilter(new RemoveGraphicalClasses(), workspace);
222            } else {
223                //redirect display-related actors 
224                final String pid = ManagementFactory.getRuntimeMXBean().getName();
225                final String threadId = String.valueOf(Thread.currentThread().getId());
226                final String displayPath = redirectDir + File.separator + pid + "_" + threadId;
227                MoMLParser.addMoMLFilter(new DisplayRedirectFilter(displayPath), workspace);
228                final ArrayList<PtolemyModule> actorModules = new ArrayList<PtolemyModule>();
229                actorModules.add(new PtolemyModule(ResourceBundle
230                            .getBundle("org/kepler/ActorModuleBatch")));
231                Initializer _defaultInitializer = new Initializer() {
232                    @Override
233                    public void initialize() {
234                        PtolemyInjector.createInjector(actorModules);
235                    }
236                };
237                ActorModuleInitializer.setInitializer(_defaultInitializer);
238            }
239            
240            // get the model from the configuration
241    
242            // see if model is in the configuration.
243            if(modelString != null) {
244    
245                try {
246                    model = (CompositeActor)parser.parse(modelString);
247                } catch (Exception e) {
248                    throw new RuntimeException("Error parsing model " + modelName +
249                        ": " + e.getMessage());
250                }            
251            
252                //LOG.info("parsed model from " + modelString);
253    
254            } else {
255                
256                // the model was saved as a file.
257                
258                if(modelFile == null) {
259                    throw new RuntimeException("No model for " + modelName +
260                        " in configuration.");
261                }
262                
263                // load the model
264                try {
265                    model = (CompositeActor)parser.parseFile(modelFile);
266                } catch (Exception e) {
267                    e.printStackTrace();
268                    throw new RuntimeException("Error parsing model " + modelName +
269                        " in file " + modelFile + ": " + e.getMessage());
270                }
271            
272                LOG.info("parsed model from " + modelFile);
273            }
274            
275            // restore the moml filters
276            MoMLParser.setMoMLFilters(null);
277            MoMLParser.setMoMLFilters(filters);
278            
279            // remove provenance recorder and reporting listener
280            final List<Attribute> toRemove = new LinkedList<Attribute>(
281                    model.attributeList(ProvenanceRecorder.class));
282            toRemove.addAll(model.attributeList(ReportingListener.class));
283            for(Attribute attribute : toRemove) {
284                try {
285                    attribute.setContainer(null);
286                } catch (Exception e) {
287                    throw new RuntimeException("Error removing " + attribute.getName() +
288                            ": " + e.getMessage());
289                }
290            }
291
292        }
293        
294        return model;
295    }
296    /** Remove top-level ports and relations from a composite actor. */
297    public static void removeModelPorts(CompositeActor model) {
298               
299        // get a copy of the list since we modify the original when ports
300        // are removed.
301        final List<?> ports = new LinkedList<Object>(model.portList());
302        for(Object object : ports) {
303            final TypedIOPort port = (TypedIOPort)object;
304            //final String name = port.getName();
305
306            //LOG.info("going to replace port " + name + " in model.");
307
308            // NOTE: do not remove ports connected to the top-level ports, since
309            // their types are used to the the input/output types of the stub.
310            /*
311            final List<?> connectedPorts = new LinkedList<Object>(port.insidePortList());
312            for(Object obj : connectedPorts) {
313                try {
314                    ((IOPort)obj).setContainer(null);
315                } catch (Exception e) {
316                    throw new RuntimeException("Error removing port " +
317                        ((IOPort)obj).getFullName() + " from model: " +
318                        e.getMessage());
319                }
320            }
321            */
322
323            /*
324            // print types of connected ports
325            final List<?> connectedPorts = new LinkedList<Object>(port.insidePortList());
326            for(Object obj : connectedPorts) {
327                LOG.info("type for " + obj + " is " + ((TypedIOPort)obj).getType());
328            } 
329            */                   
330            
331            // remove relations
332            final List<?> connectedRelations = new LinkedList<Object>(port.insideRelationList());            
333            for(Object obj : connectedRelations) {
334                try {
335                    ((IORelation)obj).setContainer(null);
336                } catch (Exception e) {
337                    throw new RuntimeException("Error removing relation " +
338                        "from model: " + e.getMessage());
339                }
340            }            
341            
342            // remove the top-level port
343            try {
344                port.setContainer(null);
345            } catch (Exception e) {
346                throw new RuntimeException("Error removing port " +
347                    port.getFullName() + " from model: " + e.getMessage());
348            }
349        }
350        
351    }
352     
353    /** Create a Token from a string based on the Ptolemy Type. */
354    public static Token createTokenFromString(String tokenStr, Type type) {
355        try {
356            if(type == BaseType.STRING) {
357                return new StringToken(tokenStr);
358            } else if(type == BaseType.INT) {
359                return new IntToken(tokenStr);
360            } else if(type == BaseType.DOUBLE) {
361                return new DoubleToken(tokenStr);
362            } else if(type == BaseType.LONG) {
363                return new LongToken(tokenStr);
364            } else if(type == BaseType.NIL) {
365                return Token.NIL;
366            } else if(type instanceof RecordType) {
367                return new RecordToken(tokenStr);
368            } else {
369                throw new RuntimeException("Unknown type of token: " + type);
370            }
371        } catch(IllegalActionException e) {
372            throw new RuntimeException("Error creating token.", e);
373        }
374    }
375    
376    /** Create a Token from a string based on the TokenType enum. */
377    public static Token createTokenFromString(String tokenStr, TokenType type) {
378            
379        try {
380            switch(type) {
381                case String:
382                    return new StringToken(tokenStr);
383                case Int:
384                    return new IntToken(tokenStr);
385                case Double:
386                    return new DoubleToken(tokenStr);
387                case Long:
388                    return new LongToken(tokenStr);
389                case Nil:
390                    return Token.NIL;
391                case Record:
392                    return new RecordToken(tokenStr);
393                default:
394                    throw new RuntimeException("Unknown type of token: " + type);
395            }
396        } catch(IllegalActionException e) {
397            throw new RuntimeException("Error creating token.", e);
398        }
399    }
400    
401    /** Get the TokenType for a token. */
402    public static TokenType getTokenTypeForSerialization(Token token) {
403        
404        Type type = token.getType();
405        
406        if(type == BaseType.STRING) {
407            return TokenType.String;
408        } else if(type == BaseType.INT) {
409            return TokenType.Int;
410        } else if(type == BaseType.DOUBLE) {
411            return TokenType.Double;
412        } else if(type == BaseType.LONG) {
413            return TokenType.Long;
414        } else if(type == BaseType.NIL) {
415            return TokenType.Nil;
416        } else if(type instanceof RecordType) {
417            return TokenType.Record;
418        } else {
419            throw new RuntimeException("Serialization of Ptolemy type " +
420                type.toString() + " is unsupported.");
421        }
422
423    }
424    
425    /** Get the ConfigurationProperty for an Engine on the configuration file.
426     *  @param engineName The name of the ddp engine
427     *  @param source The source object calling this method
428     *  @return If found, returns the ConfigurationProperty. If not found,
429     *  throws an exception.
430     */
431    public static ConfigurationProperty getEngineProperty(String engineName,
432        Nameable source) throws IllegalActionException {
433        
434        List<ConfigurationProperty> engineProperties = ConfigurationManager
435                .getInstance()
436                .getProperty(ConfigurationManager.getModule("ddp-common"))
437                .getProperties("Engines.Engine");   
438        
439        if(engineProperties == null || engineProperties.isEmpty()) {
440            throw new IllegalActionException(source, "No DDP engines found.");
441        }
442        
443        for(ConfigurationProperty engineProperty : engineProperties) {
444            ConfigurationProperty nameProperty = engineProperty.getProperty("Name");
445            if(nameProperty == null) {
446                throw new IllegalActionException(source, "DDP Engine does not have a name. Try deleting\n" +
447                        "$HOME/KeplerData/modules/ddp-common/configuration/configuration.xml\n" +
448                        "and restarting Kepler.");
449            }
450            
451            if (nameProperty.getValue().equalsIgnoreCase(engineName)) {
452                return engineProperty;
453            }
454        }
455
456        // not found
457        throw new IllegalActionException(source,
458            "No " + engineName + " Engine on configuration file.");
459    }
460    
461    /** Get a list of available execution language names. */
462    public static String[] getExecutionLanguageNames() throws IllegalActionException {
463        
464        List<String> names = new ArrayList<String>();
465        
466        final List<ConfigurationProperty> languageList = ConfigurationManager
467                .getInstance()
468                .getProperty(ConfigurationManager.getModule("ddp-common"))
469                .getProperties("ExecutionLanguages.Language");
470        
471        for(ConfigurationProperty property : languageList) {
472            ConfigurationProperty nameProperty = property.getProperty("Name");
473            if(nameProperty == null) {
474                throw new IllegalActionException(
475                        "Missing name for execution language in configuration file.");
476            }
477            names.add(nameProperty.getValue());
478        }
479        
480        String[] array = names.toArray(new String[names.size()]);
481        Arrays.sort(array);
482        return array;
483    }
484    
485    /** Get the jars for a language. Returns an empty list if there are non jars. */
486    public static List<String> getJarsForLanguage(String languageName) throws IllegalActionException {
487                
488        final List<ConfigurationProperty> languageList = ConfigurationManager
489                .getInstance()
490                .getProperty(ConfigurationManager.getModule("ddp-common"))
491                .getProperties("ExecutionLanguages.Language");
492        
493        for(ConfigurationProperty property : languageList) {
494            ConfigurationProperty nameProperty = property.getProperty("Name");
495            if(nameProperty == null) {
496                throw new IllegalActionException(
497                        "Missing name for execution language in configuration file.");
498            }
499            if(nameProperty.getValue().equals(languageName)) {
500                ConfigurationProperty jarsProperty = property.getProperty("Jars");
501                if(jarsProperty != null) {
502                    String jarsStr = jarsProperty.getValue();
503                    if(jarsStr != null) {
504                        String[] jars = jarsStr.split(",");
505                        return Arrays.asList(jars);
506                    }
507                }
508            }
509        }
510        
511        return new LinkedList<String>();
512        
513    }
514    
515    /** Get the script engine name for a language. Returns null if the language
516     *  is not found in the configuration, or is not supported by a script engine.
517     */
518    public static String getScriptEngineName(String languageName) throws IllegalActionException {
519        
520        final List<ConfigurationProperty> languageList = ConfigurationManager
521                .getInstance()
522                .getProperty(ConfigurationManager.getModule("ddp-common"))
523                .getProperties("ExecutionLanguages.Language");
524        
525        for(ConfigurationProperty property : languageList) {
526            ConfigurationProperty nameProperty = property.getProperty("Name");
527            if(nameProperty == null) {
528                throw new IllegalActionException(
529                        "Missing name for execution language in configuration file.");
530            }
531            if(nameProperty.getValue().equals(languageName)) {
532                ConfigurationProperty typeProperty = property.getProperty("Type");
533                if(typeProperty == null) {
534                    throw new IllegalActionException(
535                            "Missing type for execution language " +
536                                    languageName + " in configuration file.");
537                }
538                if(typeProperty.getValue().equals("ScriptEngine")) {
539                    ConfigurationProperty scriptEngineNameProperty = property.getProperty("ScriptEngineName");
540                    if(scriptEngineNameProperty == null) {
541                        throw new IllegalActionException(
542                                "Missing script engine name for execution language " +
543                                        languageName + " in configuration file.");
544                    }
545                    return scriptEngineNameProperty.getValue();
546                }
547            }
548        }
549        // not found
550        return null;
551    }
552    
553    /** Get the script engine factory name for a language. Returns null if the language
554     *  is not found in the configuration, or is not supported by a script engine.
555     */
556    public static String getScriptEngineFactoryName(String languageName) throws IllegalActionException {
557        
558        final List<ConfigurationProperty> languageList = ConfigurationManager
559                .getInstance()
560                .getProperty(ConfigurationManager.getModule("ddp-common"))
561                .getProperties("ExecutionLanguages.Language");
562        
563        for(ConfigurationProperty property : languageList) {
564            ConfigurationProperty nameProperty = property.getProperty("Name");
565            if(nameProperty == null) {
566                throw new IllegalActionException(
567                        "Missing name for execution language in configuration file.");
568            }
569            if(nameProperty.getValue().equals(languageName)) {
570                ConfigurationProperty typeProperty = property.getProperty("Type");
571                if(typeProperty == null) {
572                    throw new IllegalActionException(
573                            "Missing type for execution language " +
574                                    languageName + " in configuration file.");
575                }
576                if(typeProperty.getValue().equals("ScriptEngine")) {
577                    ConfigurationProperty scriptEngineFactoryNameProperty =
578                            property.getProperty("ScriptEngineFactoryName");
579                    if(scriptEngineFactoryNameProperty == null) {
580                        throw new IllegalActionException(
581                                "Missing script engine factory name for execution language " +
582                                        languageName + " in configuration file.");
583                    }
584                    return scriptEngineFactoryNameProperty.getValue();
585                }
586            }
587        }
588        // not found
589        return null;
590    }
591    
592    /** Returns true if an environment variable is set when
593     *  executing under a resource manager such as PBS or SLURM.
594     */
595    public static boolean isExecutingUnderResourceManager() {
596        // check PBS
597        return System.getenv("PBS_JOBID") != null ||
598            // check SGE
599            System.getenv("PE_NODEFILE") != null ||
600            // check SLURM
601            System.getenv("SLURM_JOBID") != null;
602    }
603
604    public static final String CONFIGURATION_KEPLER_PARA_PREFIX = "KEPLER::parameter";
605    
606    public static final String CONFIGURATION_KEPLER_PRINT_EXE_INFO = CONFIGURATION_KEPLER_PARA_PREFIX + "::printExeSummary";
607    
608    public static final String CONFIGURATION_KEPLER_TAG = "KEPLER::tagging";
609    
610    public static final String CONFIGURATION_KEPLER_PARA_PARALLEL = "KEPLER::parameter::ParallelNumber";
611    
612    public static final String CONFIGURATION_KEPLER_DISTRIBUTED_CACHE_PATH = "KEPLER::distributedcache::path";
613    
614    public static final String CONFIGURATION_KEPLER_JOB_KEY_CLASS = "KEPLER::job::key::class";
615    
616    public static final String CONFIGURATION_KEPLER_JOB_VALUE_CLASS = "KEPLER::job::value::class";
617    
618    /** The name of the configuration parameter for the MoML of the sub-workflow. */
619    public static final String CONFIGURATION_KEPLER_MODEL = "KEPLER::model";
620    
621    /** The name of the configuration parameter for the name of the sub-workflow. */
622    public static final String CONFIGURATION_KEPLER_MODEL_NAME = "KEPLER::modelName";
623        
624    /** The name of the configuration parameter for the path of the sub-worflow MoML. */
625    public static final String CONFIGURATION_KEPLER_MODEL_PATH = "KEPLER::modelPath";
626
627    /** The name of the configuration parameter for the path of the Kepler installation. */
628    public static final String CONFIGURATION_KEPLER_MODULES_DIR = "KEPLER::modulesDir";
629    
630    /** The name of the configuration parameter for the path to redirect display actors. */
631    public static final String CONFIGURATION_KEPLER_REDIRECT_DIR = "KEPLER::redirectDir";
632    
633    /** The name of the configuration parameter to specify running in the same JVM as Kepler. */
634    public static final String CONFIGURATION_KEPLER_SAME_JVM = "KEPLER::sameJVM";
635    
636    /** The name of the configuration parameter specifying the name of a DDPDataSource actor. */
637    public static final String CONFIGURATION_KEPLER_SOURCE_ACTOR_NAME = "KEPLER::sourceActorName";
638
639    /** The name of the configuration parameter specifying the name of a DDPDataSink actor. */
640    public static final String CONFIGURATION_KEPLER_SINK_ACTOR_NAME = "KEPLER::sinkActorName";
641    
642    /** The name of the configuration parameter specifying the name of the input token. */
643    public static final String CONFIGURATION_KEPLER_INPUT_TOKEN = "Kepler::inputToken";
644    
645    /** The name of the configuration parameter specifying if the full lifecycle of the
646     *  sub-workflow should be executed for each input.
647     */
648    public static final String CONFIGURATION_KEPLER_RUN_WORKFLOW_LIFECYCLE_PER_INPUT = "Kepler::runWorkflowLifeCyclePerInput";
649  
650    /** The name of the configuration parameter specifying the code to execute in the stub. */
651    public static final String CONFIGURATION_KEPLER_STUB_CODE = "Kepler::code";
652
653    /** The name of the configuration parameter specifying the input key type of the stub. */
654    public static final String CONFIGURATION_KEPLER_INPUT_KEY_TYPE = "Kepler::inputKeyType";
655
656    /** The name of the configuration parameter specifying the input value type of the stub. */
657    public static final String CONFIGURATION_KEPLER_INPUT_VALUE_TYPE = "Kepler::inputValueType";
658
659    /** The name of the configuration parameter specifying the name of the script engine factory. */
660    public static final String CONFIGURATION_KEPLER_SCRIPT_ENGINE_FACTORY_NAME = "Kepler::scriptEngineFactoryName";   
661    
662    /** Enumeration of types of token that can be serialized to/from strings. */
663    public enum TokenType {
664        String,
665        Int,
666        Double,
667        Long,
668        Nil,
669        Record;
670        
671        public static TokenType getInstance(int value) {
672            // FIXME add error checking
673            return values()[value];
674        }
675        
676        public int getValue() {
677            return ordinal();
678        }
679    }
680
681    ///////////////////////////////////////////////////////////////////
682    ////                         private fields                    ////
683
684    /** Logging. */
685    private static final Log LOG = LogFactory.getLog(Utilities.class);
686
687}