001/* Implementation of provenance query interface for SQL v8.
002
003 Copyright (c) 2009-2010 The Regents of the University of California.
004 All rights reserved.
005 Permission is hereby granted, without written agreement and without
006 license or royalty fees, to use, copy, modify, and distribute this
007 software and its documentation for any purpose, provided that the above
008 copyright notice and the following two paragraphs appear in all copies
009 of this software.
010
011 IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
012 FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
013 ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
014 THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
015 SUCH DAMAGE.
016
017 THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
018 INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
019 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
020 PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
021 CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
022 ENHANCEMENTS, OR MODIFICATIONS.
023
024*/
025
026package org.kepler.provenance.sql;
027
028import java.sql.PreparedStatement;
029import java.sql.ResultSet;
030import java.sql.SQLException;
031import java.sql.Statement;
032import java.sql.Timestamp;
033import java.util.ArrayList;
034import java.util.Arrays;
035import java.util.Date;
036import java.util.HashMap;
037import java.util.Iterator;
038import java.util.LinkedHashMap;
039import java.util.LinkedList;
040import java.util.List;
041import java.util.ListIterator;
042import java.util.Map;
043import java.util.regex.Matcher;
044
045import org.jdom.Document;
046import org.jdom.Element;
047import org.jdom.input.DOMBuilder;
048import org.jdom.output.XMLOutputter;
049import org.kepler.moml.NamedObjId;
050import org.kepler.objectmanager.lsid.KeplerLSID;
051import org.kepler.provenance.DefaultQuery;
052import org.kepler.provenance.QueryException;
053import org.kepler.sms.NamedOntClass;
054import org.kepler.util.RenameUtil;
055import org.kepler.util.WorkflowRun;
056import org.kepler.util.sql.DatabaseFactory;
057import org.kepler.util.sql.DatabaseType;
058import org.kepler.util.sql.Schema;
059
060import ptolemy.data.expr.XMLParser;
061
062/**
063 * Implementation of query interface for SQL v8.
064 *
065 * @author Daniel Crawl, Derik Barseghian, Ben Leinfelder
066 * @version $Id: SQLQueryV8.java 34619 2017-08-23 20:27:50Z crawl $
067 *
068 */
069public class SQLQueryV8 extends DefaultQuery
070{        
071    /** Construct a new SQLQueryV8 and connect to a database. */
072    public SQLQueryV8(Map<String,String> parameters) throws QueryException
073    {
074        try
075        { 
076            _dbType = DatabaseFactory.getConnectedDatabaseType(parameters, "provenance");
077        }
078        catch(SQLException e)
079        {
080            e.printStackTrace();
081            throw new QueryException("Unable to connect or initialize " +
082                "database connection: ", e);
083        }
084
085        _init();       
086    }
087
088    /** Disconnect from provenance store. */
089    @Override
090    public void disconnect() throws QueryException
091    {
092        if(_dbType != null)
093        {
094            try
095            {
096                _dbType.disconnect(); 
097            }
098            catch(SQLException e)
099            {
100                throw new QueryException("Unable to disconnect: ", e);
101            }
102    
103            _dbType = null;
104        }
105    }
106
107    /** Get a list of workflow names. */
108    @Override
109    public List<String> getWorkflows() throws QueryException
110    {
111        try
112        {
113            List<String> retval = new LinkedList<String>();
114            
115            ResultSet result = null;
116            try
117            {
118                result = _psWorkflows.executeQuery();
119                while(result.next())
120                {
121                    retval.add(result.getString("name")); 
122                }
123                return retval;
124            }
125            finally
126            {
127                if(result != null)
128                {
129                    result.close();
130                }
131            }
132        }
133        catch(SQLException e)
134        {
135            throw new QueryException("Unable to query workflow names: ", e);
136        }
137    }
138    
139    /** Get a workflow name. 
140     *
141     * @param lsid the workflow's LSID.
142     * @return the workflow name if found, otherwise null.
143     */
144    @Override
145    public String getWorkflowName(KeplerLSID lsid) throws QueryException
146    {
147        String retval = null;
148        
149        try
150        {
151            _psWorkflowNameForLSID.setString(1, lsid.toStringWithoutRevision());
152            ResultSet result = null;
153            try
154            {
155                result = _psWorkflowNameForLSID.executeQuery();
156                if(result.next())
157                {
158                    retval = result.getString(1);
159                }
160            }
161            finally
162            {
163                if(result != null)
164                {
165                    result.close();
166                }
167            }
168        }
169        catch(SQLException e)
170        {
171            throw new QueryException("Error querying workflow name: ", e);
172        }
173        
174        return retval;
175    }
176
177    /** Get a workflow name for an execution LSID.
178     *  @param lsid the execution LSID.
179     *  @return the workflow name if found, otherwise null.
180     */
181    @Override
182    public String getWorkflowNameForExecution(KeplerLSID lsid) throws QueryException {
183        
184        synchronized(_psWorkflowNameForExecutionLSID) {
185            try {
186                _psWorkflowNameForExecutionLSID.setString(1, lsid.toString());
187                try(ResultSet result = _psWorkflowNameForExecutionLSID.executeQuery();) {
188                    if(result.next()) {
189                        String retval = result.getString(1);
190                        if(result.next()) {
191                            System.err.println("WARNING: more than one workflow execution with LSID " + lsid);
192                        }
193                        return retval;
194                    }
195                }
196            }
197            catch(SQLException e) {
198                throw new QueryException("Error querying workflow name: ", e);
199            }
200        }
201        return null;
202    }
203    
204    /** Get changes within a timespan. */
205    public List<Integer> getChangesForTimespan(Date start, Date end)
206        throws QueryException 
207    {
208        return _getChangesForTimespan(start, end, null, null);
209    }
210
211    /** Get a list of all executions. */
212    @Override
213    public List<Integer> getExecutions() throws QueryException
214    {
215        try
216        {
217            return _getIntResults(_psExecutions, 1);
218        }
219        catch(SQLException e)
220        {
221            throw new QueryException("Unable to query executions: ", e);
222        }
223    }
224    
225    /** Get a list of all execution LSIDs. */
226    @Override
227    public List<KeplerLSID> getExecutionLSIDs() throws QueryException {
228        List<KeplerLSID> execLsids = new ArrayList<KeplerLSID>();
229        try
230        {
231            synchronized(_psExecutionLSIDs)
232            {
233                ResultSet result = null;
234                try
235                {
236                    result = _psExecutionLSIDs.executeQuery();
237                    while (result.next())
238                    {
239                        try {
240                            KeplerLSID execLSID = new KeplerLSID(result.getString("lsid"));
241                            if (!execLsids.contains(execLSID)){
242                                execLsids.add(execLSID);
243                            }
244                        } catch (Exception e) {
245                            // TODO Auto-generated catch block
246                            e.printStackTrace();
247                        }
248                    }
249                }
250                finally
251                {
252                    if(result != null)
253                    {
254                        result.close();
255                    }
256                }
257            }
258        }
259        catch(SQLException e)
260        {
261            throw new QueryException("Unable to query for execution LSIDs");
262        }
263        return execLsids;
264    }
265
266    /** Get a list of executions for workflow(s) with tags that contain tagsSearchString 
267     * (case insensitive)
268     */
269    @Override
270    public List<Integer> getExecutionIdsForTags(String tagsSearchString) throws QueryException {
271        List<Integer> execIds = new ArrayList<Integer>();
272        try
273        {
274            synchronized(_psExecutionIdsForTags)
275            {
276                tagsSearchString = tagsSearchString.toLowerCase();
277                _psExecutionIdsForTags.setString(1, "%" + tagsSearchString + "%");
278                ResultSet result = null;
279                try
280                {
281                    result = _psExecutionIdsForTags.executeQuery();
282                    while (result.next())
283                    {
284                        try {
285                            int execId = result.getInt("wf_exec_id");
286                            if (!execIds.contains(execId)){
287                                execIds.add(execId);
288                            }
289                        } catch (Exception e) {
290                            // TODO Auto-generated catch block
291                            e.printStackTrace();
292                        }
293                    }
294                }
295                finally
296                {
297                    if(result != null)
298                    {
299                        result.close();
300                    }
301                }
302            }
303        }
304        catch(SQLException e)
305        {
306            throw new QueryException("Unable to query for execution ids for Tags:"+tagsSearchString);
307        }
308        return execIds;
309    }    
310    
311    @Override
312    public Map<NamedOntClass, String> getTagClassesForExecutionId(int execId) throws QueryException {
313        Map<NamedOntClass, String> tags = new HashMap<NamedOntClass, String>();
314        try
315        {
316            synchronized(_psTagURNsForExecutionId)
317            {
318                _psTagURNsForExecutionId.setInt(1, execId);
319                ResultSet result = null;
320                try
321                {
322                    result = _psTagURNsForExecutionId.executeQuery();
323                    while (result.next())
324                    {
325                        try {
326                            String type = result.getString("type"); //FIXME hardcoded col names
327                            boolean isRemovable = (WorkflowRun.TAG_TYPE_RUN).equals(type);
328                            String tagURI = result.getString("urn");
329    //                        String ontologyURI = result.getString("ontology_uri");
330                            NamedOntClass cls = NamedOntClass.createNamedOntClassFromURI(tagURI);
331                            cls.setRemovable(isRemovable);
332                            if (!tags.containsKey(cls)) {
333                                tags.put(cls, type);
334                            }
335                        } catch (Exception e) {
336                            // TODO Auto-generated catch block
337                            e.printStackTrace();
338                        }
339                    }
340                }
341                finally
342                {
343                    if(result != null)
344                    {
345                        result.close();
346                    }
347                }
348            }
349        }
350        catch(SQLException e)
351        {
352            throw new QueryException("Unable to query for tags for execution id:"+execId);
353        }
354        return tags;
355    }
356    
357    /** Get the type of a tag, null if none */
358    @Override
359    public String getTypeForTag(String conceptId) {
360        String type = null;
361
362        try{
363            synchronized(_psTypeForTag)
364            {
365                _psTypeForTag.setString(1, conceptId);
366                ResultSet result = null;
367                try
368                {
369                    result = _psTypeForTag.executeQuery();
370                    while (result.next())
371                    {
372                        type = result.getString("type");
373                    }
374                }
375                finally
376                {
377                    if(result != null)
378                    {
379                        result.close();
380                    }
381                }
382            }
383        } catch (SQLException e) {
384            // TODO Auto-generated catch block
385            e.printStackTrace();
386        }
387
388        return type;
389    }
390
391
392    @Override
393    public List<String> getTagsForExecutionId(int execId) throws QueryException {
394        List<String> tags = new ArrayList<String>();
395        try
396        {
397            synchronized(_psTagsForExecutionId)
398            {
399                _psTagsForExecutionId.setInt(1, execId);
400                ResultSet result = null;
401                try
402                {
403                    result = _psTagsForExecutionId.executeQuery();
404                    while (result.next())
405                    {
406                        try {
407                            if (!tags.contains(result.getString("searchstring"))){
408                                tags.add(result.getString("searchstring"));
409                            }
410                        } catch (Exception e) {
411                            // TODO Auto-generated catch block
412                            e.printStackTrace();
413                        }
414                    }
415                }
416                finally
417                {
418                    if(result != null)
419                    {
420                        result.close();
421                    }
422                }
423            }
424        }
425        catch(SQLException e)
426        {
427            throw new QueryException("Unable to query for tags for execution id:"+execId);
428        }
429        return tags;
430    }
431
432    public List<String> getTagURNsForExecutionLSID(KeplerLSID execLSID) throws QueryException {
433        List<String> tags = new ArrayList<String>();
434        try
435        {
436            synchronized(_psTagURNsForExecutionId)
437            {
438                _psTagURNsForExecutionId.setString(1, execLSID.toString());
439                ResultSet result = null;
440                try
441                {
442                    result = _psTagURNsForExecutionId.executeQuery();
443                    while (result.next())
444                    {
445                        try {
446                            if (!tags.contains(result.getString("urn"))) {
447                                tags.add(result.getString("urn"));
448                            }
449                        } catch (Exception e) {
450                            // TODO Auto-generated catch block
451                            e.printStackTrace();
452                        }
453                    }
454                }
455                finally
456                {
457                    if(result != null)
458                    {
459                        result.close();
460                    }
461                }
462            }
463        }
464        catch(SQLException e)
465        {
466            throw new QueryException("Unable to query for tags for execution LSID:"+execLSID.toString());
467        }
468        return tags;
469    }
470    
471    /** Get workflow MoML for an execution, using execution id */
472    @Override
473    public String getMoMLForExecution(int execId) throws QueryException
474    {
475        try 
476        {
477            synchronized(_psMomlForExecutionId)
478            {
479                _psMomlForExecutionId.setInt(1, execId);
480                ResultSet result = null;
481                
482                try
483                {
484                    result = _psMomlForExecutionId.executeQuery();
485    
486                    if(!result.next())
487                    {
488                        throw new QueryException("No MoML found for execution " +
489                            execId); 
490                    }
491    
492                    byte[] data = result.getBytes(1);
493                    return _changeUnnamedWorkflow(new String(data));
494                }
495                finally
496                {
497                    if(result != null)
498                    {
499                        result.close();
500                    }
501                }
502            }
503        }
504        catch(SQLException e)
505        {
506            throw new QueryException("Unable to query for moml: ", e);
507        }
508    }
509    
510    /** Get workflow MoML for an execution, using execution lsid. */
511    @Override
512    public String getMoMLForExecution(KeplerLSID lsid) throws QueryException
513    {
514        try 
515        {
516            synchronized(_psMomlForExecutionLSID)
517            {
518                _psMomlForExecutionLSID.setString(1, lsid.toString());
519                ResultSet result = null;
520                
521                try
522                {
523                    result = _psMomlForExecutionLSID.executeQuery();
524    
525                    if(!result.next())
526                    {
527                        throw new QueryException("No Workflow MoML found for Run with KeplerLSID " +
528                            lsid.toString()); 
529                    }
530    
531                    byte[] data = result.getBytes(1);
532                    return _changeUnnamedWorkflow(new String(data));
533                }
534                finally
535                {
536                    if(result != null)
537                    {
538                        result.close();
539                    }
540                }
541            }
542        }
543        catch(SQLException e)
544        {
545            throw new QueryException("Unable to query for moml associated with run lsid: ", e);
546        }
547    }
548
549    //TODO revisit when putting runs in diff dirs, where each dir might
550    // want a separate copy of the same wf.
551    /** Get workflow MoMLs for a list of execution lsids. Only returns one copy 
552     *  of any duplicates.
553     */    
554    @Override
555    public ArrayList<String> getMoMLForExecutionLSIDs(List<KeplerLSID> lsids)
556    {
557        //Note that we don't return duplicates (i.e. e.g. 2 runs using 1 wf)
558        ListIterator<KeplerLSID> i = lsids.listIterator();
559        ArrayList<String> momls = new ArrayList<String>();
560        while (i.hasNext()) 
561        {
562            KeplerLSID j = i.next();
563            try
564            {
565                String moml = getMoMLForExecution(j);
566                if (!momls.contains(moml))
567                {
568                    momls.add(moml);
569                }
570                
571            }
572            catch(QueryException qe)
573            {
574                System.out.println("ERROR trying to getMoMLForExecution("+j+"): "+qe);
575            }
576        }
577        return momls;
578    }
579    
580
581    /** Get executions within a timespan. */
582    @Override
583    public List<Integer> getExecutionsForTimespan(Date start, Date end)
584        throws QueryException 
585    {
586        return _getExecutionsForTimespan(start, end, null, null);
587    }
588
589        
590    /**
591     *  Get WorkflowRuns for a list of execution LSIDs
592     */
593    @Override
594    public Map<KeplerLSID, WorkflowRun> getWorkflowRunsForExecutionLSIDs(List<KeplerLSID> executionLSIDs)
595        throws QueryException 
596    {
597           //FIXME: hardcoded references to row names
598                
599        Statement statement = null;
600        try
601        {
602            Map<KeplerLSID, WorkflowRun> workflowRuns = new LinkedHashMap<KeplerLSID, WorkflowRun>();
603        
604            if(executionLSIDs.isEmpty()) {
605                return workflowRuns;
606            }
607
608            // __psWorkflowRunsForExecutionLSIDs.setArray(1, execIds);
609            // Note1: Manually setting up this query since java.sql.Array is not designed for this. 
610            // To solve such issues it's suggested setting up prepared statement with more ?'s 
611            // than you think you'll ever need, set them all null, then set as many as you need. 
612            // Not sure if that would really be better, so I'm not doing it currently.
613            //        Could also instead just iterate across a number queries, but that
614            //        seems less efficient.
615            StringBuilder execLSIDs = new StringBuilder();
616            for (Iterator<KeplerLSID> i=executionLSIDs.iterator(); i.hasNext(); )
617            {
618                execLSIDs.append("'"+i.next().toString()+"'");
619                if(i.hasNext())
620                {
621                       execLSIDs.append(",");
622                }
623            }
624            String workflowStr = _dbType.getTableName("workflow");
625            String wfExecStr = _dbType.getTableName("workflow_exec");
626            String errorStr = _dbType.getTableName("error");
627
628            // NOTE: user is a reserved column name in some RDBMS.
629            String userColumnStr = _dbType.getColumnName("user");
630
631            String queryStr = 
632                "SELECT wf.name as wf_name, wfexec.wf_full_lsid as wf_lsid," +
633                    "wfexec." + userColumnStr + " as user, " + 
634                    "wfexec.id as wfexec_id, wfexec.start_time, " +
635                    "wfexec.end_time, wfexec.lsid as exec_lsid, wfexec.host_id as host_id, " +
636                    "wfexec.annotation as annotation, wfexec.derived_from as derived_from, " +
637                    "wfexec.module_dependencies as module_dependencies, " +
638                    "wfexec.type as type, " +
639                    "err.id as error_id, err.message as error_message " +
640                "FROM " + workflowStr + " wf, " + wfExecStr + " wfexec " +
641                "LEFT JOIN " + errorStr + " err ON wfexec.id=err.exec_id " +
642                "WHERE wf.id=wfexec.wf_id AND wfexec.lsid in (" + execLSIDs + ")";
643            
644            statement = _dbType.getStatement();
645            
646            LinkedHashMap<Integer, String> errorMsgs = new LinkedHashMap<Integer, String>();
647            
648            ResultSet result = null;
649            try
650            {
651                result = statement.executeQuery(queryStr);
652                KeplerLSID lastExecLSID = null;
653                while(result.next())
654                {
655                    KeplerLSID execLSID = null;
656                    try
657                    {
658                        execLSID = new KeplerLSID(result.getString("exec_lsid"));
659                    }
660                    catch(Exception e)
661                    {
662                        throw new QueryException("Error creating KeplerLSID for execution: ", e);
663                    }
664
665                    KeplerLSID wfLSID = null;
666                    try
667                    {
668                        wfLSID = new KeplerLSID(result.getString("wf_lsid"));
669                    }
670                    catch(Exception e)
671                    {
672                        throw new QueryException("Error creating KeplerLSID for workflow: ", e);
673                    }
674
675                    int errorId;
676                    
677                    if (!execLSID.equals(lastExecLSID))
678                    {
679                        errorMsgs = new LinkedHashMap<Integer, String>();
680                    }
681                    
682                    if (result.getString("error_message") != null)
683                    {
684                        errorId = Integer.parseInt(result.getString("error_id"));
685                        errorMsgs.put(errorId, result.getString("error_message"));
686                    }
687
688                    Date startTime = result.getTimestamp("start_time");
689                    Date endTime = result.getTimestamp("end_time");
690                    
691                    // endTime may be null if not set
692                    if(endTime == null)
693                    {
694                        endTime = new Date(0);
695                    }
696                    
697                    Long duration = (endTime.getTime() - startTime.getTime()) / numMillisecondsInASecond;
698                    if (endTime.compareTo(startTime) < 0)
699                    {
700                        duration = -1L;
701                    }
702                    
703                    int execId = Integer.parseInt(result.getString("wfexec_id"));
704                    // Note: in the case of resultSet that has multiple rows per exec, we just overwrite the entry in
705                    // workflowRuns each time, each time with a more complete version of errorMessages. 
706                    // a cleaner soln would be to look-ahead and then use result.previous, but not all dbs support result.previous
707                    // (e.g. hsqldb)
708                    WorkflowRun run = null;                    
709                    try
710                    {
711                        run = new WorkflowRun(execLSID,
712                                result.getString("derived_from"),
713                            result.getString("user"),
714                            result.getString("wf_name"),
715                            wfLSID, startTime, duration, execId, errorMsgs, 
716                            result.getString("host_id"),
717                            result.getString("annotation"),
718                            result.getString("module_dependencies"),
719                            result.getString("type"),
720                            getTagClassesForExecutionId(execId));
721                    }
722                    catch(Exception e)
723                    {
724                        e.printStackTrace();
725                        throw new QueryException("Error creating WorkflowRun: ", e);
726                    }
727                    
728                    // note it's important to use run.getExecLSID() instead of execLSID here
729                    // since WorkflowRun constructor may change the execLSID revision.
730                    workflowRuns.put(run.getExecLSID(), run);
731                    
732                    lastExecLSID = execLSID;
733                }
734            } 
735            finally
736            {
737                if(result != null)
738                {
739                    result.close();
740                }
741            }
742       
743            return workflowRuns;
744        }
745        catch(SQLException e)
746        {
747            e.printStackTrace();
748            throw new QueryException("Unable to query workflow runs: ", e);
749        }
750        finally
751        {
752            if(statement != null)
753            {
754                try
755                {
756                    statement.close();
757                } 
758                catch (SQLException e)
759                {
760                    throw new QueryException("Unable to close statement: ", e);
761                }
762            }
763        }
764    }
765    
766    @Override
767    public List<WorkflowRun> getWorkflowRunsForUser(String user) throws QueryException {
768
769        List<WorkflowRun> workflowRuns = new LinkedList<WorkflowRun>();
770
771        String workflowStr = _dbType.getTableName("workflow");
772        String wfExecStr = _dbType.getTableName("workflow_exec");
773        String errorStr = _dbType.getTableName("error");
774
775        // NOTE: user is a reserved column name in some RDBMS.
776        String userColumnStr = _dbType.getColumnName("user");
777
778        String queryStr = 
779                "SELECT wf.name as wf_name, wfexec.wf_full_lsid as wf_lsid," +
780                        "wfexec." + userColumnStr + " as user, " + 
781                        "wfexec.id as wfexec_id, wfexec.start_time, " +
782                        "wfexec.end_time, wfexec.lsid as exec_lsid, wfexec.host_id as host_id, " +
783                        "wfexec.annotation as annotation, wfexec.derived_from as derived_from, " +
784                        "wfexec.module_dependencies as module_dependencies, " +
785                        "wfexec.type as type, " +
786                        "err.id as error_id, err.message as error_message " +
787                        "FROM " + workflowStr + " wf, " + wfExecStr + " wfexec " +
788                        "LEFT JOIN " + errorStr + " err ON wfexec.id=err.exec_id " +
789                        "WHERE wf.id=wfexec.wf_id AND wfexec.user = ?";
790
791        try(PreparedStatement statement = _dbType.getPrepStatement(queryStr))
792        {
793            statement.setString(1, user);
794            
795            LinkedHashMap<Integer, String> errorMsgs = new LinkedHashMap<Integer, String>();
796
797            try(ResultSet result = statement.executeQuery())
798            {
799                KeplerLSID lastExecLSID = null;
800                while(result.next())
801                {
802                    KeplerLSID execLSID = null;
803                    try
804                    {
805                        execLSID = new KeplerLSID(result.getString("exec_lsid"));
806                    }
807                    catch(Exception e)
808                    {
809                        throw new QueryException("Error creating KeplerLSID for execution: ", e);
810                    }
811
812                    KeplerLSID wfLSID = null;
813                    try
814                    {
815                        wfLSID = new KeplerLSID(result.getString("wf_lsid"));
816                    }
817                    catch(Exception e)
818                    {
819                        throw new QueryException("Error creating KeplerLSID for workflow: ", e);
820                    }
821
822                    int errorId;
823
824                    if (!execLSID.equals(lastExecLSID))
825                    {
826                        errorMsgs = new LinkedHashMap<Integer, String>();
827                    }
828
829                    if (result.getString("error_message") != null)
830                    {
831                        errorId = Integer.parseInt(result.getString("error_id"));
832                        errorMsgs.put(errorId, result.getString("error_message"));
833                    }
834
835                    Date startTime = result.getTimestamp("start_time");
836                    Date endTime = result.getTimestamp("end_time");
837
838                    // endTime may be null if not set
839                    if(endTime == null)
840                    {
841                        endTime = new Date(0);
842                    }
843
844                    Long duration = (endTime.getTime() - startTime.getTime()) / numMillisecondsInASecond;
845                    if (endTime.compareTo(startTime) < 0)
846                    {
847                        duration = -1L;
848                    }
849
850                    int execId = Integer.parseInt(result.getString("wfexec_id"));
851                    // Note: in the case of resultSet that has multiple rows per exec, we just overwrite the entry in
852                    // workflowRuns each time, each time with a more complete version of errorMessages. 
853                    // a cleaner soln would be to look-ahead and then use result.previous, but not all dbs support result.previous
854                    // (e.g. hsqldb)
855                    WorkflowRun run = null;                    
856                    try
857                    {
858                        run = new WorkflowRun(execLSID,
859                                result.getString("derived_from"),
860                                result.getString("user"),
861                                result.getString("wf_name"),
862                                wfLSID, startTime, duration, execId, errorMsgs, 
863                                result.getString("host_id"),
864                                result.getString("annotation"),
865                                result.getString("module_dependencies"),
866                                result.getString("type"),
867                                getTagClassesForExecutionId(execId));
868                    }
869                    catch(Exception e)
870                    {
871                        e.printStackTrace();
872                        throw new QueryException("Error creating WorkflowRun: ", e);
873                    }
874
875                    // note it's important to use run.getExecLSID() instead of execLSID here
876                    // since WorkflowRun constructor may change the execLSID revision.
877                    workflowRuns.add(run);
878
879                    lastExecLSID = execLSID;
880                }
881            } 
882
883            return workflowRuns;
884        }
885        catch(SQLException e)
886        {
887            e.printStackTrace();
888            throw new QueryException("Unable to query workflow runs: ", e);
889        }
890    }
891    
892    /** Get a list of executions for a specific workflow. */
893    @Override
894    public List<Integer> getExecutionsForWorkflow(String workflow)
895        throws QueryException
896    {
897        try
898        {
899            _psExecutionsForWorkflow.setString(1, workflow);
900            return _getIntResults(_psExecutionsForWorkflow, 1);            
901        }
902        catch(SQLException e)
903        {
904            throw new QueryException("Unable to query executions for " +
905                "workflow " + workflow + ": ", e);
906        }
907    }
908    
909    /** Get the execution ids for a specific exec LSID. If no execution
910     *  exists, returns null.
911     */
912    @Override
913    public Integer getExecutionForExecutionLSID(KeplerLSID execLSID)
914        throws QueryException
915    {
916        try
917        {
918            _psExecutionForExecutionLSID.setString(1, execLSID.toString());
919            ResultSet result = null;
920            try
921            {
922                int retval;
923                result = _psExecutionForExecutionLSID.executeQuery();
924                if(! result.next() || (retval = result.getInt(1)) == 0)
925                {
926                    return null;
927                }
928                return retval;
929            }
930            finally
931            {
932                if(result != null)
933                {
934                    result.close();
935                }
936            }
937        }
938        catch(SQLException e)
939        {
940            throw new QueryException("Unable to query executions for " +
941                "run " + execLSID + ": ", e);
942        }
943    }
944    
945    /** Get execution id for an execution LSID without revision. 
946     *  If no execution exists, returns null.
947     */
948    @Override
949    public Integer getExecutionForExecutionLSIDWithoutRevision(
950                        String execLSIDWithoutRevision) throws QueryException {
951        try
952        {
953            _psExecutionForExecutionLSIDWithoutRevision.setString(1, execLSIDWithoutRevision+":%");
954            ResultSet result = null;
955            try
956            {
957                int retval;
958                result = _psExecutionForExecutionLSIDWithoutRevision.executeQuery();
959                if(! result.next() || (retval = result.getInt(1)) == 0)
960                {
961                    return null;
962                }
963                return retval;
964            }
965            finally
966            {
967                if(result != null)
968                {
969                    result.close();
970                }
971            }
972        }
973        catch(SQLException e)
974        {
975            throw new QueryException("Unable to query executions for " +
976                "run " + execLSIDWithoutRevision + ": ", e);
977        }
978        }
979    
980    /** Get execution id for execution that has the given LSID as its
981         *      oldest referral. 
982         */
983    @Override
984    public Integer getExecutionForOldestReferralExecutionLSIDWithoutRevision(
985                String execLSIDWithoutRevision) throws QueryException 
986    {
987        try
988        {
989        
990                // Even though what we really want to match on is
991                // the last and this could potentially match on one 
992                // in the middle, that state is currently possible, so this regex should be ok:
993            _psExecutionForOldestReferralExecutionLSIDWithoutRevision.setString(1, "%"+execLSIDWithoutRevision+":%");
994            ResultSet result = null;
995            try
996            {
997                int retval;
998                result = _psExecutionForOldestReferralExecutionLSIDWithoutRevision.executeQuery();
999                if(! result.next() || (retval = result.getInt(1)) == 0)
1000                {
1001                    return null;
1002                }
1003                return retval;
1004            }
1005            finally
1006            {
1007                if(result != null)
1008                {
1009                    result.close();
1010                }
1011            }
1012        }
1013        catch(SQLException e)
1014        {
1015            throw new QueryException("Unable to query executions for " +
1016                "run " + execLSIDWithoutRevision + ": ", e);
1017        }
1018    }
1019    
1020    /** Get the execution LSID for a specific execution id. If no execution
1021     *  exists, returns null.
1022     */
1023    @Override
1024    public KeplerLSID getExecutionLSIDForExecution(Integer execId)
1025        throws QueryException 
1026    {
1027        synchronized(_psExecutionLSIDForExecution) {
1028            try
1029            {
1030                _psExecutionLSIDForExecution.setInt(1, execId);
1031                try(ResultSet result = _psExecutionLSIDForExecution.executeQuery();)
1032                {
1033                    if(!result.next())
1034                    {
1035                        return null;
1036                    }
1037                    KeplerLSID executionLSID = new KeplerLSID(result.getString("lsid"));
1038                    return executionLSID;
1039                } catch (Exception e) {
1040                    // TODO Auto-generated catch block
1041                    e.printStackTrace();
1042                }
1043            }
1044            catch(SQLException e)
1045            {
1046                throw new QueryException("Unable to query execution lsids for " +
1047                    "execution id " + execId + ": ", e);
1048            }
1049        }
1050        return null;
1051    }
1052    
1053    /** Get a list of executions for workflow(s) with names that contain text (case insensitive). */
1054    public List<Integer> getExecutionsForWorkflowNameContains(String text)
1055        throws QueryException
1056    {
1057        try
1058        {
1059            text = text.toLowerCase();
1060            _psExecutionsForWorkflowNameContains.setString(1, "%" + text + "%");
1061            return _getIntResults(_psExecutionsForWorkflowNameContains, 1);
1062        }
1063        catch(SQLException e)
1064        {
1065            throw new QueryException("Unable to query executions for " +
1066                "workflows with names that contain: " + text + ": ", e);
1067        }
1068    }
1069    
1070    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria.*/
1071    @Override
1072    public List<Integer> getExecutionsForWorkflowRuns(String workflowName, String userName)
1073        throws QueryException
1074    {
1075        try
1076        {
1077            workflowName = workflowName.toLowerCase();
1078            _psExecutionsForWorkflowRuns.setString(1, "%" + workflowName + "%");
1079            userName = userName.toLowerCase();
1080            _psExecutionsForWorkflowRuns.setString(2, "%" + userName + "%");
1081            return _getIntResults(_psExecutionsForWorkflowRuns, 1);
1082        }
1083        catch(SQLException e)
1084        {
1085            throw new QueryException("Unable to query executions for " +
1086                "workflows with names that contain: " + workflowName + 
1087                ", usernames that contain: + "+ userName + ": ", e);
1088        }
1089    }
1090    
1091    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria.*/
1092    @Override
1093    public List<Integer> getExecutionsForWorkflowRuns(String workflowName,
1094        String userName, Date after, Date before, int execIdAfter,
1095        int execIdBefore)
1096        throws QueryException
1097    {
1098        try
1099        {
1100            workflowName = workflowName.toLowerCase();
1101            _psExecutionsForWorkflowRuns2.setString(1, "%" + workflowName + "%");
1102            userName = userName.toLowerCase();
1103            _psExecutionsForWorkflowRuns2.setString(2, "%" + userName + "%");
1104            _psExecutionsForWorkflowRuns2.setTimestamp(3,
1105                new Timestamp(after.getTime()));
1106            _psExecutionsForWorkflowRuns2.setTimestamp(4,
1107                new Timestamp(before.getTime()));
1108            _psExecutionsForWorkflowRuns2.setInt(5, execIdAfter);
1109            _psExecutionsForWorkflowRuns2.setInt(6, execIdBefore);
1110
1111            return _getIntResults(_psExecutionsForWorkflowRuns2, 1);
1112        }
1113        catch(SQLException e)
1114        {
1115            throw new QueryException("Unable to query executions for " +
1116                "workflows with names that contain: " + workflowName + 
1117                ", usernames that contain: + "+ userName + 
1118                ", between " + after + " and "+ before + 
1119                ", with execId between "+execIdAfter + " and " + execIdBefore+": ", e);
1120        }
1121    }
1122    
1123    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria.*/
1124    @Override
1125    public List<KeplerLSID> getExecutionLSIDsForWorkflowRuns(
1126            String workflowName, String userName, Date after, Date before,
1127            int execIdAfter, int execIdBefore) throws QueryException {
1128        List<KeplerLSID> execLSIDs = new ArrayList<KeplerLSID>();
1129        
1130        try{
1131            workflowName = workflowName.toLowerCase();
1132            _psExecutionLSIDsForWorkflowRuns.setString(1, "%" + workflowName + "%");
1133            userName = userName.toLowerCase();
1134            _psExecutionLSIDsForWorkflowRuns.setString(2, "%" + userName + "%");
1135            _psExecutionLSIDsForWorkflowRuns.setTimestamp(3,
1136                new Timestamp(after.getTime()));
1137            _psExecutionLSIDsForWorkflowRuns.setTimestamp(4,
1138                new Timestamp(before.getTime()));
1139            _psExecutionLSIDsForWorkflowRuns.setInt(5, execIdAfter);
1140            _psExecutionLSIDsForWorkflowRuns.setInt(6, execIdBefore);
1141            
1142            ResultSet result = null;
1143            try
1144            {
1145                result = _psExecutionLSIDsForWorkflowRuns.executeQuery();
1146                while (result.next())
1147                {
1148                    try {
1149                        KeplerLSID execLSID = new KeplerLSID(result.getString("lsid"));
1150                        if (!execLSIDs.contains(execLSID)){
1151                            execLSIDs.add(execLSID);
1152                        }
1153                    } catch (Exception e) {
1154                        e.printStackTrace();
1155                    }
1156                }
1157            }
1158            finally
1159            {
1160                if(result != null)
1161                {
1162                    result.close();
1163                }
1164            }
1165        }
1166        catch(SQLException e)
1167        {
1168            throw new QueryException("Unable to query executions for " +
1169                    "workflows with names that contain: " + workflowName + 
1170                    ", usernames that contain: + "+ userName + 
1171                    ", between " + after + " and "+ before + 
1172                    ", with execId between "+execIdAfter + " and " + execIdBefore+": ", e);
1173        }
1174        
1175        return execLSIDs;
1176    }
1177    
1178    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria 
1179     *  before a date.
1180     *  */
1181    @Override
1182    public List<Integer> getExecutionsForWorkflowRunsBefore(String workflowName, String userName, Date before)
1183        throws QueryException
1184    {
1185        try
1186        {
1187            workflowName = workflowName.toLowerCase();
1188            _psExecutionsForWorkflowRunsBefore.setString(1, "%" + workflowName + "%");
1189            userName = userName.toLowerCase();
1190            _psExecutionsForWorkflowRunsBefore.setString(2, "%" + userName + "%");
1191            _psExecutionsForWorkflowRunsBefore.setTimestamp(3,
1192                new Timestamp(before.getTime()));
1193            return _getIntResults(_psExecutionsForWorkflowRunsBefore, 1);
1194        }
1195        catch(SQLException e)
1196        {
1197            throw new QueryException("Unable to query executions for " +
1198                    "workflows with names that contain: " + workflowName + 
1199                    ", usernames that contain: + "+ userName + 
1200                    ", before " + before + ": ", e);
1201        }
1202    }
1203    
1204    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria 
1205     *  after a date.
1206     *  */
1207    @Override
1208    public List<Integer> getExecutionsForWorkflowRunsAfter(String workflowName, String userName, Date after)
1209        throws QueryException
1210    {
1211        try
1212        {
1213            workflowName = workflowName.toLowerCase();
1214            _psExecutionsForWorkflowRunsAfter.setString(1, "%" + workflowName + "%");
1215            userName = userName.toLowerCase();
1216            _psExecutionsForWorkflowRunsAfter.setString(2, "%" + userName + "%");
1217            _psExecutionsForWorkflowRunsAfter.setTimestamp(3,
1218                new Timestamp(after.getTime()));
1219            return _getIntResults(_psExecutionsForWorkflowRunsAfter, 1);
1220        }
1221        catch(SQLException e)
1222        {
1223            throw new QueryException("Unable to query executions for " +
1224                    "workflows with names that contain: " + workflowName + 
1225                    ", usernames that contain: + "+ userName + 
1226                    ", after " + after + ": ", e);
1227        }
1228    }
1229    
1230    /** Get a list of executions for workflow(s) that match Workflow Run Manager (WRM) search criteria 
1231     *  "at" a date. We treat this date as a string so we can match using LIKE 'date%'.
1232     *  */
1233    public List<Integer> getExecutionsForWorkflowRunsAt(String workflowName, String userName, String date)
1234        throws QueryException
1235    {
1236        try
1237        {
1238            workflowName = workflowName.toLowerCase();
1239            _psExecutionsForWorkflowRunsAt.setString(1, "%" + workflowName + "%");
1240            userName = userName.toLowerCase();
1241            _psExecutionsForWorkflowRunsAt.setString(2, "%" + userName + "%");
1242            _psExecutionsForWorkflowRunsAt.setString(3, date + "%"); //TODO fix this
1243            return _getIntResults(_psExecutionsForWorkflowRunsAt, 1);
1244        }
1245        catch(SQLException e)
1246        {
1247            throw new QueryException("Unable to query executions for " +
1248                    "workflows with names that contain: " + workflowName + 
1249                    ", usernames that contain: + "+ userName + 
1250                    ", at " + date + ": ", e);
1251        }
1252    }
1253    
1254    /** Get a list of executions for a specific execution annotation. */
1255    @Override
1256    public List<Integer> getExecutionsForAnnotation(String annotation) 
1257        throws QueryException
1258    {
1259        try
1260        {
1261            _psExecutionsForAnnotation.setString(1, annotation);
1262            return _getIntResults(_psExecutionsForAnnotation, 1);
1263        }
1264        catch(SQLException e)
1265        {
1266            throw new QueryException("Unable to query executions for " +
1267                "annotation " + annotation + ": ", e);
1268        }
1269    }
1270
1271    /** Get the start and end timestamps of an execution.
1272     *  @param execId the execution id
1273     *  @return a two-element array: the first entry is the start timestamp,
1274     *  and the second is the end timestamp. If the execution id is not found,
1275     *  returns null.
1276     */
1277    @Override
1278    public Date[] getTimestampsForExecution(int execId)
1279        throws QueryException
1280    {
1281        synchronized(_psTimestampsForExecution) {
1282            try
1283            {
1284                _psTimestampsForExecution.setInt(1, execId);
1285                try(ResultSet result = _psTimestampsForExecution.executeQuery();)
1286                {
1287                    if(result.next())
1288                    {
1289                        Date[] retval = new Date[2];
1290                        retval[0] = result.getTimestamp("start_time");
1291                        retval[1] = result.getTimestamp("end_time");
1292                        return retval;
1293                    }
1294                }
1295            }
1296            catch(SQLException e)
1297            {
1298                throw new QueryException("Error querying timestampts for execution " + execId, e);
1299            }
1300        }
1301        return null;
1302    }
1303    
1304    /** Get the last execution for a workflow. NOTE: the LSID revision 
1305     *  is ignored.
1306     *  @param lsid the workflow lsid.
1307     *  @return if workflow has been executed, the last execution id.
1308     *  otherwise, returns null.
1309     */
1310    @Override
1311    public Integer getLastExecutionForWorkflow(KeplerLSID lsid)
1312        throws QueryException
1313    {
1314        synchronized(_psLastExecutionForWorkflowLSID) {
1315            try
1316            {
1317                _psLastExecutionForWorkflowLSID.setString(1,
1318                    lsid.toStringWithoutRevision());
1319                try(ResultSet result = _psLastExecutionForWorkflowLSID.executeQuery();)
1320                {
1321                    if(!result.next()) {
1322                        return null;
1323                    }
1324                    int retval = result.getInt(1);
1325                    if(retval == 0) {
1326                        return null;
1327                    }
1328                    return Integer.valueOf(retval);
1329                }
1330            }
1331            catch(SQLException e)
1332            {
1333                throw new QueryException("Unable to query last workflow " +
1334                    "execution: ", e);
1335            }
1336        }
1337    }
1338
1339    /** Get the last execution for a workflow.
1340     *  @param workflow the workflow name
1341     *  @return if workflow has been executed, the last execution id.
1342     *  otherwise, returns null.
1343     */
1344    @Override
1345    public Integer getLastExecutionForWorkflow(String workflow)
1346        throws QueryException
1347    {
1348        try
1349        {
1350            _psLastExecutionForWorkflow.setString(1, workflow);
1351            ResultSet result = null;
1352            try
1353            {
1354                int retval;
1355                result = _psLastExecutionForWorkflow.executeQuery();
1356                if(! result.next() || (retval = result.getInt(1))== 0)
1357                {
1358                    return null;
1359                }
1360                return retval;
1361            }
1362            finally
1363            {
1364                if(result != null)
1365                {
1366                    result.close();
1367                }
1368            }
1369        }
1370        catch(SQLException e)
1371        {
1372            throw new QueryException("Unable to query last workflow " +
1373                "execution: ", e);
1374        }
1375    }
1376
1377    /** Get the last execution LSID for a workflow.
1378     *  @param workflow the workflow name.
1379     *  @return execution lsid
1380     */
1381     @Override
1382    public KeplerLSID getLastExecutionLSIDForWorkflow(String workflow)
1383        throws QueryException
1384     {
1385        try   
1386        {
1387            _psLastExecutionLSIDForWorkflowName.setString(1, workflow);
1388            ResultSet result = null;
1389            try
1390            {
1391                result = _psLastExecutionLSIDForWorkflowName.executeQuery();
1392                if(! result.next())
1393                {
1394                    throw new QueryException("No executions found for " +
1395                        "workflow " + workflow);
1396                }
1397                String lsidStr = result.getString(1);
1398                return new KeplerLSID(lsidStr);
1399            }
1400            finally
1401            {
1402                if(result != null)
1403                {
1404                    result.close();
1405                }
1406            }
1407        }
1408        catch(Exception e)
1409        {
1410            throw new QueryException("Unable to query last workflow " +
1411                "execution: ", e);
1412        }
1413     }
1414     
1415     /** Get the last execution LSID for a workflow.
1416      *  @param lsid the workflow lsid.
1417      *  @return execution lsid
1418      */
1419      @Override
1420    public KeplerLSID getLastExecutionLSIDForWorkflow(KeplerLSID lsid)
1421         throws QueryException
1422      {
1423         try   
1424         {
1425             _psLastExecutionLSIDForWorkflowLSID.setString(1, lsid.toStringWithoutRevision());
1426             ResultSet result = null;
1427             try
1428             {
1429                 result = _psLastExecutionLSIDForWorkflowLSID.executeQuery();
1430                 if(! result.next())
1431                 {
1432                     throw new QueryException("No executions found for " +
1433                         "workflow LSID " + lsid);
1434                 }
1435                 String lsidStr = result.getString(1);
1436                 return new KeplerLSID(lsidStr);
1437             }
1438             finally
1439             {
1440                 if(result != null)
1441                 {
1442                     result.close();
1443                 }
1444             }
1445         }
1446         catch(Exception e)
1447         {
1448             throw new QueryException("Unable to query last workflow " +
1449                 "execution: ", e);
1450         }
1451      }
1452
1453
1454   /** Get the error for an execution. Returns null if no error message
1455    *  was found. NOTE: returning null is not an indication if the
1456    *  execution had an error.
1457    *  @see isErrorForExecution
1458    *  @param lsid the workflow execution lsid. 
1459    */
1460    @Override
1461    public String getErrorForExecution(KeplerLSID lsid) throws QueryException
1462    {
1463        String retval = null; 
1464
1465        try
1466        {
1467            _psErrorForExecution.setString(1, lsid.toString());
1468            ResultSet result = null;
1469
1470            try
1471            {
1472                result = _psErrorForExecution.executeQuery();
1473                if(result.next())
1474                {
1475                    retval = result.getString(1); 
1476                }
1477            }
1478            finally
1479            {
1480                if(result != null)
1481                {
1482                    result.close();
1483                }
1484            }
1485        }
1486        catch(SQLException e)
1487        {
1488            throw new QueryException("Error querying workflow error: ", e);
1489        }
1490
1491        return retval;
1492    }
1493
1494    /** Returns true if the execution had an error. */
1495    @Override
1496    public boolean isErrorForExecution(KeplerLSID executionLSID) throws QueryException
1497    {
1498        try
1499        {
1500            ResultSet result = null;
1501            try
1502            {
1503                _psDoesExecutionHaveError.setString(1, executionLSID.toString());
1504                result = _psDoesExecutionHaveError.executeQuery();
1505                return result.next();
1506            }
1507            finally
1508            {
1509                if(result != null)
1510                {
1511                    result.close();
1512                }
1513            }
1514        }
1515        catch(SQLException e)
1516        {
1517            throw new QueryException("Unable to query if execution has an error: ", e);
1518        }
1519    }
1520
1521    /** See if an execution appears to be imported. */
1522    @Override
1523    public boolean isImportedExecution(KeplerLSID executionLSID) throws QueryException
1524    {
1525        try
1526        {
1527            ResultSet result = null;
1528            try
1529            {
1530                _psIsExecutionImported.setString(1, executionLSID.toString());
1531                result = _psIsExecutionImported.executeQuery();
1532                // if there are rows in the query, then the run was not
1533                // imported
1534                return (!result.next());
1535            }
1536            finally
1537            {
1538                if(result != null)
1539                {
1540                    result.close();
1541                }
1542            }
1543        }
1544        catch(SQLException e)
1545        {
1546            throw new QueryException("Unable to query if execution was imported:", e);
1547        }
1548    }
1549
1550    /** Get a list of executions corresponding to a specific type. */
1551    @Override
1552    public List<KeplerLSID> getExecutionsForType(WorkflowRun.type type) throws QueryException
1553    {
1554        ResultSet result = null;
1555        try
1556        {
1557            try
1558            {
1559                List<KeplerLSID> retval = new LinkedList<KeplerLSID>();
1560                _psExecutionsForType.setString(1, type.toString());
1561                result = _psExecutionsForType.executeQuery();
1562                while(result.next())
1563                {
1564                    retval.add(new KeplerLSID(result.getString(1)));
1565                }
1566                return retval;
1567            }
1568            finally
1569            {
1570                if(result != null)
1571                {
1572                    result.close();
1573                }
1574            }
1575        }
1576        catch(Exception e)
1577        {
1578            throw new QueryException("Unable to query executions for type: ", e);
1579        }
1580    }
1581
1582    /** Get an sequence of tokens for an execution.
1583     *  @param execId the execution id
1584     *  @param portId the port id. If null, returns the ids of
1585     *  tokens read in entire execution.
1586     *  @param last if true, the sequence starts at the last token created
1587     *  and goes backwards to the first; otherwise the sequence starts at
1588     *  the first.
1589     */
1590    @Override
1591    public List<Integer> getTokensForExecution(int execId, Integer portId, boolean last)
1592        throws QueryException
1593    {
1594        List<Integer> list = new LinkedList<Integer>();
1595    
1596        try
1597        {
1598            ResultSet result = null;
1599            try
1600            {
1601                if (portId != null)
1602                {
1603                    _psEntityTokensForExecution.setInt(1, execId);
1604                    _psEntityTokensForExecution.setInt(2, portId);
1605                    _psEntityTokensForExecution.setInt(3, execId);
1606                    _psEntityTokensForExecution.setInt(4, portId);
1607                    result = _psEntityTokensForExecution.executeQuery();
1608                }
1609                else
1610                {
1611                    _psTokensForExecution.setInt(1, execId);
1612                    result = _psTokensForExecution.executeQuery();
1613                }
1614                while(result.next())
1615                {
1616                    list.add(result.getInt(1));
1617                }
1618            }
1619            finally
1620            {
1621                if(result != null)
1622                {
1623                    result.close();
1624                }
1625            }
1626        }
1627        catch(SQLException e)
1628        {
1629            throw new QueryException("Unable to retrieve for tokens: ", e);
1630        }
1631
1632        /*
1633        if(list.size() == 0)
1634        {
1635            throw new QueryException("No tokens found for execution " + execId);
1636        }
1637        */
1638    
1639        Integer[] array = list.toArray(new Integer[0]);
1640        Arrays.sort(array);
1641    
1642        LinkedList<Integer> retval = new LinkedList<Integer>();
1643    
1644        for(int i = 0; i < array.length; i++)
1645        {
1646            if(last)
1647            {
1648                retval.addFirst(array[i]);
1649            }
1650            else
1651            {
1652                retval.addLast(array[i]);
1653            }
1654        }
1655        return retval;
1656    }
1657
1658    /** Get the firing id(s) of the actor(s) that read or wrote a token.
1659     *  @param tokenId the token id
1660     *  @param read If true, return the actor(s) firing id(s) that read
1661     *  the token. Otherwise, return the actor firing that wrote the token.
1662     *  */
1663    @Override
1664    public List<Integer> getActorFiringForToken(int tokenId, boolean read)
1665        throws QueryException
1666    {
1667        try 
1668        {
1669            List<Integer> retval;
1670
1671            if(read)
1672            {
1673                synchronized(_psActorFiringsForTokenRead)
1674                {
1675                    _psActorFiringsForTokenRead.setInt(1, tokenId);
1676                    retval = _getIntResults(_psActorFiringsForTokenRead, 1);
1677                }
1678            }
1679            else
1680            {
1681                retval = new LinkedList<Integer>();
1682
1683                synchronized(_psActorFiringForTokenWrite) 
1684                {
1685                    _psActorFiringForTokenWrite.setInt(1, tokenId);
1686                    ResultSet result = null;
1687                    
1688                    try
1689                    {
1690                        result =                         
1691                            _psActorFiringForTokenWrite.executeQuery();
1692
1693                        // make sure a firing produced this token
1694                        if(!result.next())
1695                        {
1696                            throw new QueryException("No firing produced token " +
1697                                tokenId);
1698                        }
1699                        
1700                        retval.add(result.getInt(1));
1701                    }
1702                    finally
1703                    {
1704                        if(result != null)
1705                        {
1706                            result.close();
1707                        }
1708                    }
1709                }
1710            }
1711                
1712            return retval;
1713        }
1714        catch(SQLException e)
1715        {
1716            throw new QueryException("Unable to query for actor firing: ", e);
1717        }
1718    }
1719  
1720    /** Get tokens for a firing.
1721     *  @param fireId the firing id
1722     *  @param read if true, get the tokens read; otherwise get the tokens
1723     *  written.
1724     */
1725    @Override
1726    public List<Integer> getTokensForFiring(int fireId, boolean read)
1727        throws QueryException
1728    {
1729        PreparedStatement prep;
1730        if(read)
1731        {
1732            prep = _psTokensReadForFiring;    
1733        }
1734        else
1735        {
1736            prep = _psTokensWrittenForFiring;    
1737        }
1738
1739        synchronized(prep) {
1740            try
1741            {
1742                prep.setInt(1, fireId);
1743                return _getIntResults(prep, 1);
1744            }
1745            catch(SQLException e)
1746            {
1747                throw new QueryException("Unable to query tokens for firing id: ", e);
1748            }
1749        }
1750    }
1751    
1752    /** Get the value of a token. */
1753    @Override
1754    public String getTokenValue(int tokenId) throws QueryException
1755    {
1756        String dataId = null;
1757        String tokenValue;
1758        synchronized(_psPortEventData) {
1759            try
1760            {
1761                _psPortEventData.setInt(1, tokenId);
1762                try(ResultSet result = _psPortEventData.executeQuery();)
1763                {
1764                    if(!result.next()) {
1765                        throw new QueryException("Token not found: " + tokenId);                
1766                    }
1767                    tokenValue = result.getString(1);
1768                    if(tokenValue == null) {
1769                        dataId = result.getString(2);
1770                    }
1771                }
1772            }
1773            catch(SQLException e)
1774            {
1775                throw new QueryException("Unable to query token value from port_event table: ", e);
1776            }
1777        }
1778        
1779        if(dataId == null) {
1780            if(tokenValue == null) {
1781                throw new QueryException("No value found for token " + tokenId);
1782            }
1783            return tokenValue;
1784        } else {
1785            synchronized(_psDataContents) {
1786                //System.out.println("querying from data");
1787                try {
1788                    _psDataContents.setString(1, dataId);
1789                    try(ResultSet result = _psDataContents.executeQuery();) {
1790                        if(!result.next()) {
1791                            throw new QueryException("Data md5 not found: " + dataId);
1792                        }
1793                        return new String(result.getBytes(1));
1794                    }
1795                } catch(SQLException e) {
1796                    throw new QueryException("Unable to query token value from data table: ", e);
1797                }
1798            }
1799        }
1800    }
1801
1802    /** Get the type of a token. */
1803    @Override
1804    public String getTokenType(int tokenId) throws QueryException
1805    {
1806        synchronized(_psTokenType) {
1807            try
1808            {
1809                _psTokenType.setInt(1, tokenId);
1810                try(ResultSet result = _psTokenType.executeQuery();)
1811                {
1812                    if(!result.next())
1813                    {
1814                        throw new QueryException("Token not found: " + tokenId); 
1815                    }
1816                    return result.getString(1);
1817                }
1818            }
1819            catch(SQLException e)
1820            {
1821                throw new QueryException("Unable to query token type: ", e);
1822            }
1823        }
1824    }
1825
1826    /** Get the channel that a token was read or written on.
1827     *  @param tokenId the token id
1828     *  @param read if true, return the channel the token was read on.
1829     *  otherwise, return the channel the token was written on.
1830     *  @param fireId the actor firing id. can be null for writes, but
1831     *  must be specified for reads.
1832     *  @return the channel the token was read or written on.
1833     */
1834    @Override
1835    public Integer getChannelForToken(int tokenId, boolean read, Integer fireId)
1836            throws QueryException {
1837        
1838        PreparedStatement preparedStatement;
1839        if(read) {
1840            preparedStatement = _psChannelForTokenRead;
1841        } else {
1842            preparedStatement = _psChannelForTokenWrite;
1843        }
1844     
1845        synchronized(preparedStatement) {
1846            try {
1847                preparedStatement.setInt(1, tokenId);
1848                if(read) {
1849                    preparedStatement.setInt(2, fireId);
1850                }
1851                try(ResultSet result = preparedStatement.executeQuery();) {
1852                    if(result.next()) {
1853                        return result.getInt(1);
1854                    }
1855                }
1856            } catch (SQLException e) {
1857                throw new QueryException("Error querying read/write channel.", e);
1858            }
1859        }
1860        return null;
1861    }
1862
1863    /** Get the id of entity. If workflow or entity is not found, returns null.
1864     *  @param entityName name of entity.
1865     *  @param lsid the workflow LSID. NOTE: the revision is ignored.
1866     */
1867    @Override
1868    public Integer getEntityId(String entityName, KeplerLSID lsid)
1869        throws QueryException
1870    {
1871        try
1872        {
1873            _psEntityIdLSID.setString(1, entityName);
1874            _psEntityIdLSID.setString(2, lsid.toStringWithoutRevision());
1875            ResultSet result = null;
1876            try
1877            {
1878                int retval;
1879                result = _psEntityIdLSID.executeQuery();
1880                if(!result.next() || (retval = result.getInt(1)) == 0)
1881                {
1882                    return null;
1883                }
1884                return retval;
1885            }
1886            finally
1887            {
1888                if(result != null)
1889                {
1890                    result.close();
1891                }
1892            }
1893        }
1894        catch(SQLException e)
1895        {
1896            throw new QueryException("Unable to query entity id: ", e);
1897        }
1898    }
1899
1900    /** Get the id of entity. If workflow or entity is not found, returns null. 
1901     *  @param entityName the name of the entity. 
1902     *  @param workflowName the name of the workflow.
1903     */
1904    @Override
1905    public Integer getEntityId(String entityName, String workflowName)
1906        throws QueryException
1907    {
1908        try
1909        {
1910            _psEntityId.setString(1, entityName);
1911            _psEntityId.setString(2, workflowName);
1912            ResultSet result = null;
1913            try
1914            {
1915                int retval;
1916                result = _psEntityId.executeQuery();
1917                if(!result.next() || (retval = result.getInt(1)) == 0)
1918                {
1919                    return null; 
1920                }
1921
1922                return retval;
1923            }
1924            finally
1925            {
1926                if(result != null)
1927                {
1928                    result.close();
1929                }
1930            }
1931        }
1932        catch(SQLException e)
1933        {
1934            throw new QueryException("Unable to query entity id: ", e);
1935        }
1936    }
1937    
1938    @Override
1939    public String getEntityType(Integer entityId) throws QueryException
1940    {
1941        try
1942        {
1943            _psEntityType.setInt(1, entityId);
1944            ResultSet result = null;
1945            try
1946            {
1947                result = _psEntityType.executeQuery();
1948                if(!result.next())
1949                {
1950                    throw new QueryException("Entity not found: " + entityId); 
1951                }
1952
1953                return result.getString(1);
1954            }
1955            finally
1956            {
1957                if(result != null)
1958                {
1959                    result.close();
1960                }
1961            }
1962        }
1963        catch(SQLException e)
1964        {
1965            throw new QueryException("Unable to query entity type: ", e);
1966        }
1967    }
1968    
1969    
1970    @Override
1971    public Integer getEntityWorkflowId(Integer entityId) throws QueryException
1972    {
1973        try
1974        {
1975            _psEntityWorkflowId.setInt(1, entityId);
1976            ResultSet result = null;
1977            try
1978            {
1979                result = _psEntityWorkflowId.executeQuery();
1980                if(!result.next())
1981                {
1982                    throw new QueryException("Entity not found: " + entityId); 
1983                }
1984
1985                return result.getInt(1);
1986            }
1987            finally
1988            {
1989                if(result != null)
1990                {
1991                    result.close();
1992                }
1993            }
1994        }
1995        catch(SQLException e)
1996        {
1997            throw new QueryException("Unable to query entity type: ", e);
1998        }
1999    }
2000    
2001    /** Get an actor's name for a firing id. */
2002    @Override
2003    public String getActorName(int fireId) throws QueryException
2004    {
2005        synchronized(_psActorNameForFiring) {
2006            try 
2007            {
2008                _psActorNameForFiring.setInt(1, fireId);
2009                try(ResultSet result = _psActorNameForFiring.executeQuery();)
2010                {
2011                    if(!result.next())
2012                    {
2013                        throw new QueryException("No actor for firing id.");
2014                    }
2015                    return result.getString("name");
2016                }
2017            }
2018            catch(SQLException e)
2019            {
2020                throw new QueryException("Unable to get actor name: ", e); 
2021            }
2022        }
2023    }
2024
2025    /** Get an actor's type for a firing id. */
2026    @Override
2027    public String getActorType(int fireId) throws QueryException
2028    {
2029        try 
2030        {
2031            _psActorTypeForFiring.setInt(1, fireId);
2032            ResultSet result = null;
2033            try
2034            {
2035                result = _psActorTypeForFiring.executeQuery();
2036                if(!result.next())
2037                {
2038                    throw new QueryException("No actor for firing id.");
2039                }
2040            
2041                return result.getString("class");
2042            }
2043            finally
2044            {
2045                if(result != null)
2046                {
2047                    result.close();
2048                }
2049            }
2050        }
2051        catch(SQLException e)
2052        {
2053            throw new QueryException("Unable to get actor type: ", e); 
2054        }
2055    }
2056
2057    /** Get an actor's parameter name value pairs for a firing id.
2058     *  NOTE: Parameter values may change during a firing; this method
2059     *  returns the values at the <b>start</b> of the firing. 
2060     */
2061    @Override
2062    public Map<String,String> getParameterNameValuesForFiring(int fireId)
2063        throws QueryException
2064    {
2065        try
2066        {
2067            String actorName = getActorName(fireId);
2068            _psParamNameValuesForFiring.setString(1, actorName + ".%");
2069            _psParamNameValuesForFiring.setInt(2, fireId);
2070            Map<String,String> nameValuesMap = _getParameterNameValues(_psParamNameValuesForFiring);
2071            
2072            // use a different map to remove components (see below) to avoid
2073            // ConcurrentModificationExceptions
2074            Map<String,String> retval = new LinkedHashMap<String,String>(nameValuesMap);
2075            
2076            // remove the components that are not direct parameters
2077            // e.g., parameters of the actors ports.
2078            for(String name : nameValuesMap.keySet())
2079            {
2080                if(name.lastIndexOf('.') > actorName.length())
2081                {
2082                    retval.remove(name);
2083                }
2084            }
2085            
2086            return retval;
2087        }
2088        catch(SQLException e)
2089        {
2090            throw new QueryException("Unable to get name value pairs: ", e);
2091        }
2092    }
2093
2094    /** Get the parameter name value pairs for an execution. NOTE: Parameter
2095     *  values may change during an execution; this method returns the
2096     *  values at the <b>start</b> of the execution.
2097     */
2098    @Override
2099    public Map<String,String> getParameterNameValuesForExecution(int execId)
2100        throws QueryException
2101    {
2102        try
2103        {
2104            _psParamNameValuesForExecution.setInt(1, execId);
2105            return _getParameterNameValues(_psParamNameValuesForExecution);
2106        }
2107        catch(SQLException e)
2108        {
2109            throw new QueryException("Unable to get name value pairs: ", e);
2110        }
2111    }
2112
2113    /** Get the latest value of a parameter. 
2114     *  @param parameter the parameter name.
2115     *  @param lsid the workflow lsid
2116     *  @return the latest value of the parameter. If the workflow LSID or
2117     *  parameter name cannot be found, returns null.
2118     */
2119    @Override
2120    public String getParameterValueLatest(String parameter, KeplerLSID lsid)
2121        throws QueryException
2122    {
2123        return getParameterValueAtTime(new Date(Long.MAX_VALUE), parameter,
2124            lsid);
2125    }
2126
2127  
2128    /** Get the value of a parameter at a specific time. 
2129     * @param timestamp the time
2130     * @param parameter the parameter name
2131     * @param lsid the workflow lsid
2132     * @return the value of the parameter at the specified time. If the
2133     * workflow LSID or parameter name cannot be found, returns null.
2134     */
2135    @Override
2136    public String getParameterValueAtTime(Date timestamp, String parameter,
2137        KeplerLSID lsid) throws QueryException
2138    {
2139        try
2140        {
2141            String paramName = _getEntityName(parameter);
2142            
2143            //System.out.println("look for parameter name = " + paramName);
2144
2145            _psParamValueAtTimeForWorkflowLSID.setString(1, paramName);
2146            _psParamValueAtTimeForWorkflowLSID.setString(2, lsid.toStringWithoutRevision());
2147            _psParamValueAtTimeForWorkflowLSID.setTimestamp(3,
2148                new Timestamp(timestamp.getTime()));
2149            
2150            //System.out.println("query " + _psParamValueAtTimeForWorkflowLSID);
2151
2152            ResultSet result = null;
2153            
2154            try
2155            {
2156                result = _psParamValueAtTimeForWorkflowLSID.executeQuery();
2157                if(result.next())
2158                {
2159                    return result.getString("value");
2160                }
2161            }
2162            finally
2163            {
2164                if(result != null)
2165                {
2166                    result.close();
2167                }
2168            }
2169        }
2170        catch(SQLException e)
2171        {
2172            throw new QueryException("Error querying parameter value: ", e); 
2173        }
2174
2175        return null;
2176    }
2177
2178    /** Given a fully-qualified entity name, return the name as it would
2179     *  appear in the entity table.
2180     */
2181    protected String _getEntityName(String name)
2182    {
2183        int index = name.indexOf('.', 1);
2184        if(index > -1)
2185        {
2186            return name.substring(index);
2187        }
2188        return "";
2189    }
2190    
2191    /** Given a name from the entity table, return the fully-qualified name. */
2192    protected String _getFullNameFromEntityName(String entity, String full)
2193    {
2194        int index = full.indexOf('.', 1);
2195        if(index == -1)
2196        {
2197            return full + entity;
2198        }
2199        else
2200        {
2201            return full.substring(0, index) + entity;
2202        }
2203    }
2204 
2205    /** Get the immediate tokens that generated a token. */
2206    @Override
2207    public List<Integer> getImmediateDependencies(int tokenId)
2208        throws QueryException
2209    {
2210        // get the firing id that generated this token
2211        List<Integer> firingsList = getActorFiringForToken(tokenId, false);
2212        int fireId;
2213
2214        if(firingsList.size() == 0)
2215        {
2216            throw new QueryException("No firing generated token " + tokenId +
2217                ".");
2218        }
2219        else if(firingsList.size() == 1)
2220        {
2221            fireId = firingsList.get(0);    
2222        }
2223        else // firingsList.size() > 1
2224        {
2225            throw new QueryException("Multiple firings produced token " +
2226                tokenId + ".");
2227        }
2228
2229        // get the tokens read for that firing id
2230        return getTokensForFiring(fireId, true);
2231    }
2232
2233    /** Get a file associated with an execution. */
2234    @Override
2235    public List<byte[]> getAssociatedDataForExecution(int execId,
2236        Map<String,String> metadataMap, boolean matchAny)
2237        throws QueryException
2238    {
2239        List<byte[]> retList = new ArrayList<byte[]>();
2240
2241        try
2242        {    
2243            //construct the SQL string
2244            String data = _dbType.getTableName("data");
2245            String assocDataStr = _dbType.getTableName("associated_data");
2246                StringBuffer queryBuf = new StringBuffer(
2247                    // XXX can't distinct() on blob in oracle
2248                    "SELECT data.contents " +
2249                "FROM " 
2250                    + assocDataStr + " ad, " 
2251                    + data + " data " +
2252                "WHERE data.md5 = ad.data_id AND ad.wf_exec_id = ? ");
2253            
2254            String operator = "AND";
2255            if (matchAny) 
2256            {
2257                operator = "OR";
2258            }
2259            
2260            for(int i = 0; i < metadataMap.size(); i++) 
2261            {
2262                queryBuf.append(operator);
2263                queryBuf.append(" (name = ? AND val = ?)");
2264            }
2265
2266            try(PreparedStatement psAssociatedData = 
2267                    _dbType.getConnection().prepareStatement(queryBuf.toString()))
2268            {
2269                psAssociatedData.setInt(1, execId);
2270                int paramCounter = 2;
2271                
2272                for(Map.Entry<String,String> entry : metadataMap.entrySet()) 
2273                {
2274                    //select distinct(file) where (name = ? and value = ?) or (name = ? and value = ?) or (name = ? and value = ?)
2275                    psAssociatedData.setString(paramCounter++, entry.getKey());
2276                    psAssociatedData.setString(paramCounter++, entry.getValue());
2277                }
2278                
2279                //execute
2280                try(ResultSet result = psAssociatedData.executeQuery())
2281                {
2282                    while (result.next())
2283                    {
2284                        retList.add(result.getBytes(1));
2285                    }
2286                }
2287            }
2288        }
2289        catch(SQLException e)
2290        {
2291            throw new QueryException("Error querying associated data: ", e);
2292        }
2293        return retList;
2294    }
2295   
2296    /** Get any associated keys-values for an execution. */
2297    @Override
2298    public Map<String,String> getAssociatedKeysValuesForExecution(int execId)
2299        throws QueryException {
2300     
2301        Map<String,String> retval = new HashMap<String,String>();
2302
2303        synchronized(_psAssociatedKeysValuesForExecution) {
2304            try {
2305                _psAssociatedKeysValuesForExecution.setInt(1, execId);
2306                
2307                try(ResultSet rs = _psAssociatedKeysValuesForExecution.executeQuery()) {
2308                    while(rs.next()) {
2309                        retval.put(rs.getString(1), rs.getString(2));
2310                    }                    
2311                }
2312                
2313            } catch(SQLException e) {
2314                throw new QueryException("Error querying keys values.", e);
2315            }
2316        }
2317        
2318        return retval;
2319        
2320    }
2321
2322    /** Get a list of files saved in the database. */
2323    public List<String> getSavedFilenames() throws QueryException
2324    {
2325        List<String> retval = new LinkedList<String>();
2326        
2327        Statement statement = null;
2328        ResultSet result = null;
2329        try
2330        {
2331            try
2332            {
2333                statement = _dbType.getStatement();
2334                // XXX change to prepared statement
2335                result = statement.executeQuery("select contents from data " +
2336                    "where md5 in (select data_id from port_event where " +
2337                    "file_id != '')");
2338                while(result.next())
2339                {
2340                    retval.add(new String(result.getBytes(1)));
2341                }
2342            }
2343            finally
2344            {
2345                if(result != null)
2346                {
2347                    result.close();
2348                }
2349                
2350                if(statement != null)
2351                {
2352                    statement.close();
2353                }
2354            }
2355        }
2356        catch(SQLException e)
2357        {
2358            throw new QueryException("Error querying filenames: ", e);
2359        }
2360        
2361        return retval;
2362    }
2363    
2364    /** Get the ports for an actor.
2365     *  @param workflow the LSID of the workflow
2366     *  @param actor the fully-qualified name of the actor
2367     *  @return a list of fully-qualified names of ports of the actor
2368     */
2369    @Override
2370    public List<String> getPortsForActor(KeplerLSID workflow, String actor)
2371        throws QueryException
2372    {        
2373        try
2374        {
2375            _psPortsForActor.setString(1, workflow.toStringWithoutRevision());
2376            // the names of the ports are prefixed with the actor name and .
2377            _psPortsForActor.setString(2, _getEntityName(actor) + ".%");
2378
2379            //System.out.println("actor = " + _getEntityName(actor));
2380            
2381            ResultSet result = null;
2382            try
2383            {
2384                List<String> retval = new LinkedList<String>();
2385                result = _psPortsForActor.executeQuery();
2386                while(result.next())
2387                {
2388                    String name = result.getString(1);
2389                    //System.out.println("name = " + name);
2390                    String fullName = _getFullNameFromEntityName(name, actor); 
2391                    retval.add(fullName);
2392                }
2393                return retval;
2394            }
2395            finally
2396            {
2397                if(result != null)
2398                {
2399                    result.close();
2400                }
2401            }
2402        }
2403        catch(SQLException e)
2404        {
2405            throw new QueryException("Error querying ports for actor: ", e);
2406        }
2407    }
2408
2409    /** Get the host id for a specific execution id. If no execution
2410     *  exists, returns null.
2411     */
2412    @Override
2413    public String getHostIdForExecution(Integer execId) throws QueryException 
2414    {
2415        try
2416        {
2417            _psHostIdForExecution.setInt(1, execId);
2418            ResultSet result = null;
2419            try
2420            {
2421                result = _psHostIdForExecution.executeQuery();
2422                if(!result.next())
2423                {
2424                    return null;
2425                }
2426                return result.getString(1);
2427            }
2428            finally
2429            {
2430                if(result != null)
2431                {
2432                    result.close();
2433                }
2434            }
2435        }
2436        catch(SQLException e)
2437        {
2438            throw new QueryException("Unable to query host id for " +
2439                "execution id " + execId + ": ", e);
2440        }
2441    }    
2442    
2443    /** Get the user for a specific execution LSID. */
2444    @Override
2445    public String getUserForExecution(KeplerLSID execLSID) throws QueryException {
2446        
2447        synchronized(_psUserForExecutionLSID) {
2448            try {
2449                _psUserForExecutionLSID.setString(1, execLSID.toString());
2450                try(ResultSet result = _psUserForExecutionLSID.executeQuery()) {
2451                    if(result.next()) {
2452                        return result.getString(1);
2453                    }
2454                }
2455            } catch(SQLException e) {
2456                throw new QueryException("Unable to query user for " +
2457                    "execution lsid " + execLSID + ": ", e);
2458            }
2459        }
2460        return null;
2461    }
2462    
2463    /** Get the user for a specific execution id. If no execution
2464     *  exists, returns null.
2465     */
2466    @Override
2467    public String getUserForExecution(Integer execId) throws QueryException 
2468    {
2469        try
2470        {
2471            _psUserForExecution.setInt(1, execId);
2472            ResultSet result = null;
2473            try
2474            {
2475                result = _psUserForExecution.executeQuery();
2476                if(!result.next())
2477                {
2478                    return null;
2479                }
2480                return result.getString(1);
2481            }
2482            finally
2483            {
2484                if(result != null)
2485                {
2486                    result.close();
2487                }
2488            }
2489        }
2490        catch(SQLException e)
2491        {
2492            throw new QueryException("Unable to query user for " +
2493                "execution id " + execId + ": ", e);
2494        }
2495    } 
2496    
2497    
2498    /** Get the output role (port name) of a token. */
2499    @Override
2500    public String getOutputRoleToken(int tokenId) throws QueryException
2501    {
2502        synchronized(_psOutputRoleToken) {
2503            try
2504            {
2505                _psOutputRoleToken.setInt(1, tokenId);
2506                try(ResultSet result = _psOutputRoleToken.executeQuery();) {
2507                    if(! result.next())
2508                    {
2509                        throw new QueryException("Token not found: " + tokenId);                
2510                    }
2511                    return result.getString(1);
2512                }
2513            }
2514            catch(SQLException e)
2515            {
2516                throw new QueryException("Unable to query output role: ", e);
2517            }
2518        }
2519    }    
2520    
2521    
2522    /** Get the input role (port name) of a token. */
2523    @Override
2524    public String getInputRoleForTokenAndFireId(int tokenId, int fireId) throws QueryException
2525    {
2526        synchronized(_psInputRoleToken) {
2527            try
2528            {
2529                _psInputRoleToken.setInt(1, tokenId);
2530                _psInputRoleToken.setInt(2, fireId);
2531    
2532                try(ResultSet result = _psInputRoleToken.executeQuery();) { 
2533                    if(! result.next())
2534                    {
2535                        throw new QueryException("Token not found: " + tokenId);                
2536                    }
2537                    return result.getString(1);
2538                }
2539            }
2540            catch(SQLException e)
2541            {
2542                throw new QueryException("Unable to query input role: ", e);
2543            }
2544        }
2545    }    
2546    
2547    /** Get the parameter name value pairs for an execution, whose parameter type is
2548     * either '.Parameter' or '.StringParameter' 
2549     */
2550    @Override
2551    public Map<String,String> getParameterNameValuesOfSpecificTypeForExecution(int execId)
2552        throws QueryException
2553    {
2554        try
2555        {
2556            _psParamNameValuesOfSpecificTypeForExecution.setInt(1, execId);
2557            return _getParameterNameValues(_psParamNameValuesOfSpecificTypeForExecution);
2558        }
2559        catch(SQLException e)
2560        {
2561            throw new QueryException("Unable to get name value pairs: ", e);
2562        }
2563    }   
2564    
2565    /** Get the start and end timestamps of an actor firing. */
2566    @Override
2567    public Date[] getTimestampsForActorFiring(int fireId)
2568        throws QueryException
2569    {
2570        synchronized(_psTimestampsForActorFiring) {
2571            try
2572            {
2573                _psTimestampsForActorFiring.setInt(1, fireId);
2574                try(ResultSet result = _psTimestampsForActorFiring.executeQuery();)
2575                {
2576                    if(result.next())
2577                    {
2578                        Date[] retval = new Date[2];
2579                        retval[0] = result.getTimestamp("start_time");
2580                        retval[1] = result.getTimestamp("end_time");
2581                        return retval;
2582                    }
2583                }
2584            }
2585            catch(SQLException e)
2586            {
2587                throw new QueryException("Error querying timestamps for actor firing:", e);
2588            }
2589        }
2590        return null;
2591    }
2592    
2593    /** Get actor fire ids based on its full name and workflow exec ID. */
2594    @Override
2595    public List<Integer> getActorFiringIds(String actorName, Integer wfExecId)
2596        throws QueryException
2597    {
2598        try
2599        {
2600                _psActorFireId.setInt(1, wfExecId);
2601                _psActorFireId.setString(2, _getEntityName(actorName));
2602                return _getIntResults(_psActorFireId, 1);
2603        }
2604        catch(SQLException e)
2605        {
2606            throw new QueryException("Error querying timestamps for actor firing:", e);
2607        }
2608    } 
2609    
2610    /** Get an actor's parameter and port parameter name value pairs for a firing id.
2611     *  NOTE: Parameter values may change during a firing; this method
2612     *  returns the values at the <b>start</b> of the firing. 
2613     */
2614    @Override
2615    public Map<String,String> getParameterAndPortParameterNameValuesForFiring(int fireId)
2616        throws QueryException
2617    {
2618        try
2619        {
2620            String actorName = getActorName(fireId);
2621            _psParamAndPortParamNameValuesForFiring.setString(1, actorName + ".%");
2622            _psParamAndPortParamNameValuesForFiring.setInt(2, fireId);
2623            Map<String,String> nameValuesMap = _getParameterNameValues(_psParamAndPortParamNameValuesForFiring);
2624            
2625            // use a different map to remove components (see below) to avoid
2626            // ConcurrentModificationExceptions
2627            Map<String,String> retval = new LinkedHashMap<String,String>(nameValuesMap);
2628            
2629            // remove the components that are not direct parameters
2630            // e.g., parameters of the actors ports.
2631            for(String name : nameValuesMap.keySet())
2632            {
2633                if(name.lastIndexOf('.') > actorName.length())
2634                {
2635                    retval.remove(name);
2636                }
2637            }
2638            
2639            return retval;
2640        }
2641        catch(SQLException e)
2642        {
2643            throw new QueryException("Unable to get name value pairs: ", e);
2644        }
2645    }    
2646
2647    /** Get all actor fire ids for a workflow exec ID. */
2648    @Override
2649    public List<Integer> getActorFirings(int wfExecId) throws QueryException {
2650        
2651        synchronized(_psActorFirings) {
2652            try {
2653                _psActorFirings.setInt(1, wfExecId);
2654                return _getIntResults(_psActorFirings, 1);
2655            } catch(SQLException e) {
2656                throw new QueryException("Error getting actor firings: ", e);
2657            }
2658        }
2659    }
2660        
2661    /** Get the execution times for an actor.
2662    *
2663    * @param workflow the workflow LSID. If null, must specify execution id.
2664    * @param execLSID the execution LSID. If null, the last execution of the workflow
2665    * is used.
2666    * @param actor the full actor name.
2667    * @return a map of actor fire id to execution time pairs. if a firing does not
2668    * have a valid start or stop time, the firing is not included in the result.
2669    */
2670    @Override
2671    public Map<String,Long> getExecutionTimesForActor(KeplerLSID workflow,
2672        KeplerLSID execLSID, String actor) throws QueryException
2673    {
2674        int runId;
2675        
2676        // see if the execution id is specified
2677        if(execLSID == null)
2678        {
2679            runId = _getLastExecutionId(workflow);
2680        }
2681        else
2682        {
2683            runId = getExecutionForExecutionLSID(execLSID);
2684        }
2685        
2686        try
2687        {
2688            //System.out.println(actor + " --> " + _getEntityName(actor));
2689            
2690            _psActorExecutionTimes.setString(1, _getEntityName(actor));
2691            _psActorExecutionTimes.setInt(2, runId);
2692            
2693            //System.out.println(_psActorExecutionTimes);
2694            
2695            // use a LinkedHashMap for predictable iteration order.
2696            Map<String,Long> retval = new LinkedHashMap<String,Long>();
2697    
2698            try(ResultSet result = _psActorExecutionTimes.executeQuery();)
2699            {    
2700                while(result.next())
2701                {
2702                    Timestamp start = result.getTimestamp(2);
2703                    long startVal;
2704                    if(start == null) {
2705                        System.err.println("WARNING: missing start time for " + actor);
2706                        continue;
2707                    } else {
2708                        startVal = start.getTime();
2709                    }
2710                    
2711                    Timestamp end = result.getTimestamp(3);
2712                    long endVal;
2713                    if(end == null) {
2714                        System.err.println("WARNING: missing stop time for " + actor);
2715                        continue;
2716                    } else {
2717                        endVal = end.getTime();
2718                    }
2719                    
2720                    long duration = endVal - startVal;
2721                    
2722                    retval.put(result.getString(1), duration);
2723                    //System.out.println("fire time for " + actor + " id " + result.getString(1) + " duration " + duration);
2724                }
2725                return retval;
2726            }
2727        }
2728        catch(SQLException e)
2729        {
2730            throw new QueryException("Error querying actor execution times: ", e);
2731        }
2732    }
2733        
2734    /** Get the number of bytes read or written by a port.
2735    *
2736    *  @param workflow the workflow LSID. If null, must specify execution id.
2737    *  @param execId the execution id. If null, the last execution of the workflow
2738    *  is used.
2739    *  @param port the full port name.
2740    *  @param read If true, get the bytes read. Otherwise, get the bytes written.
2741    *  @return a map of port event id to bytes read/written.
2742    */
2743    @Override
2744    public Map<Integer,Integer> getIOBytesForPort(KeplerLSID workflow, Integer execId,
2745        String port, boolean read) throws QueryException
2746    {
2747        
2748        // FIXME queries need updating after addition of port_event.data
2749        //
2750        
2751        if(execId == null)
2752        {
2753            execId = _getLastExecutionId(workflow);
2754        }
2755        
2756        try
2757        {    
2758            PreparedStatement ps;
2759            
2760            if(read)
2761            {
2762                ps = _psPortIOBytesRead;
2763            }
2764            else
2765            {
2766                ps = _psPortIOBytesWrite;
2767            }
2768            
2769            ps.setString(1, _getEntityName(port));
2770            ps.setInt(2, execId);
2771            
2772            Map<Integer,Integer> retval = new HashMap<Integer,Integer>();
2773
2774            try(ResultSet result = ps.executeQuery();)
2775            {
2776                while(result.next())
2777                {
2778                    int id = result.getInt(1);
2779                    byte[] data = result.getBytes(2);
2780                    retval.put(id, data.length);
2781                }
2782                
2783                return retval;
2784            }
2785        }
2786        catch(SQLException e)
2787        {
2788            throw new QueryException("Error querying port IO bytes: ", e);
2789        }
2790    }
2791  
2792    /*
2793    @Override
2794    public List<LinkIO> getLinksIO(KeplerLSID workflow, Integer execId) throws QueryException
2795    {
2796        if(execId == null)
2797        {
2798            execId = _getLastExecutionId(workflow);
2799        }
2800        
2801        try
2802        {
2803            _psIOLinkRead.setInt(1, execId);
2804
2805            Map<Integer, LinkIO> map = new HashMap<Integer, LinkIO>();
2806            
2807            try(ResultSet result = _psIOLinkRead.executeQuery();)
2808            {
2809                //System.out.println(_psIOLinkRead);
2810                
2811                while(result.next())
2812                {
2813                    // reading actor name, id of port of event of writer, data
2814                    LinkIO io = new LinkIO();
2815                    io.dest = result.getString(1);
2816                    io.source_id = result.getInt(2);
2817                    io.data = result.getBytes(3);
2818                    map.put(io.source_id, io);
2819                    //System.out.println(io);
2820                }
2821                         
2822            }
2823            
2824            Statement sIOLinkWrite = _dbType.getStatement();
2825            String actorFireStr = _dbType.getTableName("actor_fire");
2826            String entityStr =  _dbType.getTableName("entity");
2827            String portEventStr = _dbType.getTableName("port_event");
2828  
2829            StringBuilder queryStr = new StringBuilder("SELECT ent.name, pe.id " +
2830                "FROM " + portEventStr + " pe, " + actorFireStr + " af, " +
2831                entityStr + " ent " + 
2832                "WHERE pe.id IN (");
2833            
2834            int i = 0;
2835            for(LinkIO io : map.values())
2836            {
2837                queryStr.append(" " + io.source_id);
2838                if(i < map.size() - 1)
2839                {
2840                    queryStr.append(", ");
2841                }
2842                i++;
2843            }
2844            
2845            queryStr.append(" ) AND pe.fire_id = af.id AND af.actor_id = ent.id");
2846            
2847            //System.out.println(queryStr);
2848                
2849            try(ResultSet result = sIOLinkWrite.executeQuery(queryStr.toString());)
2850            {
2851                while(result.next())
2852                {
2853                    int portEventId = result.getInt(2);
2854                    LinkIO io = map.get(portEventId);
2855                    io.source = result.getString(1);
2856                }
2857                
2858                sIOLinkWrite.close();
2859                return new LinkedList<LinkIO>(map.values());
2860                
2861            }
2862        }
2863        catch(SQLException e)
2864        {
2865            throw new QueryException("Error querying link IO: ", e);
2866        }
2867    }
2868    */
2869    
2870    /** Get the timestamp(s) when a token was read. If a token was
2871     *  never read, this can return null or an empty array.
2872     */
2873    @Override
2874    public Date[] getTimestampsForTokenRead(Integer tokenId) throws QueryException {
2875        synchronized(_psTimestampsForTokenRead) {
2876            try {
2877                _psTimestampsForTokenRead.setInt(1, tokenId);
2878                try(ResultSet result = _psTimestampsForTokenRead.executeQuery();) {
2879                    List<Date> timestamps = new ArrayList<Date>();
2880                    while(result.next()) {
2881                        timestamps.add(result.getTimestamp(1));
2882                    }
2883                    return timestamps.toArray(new Date[timestamps.size()]);
2884                }
2885            }
2886            catch(SQLException e) {
2887                throw new QueryException("Error querying timestamps for token read.", e);
2888            }
2889        }
2890    }
2891
2892    /** Get the timestamp when a token was written. */
2893    @Override
2894    public Date getTimestampForTokenWrite(Integer tokenId) throws QueryException {
2895        synchronized(_psTimestampForTokenWrite) {
2896            try {
2897                _psTimestampForTokenWrite.setInt(1, tokenId);
2898                try(ResultSet result = _psTimestampForTokenWrite.executeQuery();) {
2899                    if(result.next()) {
2900                        return result.getTimestamp(1);
2901                    }
2902                }
2903            }
2904            catch(SQLException e) {
2905                throw new QueryException("Error querying timestamps for token write.", e);
2906            }
2907        }
2908        return null;
2909    }
2910
2911    ///////////////////////////////////////////////////////////////////////
2912    //// protected methods                                             ////
2913
2914    /** Construct a new SQLQueryV8 from a connected DatabaseType. */
2915    protected SQLQueryV8(DatabaseType dbType) throws QueryException
2916    {
2917        _dbType = dbType;
2918
2919        try
2920        {
2921            _createPreparedStatements();
2922        }
2923        catch(SQLException e)
2924        {
2925            throw new QueryException("Error creating prepared statements: ", e);
2926        }
2927    }
2928
2929    /** Check the schema version and initialize the prepared statements. */
2930    protected void _init()
2931        throws QueryException
2932    {
2933        try
2934        {
2935            Schema schema = Schemas.createSchemaV8();
2936            Schemas.checkVersion(_dbType, schema);
2937            _createPreparedStatements();
2938        }
2939        catch(SQLException e)
2940        {
2941            e.printStackTrace();
2942            throw new QueryException("Unable to connect or initialize " +
2943                "database connection: ", e);
2944        }
2945    }
2946
2947    /** Initialize the prepared statements. */
2948    protected void _createPreparedStatements() throws SQLException
2949    {
2950        // get the table table names
2951        String actorFireStr = _dbType.getTableName("actor_fire");
2952        String actorStr =  _dbType.getTableName("actor");
2953        String associatedDataStr = _dbType.getTableName("associated_data");
2954        String dataStr = _dbType.getTableName("data");
2955        String entityStr =  _dbType.getTableName("entity");
2956        String errorStr = _dbType.getTableName("error");
2957        String parameterStr = _dbType.getTableName("parameter");
2958        String parameterExecStr = _dbType.getTableName("parameter_exec");
2959        String portEventStr = _dbType.getTableName("port_event");
2960        String wfChangeStr = _dbType.getTableName("workflow_change");
2961        String wfExecStr = _dbType.getTableName("workflow_exec");
2962        String wfStr = _dbType.getTableName("workflow");
2963        String tagStr = _dbType.getTableName("tag");
2964        
2965        // NOTE: user is a reserved column name in oracle
2966        String userColumnStr = _dbType.getColumnName("user");
2967
2968
2969        if(_psWorkflows == null)
2970        {
2971            _psWorkflows = _dbType.getSQLSelect("workflow", "*");
2972        }
2973        
2974        if(_psWorkflowNameForLSID == null)
2975        {
2976            _psWorkflowNameForLSID = _dbType.getSQLSelect("workflow", "name",
2977                "lsid = ?");
2978        }
2979        
2980        if(_psWorkflowNameForExecutionLSID == null) {
2981            _psWorkflowNameForExecutionLSID = _dbType.getPrepStatement(
2982                "SELECT wf.name " +
2983                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
2984                "WHERE wfExec.wf_id = wf.id AND wfexec.lsid = ?");
2985        }
2986
2987        if(_psExecutions == null)
2988        {
2989            _psExecutions = _dbType.getSQLSelect("workflow_exec", "id"); 
2990        }
2991
2992        if(_psExecutionsForTimespan == null)
2993        {
2994            _psExecutionsForTimespan = _dbType.getSQLSelect("workflow_exec",
2995                "id", "start_time >= ? AND end_time <= ?");
2996        }
2997        
2998        if(_psExecutionsForTimespanOnHost == null)
2999        {
3000            _psExecutionsForTimespanOnHost = _dbType.getPrepStatement(
3001                "SELECT wfexec.id " +
3002                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3003                "WHERE wfexec.start_time >= ? AND wfexec.end_time <= ? AND " +
3004                "wfexec.host_id = ? AND wf.id = wfexec.wf_id AND wf.name = ?");
3005        }
3006        
3007        if(_psTimestampsForExecution == null)
3008        {
3009            _psTimestampsForExecution = _dbType.getSQLSelect("workflow_exec",
3010                    "start_time, end_time", "id = ?");
3011        }
3012        
3013        if(_psEntitiesForChange == null)
3014        {
3015            _psEntitiesForChange = _dbType.getSQLSelect("entity", "id",
3016                "wf_change_id = ?");
3017        }
3018
3019        if(_psChangesForTimespan == null)
3020        {
3021            _psChangesForTimespan = _dbType.getSQLSelect("workflow_change",
3022                "id", "time >= ? AND time <= ?");
3023        }
3024        
3025        if(_psChangesForTimespanOnHost == null)
3026        {
3027            _psChangesForTimespanOnHost = _dbType.getPrepStatement(
3028                "SELECT id " +
3029                "FROM " + wfStr + " wf, " + wfChangeStr + " wfchange " +
3030                "WHERE wfchange.time >= ? AND wfchange.time <= ? AND " +
3031                "wfchange.host_id = ? AND wf.id = wfchange.wf_id AND " +
3032                "wf.name = ?");
3033        }
3034        
3035        if(_psExecutionsForWorkflow == null)
3036        {
3037            String queryStr = "SELECT wfexec.id " +
3038                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3039                "WHERE wf.id = wfexec.wf_id AND wf.name = ?";
3040            _psExecutionsForWorkflow = _dbType.getPrepStatement(queryStr);
3041        }
3042        
3043        if(_psExecutionForExecutionLSID == null)
3044        {
3045            String queryStr = "SELECT wfexec.id " +
3046                "FROM " + wfExecStr + " wfexec " +
3047                "WHERE wfexec.lsid = ?";
3048            _psExecutionForExecutionLSID = _dbType.getPrepStatement(queryStr);
3049        }
3050        
3051        if(_psExecutionForExecutionLSIDWithoutRevision == null)
3052        {
3053            String queryStr = "SELECT wfexec.id " +
3054                "FROM " + wfExecStr + " wfexec " +
3055                "WHERE wfexec.lsid LIKE ?";
3056            _psExecutionForExecutionLSIDWithoutRevision = _dbType.getPrepStatement(queryStr);
3057        }
3058        
3059        if(_psExecutionForOldestReferralExecutionLSIDWithoutRevision == null)
3060        {
3061                 String queryStr = "SELECT wfexec.id " +
3062             "FROM " + wfExecStr + " wfexec " +
3063             "WHERE wfexec.derived_from LIKE ?";
3064                 _psExecutionForOldestReferralExecutionLSIDWithoutRevision = _dbType.getPrepStatement(queryStr);
3065        }
3066        
3067        if(_psExecutionLSIDForExecution == null)
3068        {
3069            String queryStr = "SELECT wfexec.lsid " +
3070                "FROM " + wfExecStr + " wfexec " +
3071                "WHERE wfexec.id = ?";
3072            _psExecutionLSIDForExecution = _dbType.getPrepStatement(queryStr);
3073        }
3074        
3075        if(_psExecutionsForWorkflowNameContains == null)
3076        {
3077            String queryStr = "SELECT wfexec.id " +
3078                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3079                "WHERE wf.id = wfexec.wf_id AND LOWER(wf.name) LIKE ?";
3080            _psExecutionsForWorkflowNameContains = _dbType.getPrepStatement(queryStr);
3081        }
3082        
3083        if(_psExecutionsForWorkflowRuns == null)
3084        {
3085            String queryStr = "SELECT wfexec.id " +
3086                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3087                "WHERE wf.id = wfexec.wf_id AND LOWER(wf.name) LIKE ? " +
3088                "AND LOWER(wfexec." + userColumnStr + ") LIKE ?";
3089            _psExecutionsForWorkflowRuns = _dbType.getPrepStatement(queryStr);
3090        }
3091        
3092        if(_psExecutionsForWorkflowRunsAfter == null)
3093        {
3094            String queryStr = "SELECT wfexec.id " +
3095                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3096                "WHERE wf.id = wfexec.wf_id AND LOWER(wf.name) LIKE ? " +
3097                "AND LOWER(wfexec. " + userColumnStr + ") LIKE ? " +
3098                "AND wfexec.start_time > ?";
3099            _psExecutionsForWorkflowRunsAfter = _dbType.getPrepStatement(queryStr);
3100        }
3101        
3102        if(_psExecutionsForWorkflowRunsAt == null)
3103        {
3104            String queryStr = "SELECT wfexec.id " +
3105                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3106                "WHERE wf.id = wfexec.wf_id AND LOWER(wf.name) LIKE ? " +
3107                "AND LOWER(wfexec." + userColumnStr + ") LIKE ? " +
3108                "AND wfexec.start_time LIKE ?"; //TODO implement.
3109            _psExecutionsForWorkflowRunsAt = _dbType.getPrepStatement(queryStr);
3110        }
3111        
3112        if(_psExecutionsForWorkflowRunsBefore == null)
3113        {
3114            String queryStr = "SELECT wfexec.id " +
3115                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3116                "WHERE wf.id = wfexec.wf_id AND LOWER(wf.name) LIKE ? " +
3117                "AND LOWER(wfexec." + userColumnStr + ") LIKE ? " +
3118                "AND wfexec.start_time < ?";
3119            _psExecutionsForWorkflowRunsBefore = _dbType.getPrepStatement(queryStr);
3120        }
3121        
3122        if(_psExecutionsForWorkflowRuns2 == null) //inclusive
3123        {
3124            String queryStr = "SELECT wfexec.id " +
3125                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3126                "WHERE wf.id = wfexec.wf_id " +
3127                "AND LOWER(wf.name) LIKE ? " +
3128                "AND LOWER(wfexec." + userColumnStr + ") LIKE ? " +
3129                "AND wfexec.start_time >= ? " +
3130                "AND wfexec.start_time <= ? " + 
3131                "AND wfexec.id >= ? " +
3132                "AND wfexec.id <= ?";
3133            _psExecutionsForWorkflowRuns2 = _dbType.getPrepStatement(queryStr);
3134        }
3135        
3136        if(_psExecutionLSIDsForWorkflowRuns == null) //inclusive
3137        {
3138            String queryStr = "SELECT wfexec.lsid " +
3139                "FROM " + wfStr + " wf, " + wfExecStr + " wfexec " +
3140                "WHERE wf.id = wfexec.wf_id " +
3141                "AND LOWER(wf.name) LIKE ? " +
3142                "AND LOWER(wfexec." + userColumnStr + ") LIKE ? " +
3143                "AND wfexec.start_time >= ? " +
3144                "AND wfexec.start_time <= ? " + 
3145                "AND wfexec.id >= ? " +
3146                "AND wfexec.id <= ?";
3147            _psExecutionLSIDsForWorkflowRuns = _dbType.getPrepStatement(queryStr);
3148        }
3149        
3150        if(_psExecutionIdsForTags == null)
3151        {
3152            String queryStr = "SELECT tag.wf_exec_id " +
3153                "FROM " + tagStr + " tag " +
3154                "WHERE LOWER(tag.searchstring) LIKE ?";
3155            _psExecutionIdsForTags = _dbType.getPrepStatement(queryStr);
3156        }
3157        
3158        if(_psExecutionLSIDs == null)
3159        {
3160            _psExecutionLSIDs = _dbType.getPrepStatement("SELECT lsid " +
3161                "FROM " + wfExecStr +
3162                " ORDER BY lsid ASC");
3163            
3164            //_psExecutionLSIDs = _dbType.getSQLSelect("workflow_exec", "lsid");
3165        }
3166        
3167        if(_psTagsForExecutionId == null)
3168        {
3169            String queryStr = "SELECT tag.searchstring " +
3170                "FROM " + tagStr + " tag " +
3171                "WHERE tag.wf_exec_id = ?";
3172            _psTagsForExecutionId = _dbType.getPrepStatement(queryStr);
3173        }
3174
3175        if (_psTagURNsForExecutionId == null) {
3176            String queryStr = "SELECT tag.type, tag.urn FROM " + tagStr + " tag WHERE tag.wf_exec_id = ?";
3177            _psTagURNsForExecutionId = _dbType.getPrepStatement(queryStr);
3178        }
3179        
3180        if (_psTypeForTag == null) {
3181            String queryStr = "SELECT tag.type FROM " + tagStr + " tag WHERE tag.urn = ?";
3182            _psTypeForTag = _dbType.getPrepStatement(queryStr);
3183        }
3184        
3185        if(_psExecutionsForAnnotation == null)
3186        {
3187            _psExecutionsForAnnotation = _dbType.getSQLSelect("workflow_exec",
3188                "id", "annotation = ?");
3189        }
3190
3191        // read port events have write_id = id of port event that generated
3192        // the token, i.e. != -1.
3193        if(_psTokensReadForFiring == null)
3194        {
3195            _psTokensReadForFiring = _dbType.getSQLSelect("port_event",
3196                "write_event_id", "write_event_id != -1 AND fire_id = ?");
3197        }
3198
3199        // write port events have write_id = -1
3200        if(_psTokensWrittenForFiring == null)
3201        {
3202            _psTokensWrittenForFiring = _dbType.getSQLSelect("port_event",
3203                "id", "write_event_id = -1 AND fire_id = ?");
3204        }
3205        
3206        if(_psActorNameForFiring == null)
3207        {
3208            String queryStr = "SELECT e.name " +
3209                "FROM " + actorFireStr + " af, " + entityStr + " e " +
3210                "WHERE af.actor_id = e.id AND af.id = ?";
3211            _psActorNameForFiring = _dbType.getPrepStatement(queryStr);
3212        }
3213
3214        if(_psActorTypeForFiring == null)
3215        {
3216            String queryStr = "SELECT a.class " +
3217                "FROM " + actorFireStr + " af, " + actorStr + " a " +
3218                "WHERE af.actor_id = a.id AND af.id = ?";
3219            _psActorTypeForFiring = _dbType.getPrepStatement(queryStr);
3220        }
3221
3222        if(_psParamNameValuesForFiring == null)
3223        {
3224            String queryStr = "SELECT name, value " +
3225                "FROM " + entityStr + " e, " + parameterStr + " p, " +
3226                parameterExecStr + " pe, " + actorFireStr + " af " +
3227                "WHERE e.type = 'parameter' AND e.id = p.id AND " +
3228                "e.name LIKE ? AND e.id = pe.parameter_id AND pe.wf_exec_id = af.wf_exec_id AND af.id = ?";
3229            _psParamNameValuesForFiring =
3230                _dbType.getPrepStatement(queryStr);
3231        }
3232        
3233        if(_psParamNameValuesForExecution == null)
3234        {
3235            String queryStr = "SELECT name, value " +
3236                "FROM " + entityStr + " e, " + parameterStr + " p, " +
3237                parameterExecStr + " pe " + 
3238                "WHERE pe.wf_exec_id = ? AND pe.parameter_id = e.id AND " +
3239                "pe.parameter_id = p.id";
3240            _psParamNameValuesForExecution =
3241                _dbType.getPrepStatement(queryStr);
3242        }
3243
3244        if(_psParamValueAtTimeForWorkflowLSID == null)
3245        {
3246            String queryStr = 
3247                "SELECT p.value, chg.time " +
3248                "FROM " + entityStr + " e, " + wfChangeStr + " chg, " +
3249                wfStr + " wf, " + parameterStr + " p " +
3250                "WHERE p.id = e.id AND e.name = ? AND " +
3251                "e.wf_change_id = chg.id AND wf.lsid = ? AND " +
3252                "wf.id = chg.wf_id AND chg.time <= ? " +
3253                "ORDER BY chg.time DESC";
3254            _psParamValueAtTimeForWorkflowLSID = _dbType.getPrepStatement(queryStr);
3255        }
3256        
3257        if(_psTokensForExecution == null)
3258        {
3259            // this returns all the tokens written for an execution.
3260            String queryStr = "SELECT DISTINCT(pe.write_event_id) " +
3261                "FROM " + portEventStr + " pe, " + actorFireStr + " af " +
3262                "WHERE pe.write_event_id != -1 AND pe.fire_id = af.id AND " +
3263                "af.wf_exec_id = ?";
3264            _psTokensForExecution = _dbType.getPrepStatement(queryStr);
3265        }
3266        
3267        if(_psTokensForFiring == null)
3268        {
3269            _psTokensForFiring = _dbType.getSQLSelect("port_event", "id",
3270                "fire_id = ?");
3271        }
3272        
3273        if(_psEntityTokensForExecution == null)
3274        {
3275            String readQueryStr = "SELECT pe.write_event_id " +
3276                "FROM " + portEventStr + " pe, " + actorFireStr + " af " +
3277                "WHERE pe.write_event_id != -1 AND pe.fire_id = af.id AND " +
3278                "af.wf_exec_id = ? and pe.port_id = ?";
3279            String writeQueryStr = "SELECT pe.id " +
3280                "FROM " + portEventStr + " pe, " + actorFireStr + " af " +
3281                "WHERE pe.write_event_id = -1 AND pe.fire_id = af.id AND " +
3282                "af.wf_exec_id = ? and pe.port_id = ?";
3283            _psEntityTokensForExecution = _dbType.getPrepStatement(readQueryStr + " UNION ALL " + writeQueryStr);
3284        }
3285
3286        if(_psLastExecutionForWorkflow == null)
3287        {
3288            String queryStr = "SELECT MAX(wfe.id) " +
3289                "FROM " + wfStr + " wf, " + wfExecStr + " wfe " +
3290                "WHERE wf.id = wfe.wf_id AND wf.name = ?";
3291            _psLastExecutionForWorkflow = _dbType.getPrepStatement(queryStr);
3292        }
3293
3294        if(_psLastExecutionForWorkflowLSID == null)
3295        {
3296            String queryStr = "SELECT MAX(wfe.id) " +
3297                "FROM " + wfStr + " wf, " + wfExecStr + " wfe " +
3298                "WHERE wf.id = wfe.wf_id AND wf.lsid = ?";
3299            _psLastExecutionForWorkflowLSID = _dbType.getPrepStatement(queryStr);
3300        }
3301
3302        if(_psLastExecutionLSIDForWorkflowName == null)
3303        {
3304            String queryStr = "SELECT wfe.lsid " +
3305             "FROM " + wfStr + " wf, " + wfExecStr + " wfe " +
3306             "WHERE wf.id = wfe.wf_id and wf.name = ? " +
3307             "ORDER BY wfe.id DESC";
3308            _psLastExecutionLSIDForWorkflowName = _dbType.getPrepStatement(queryStr);
3309        }
3310        
3311        if(_psLastExecutionLSIDForWorkflowLSID == null)
3312        {
3313            String queryStr = "SELECT wfe.lsid " +
3314             "FROM " + wfStr + " wf, " + wfExecStr + " wfe " +
3315             "WHERE wf.id = wfe.wf_id and wf.lsid = ? " +
3316             "ORDER BY wfe.id DESC";
3317            _psLastExecutionLSIDForWorkflowLSID = _dbType.getPrepStatement(queryStr);
3318        }
3319
3320        if(_psLastExecutionStartTime == null)
3321        {
3322            _psLastExecutionStartTime = _dbType.getPrepStatement(
3323                "SELECT MAX(wfe.start_time) " +
3324                "FROM " + wfExecStr + " wfe, " + wfStr + " wf " +
3325                "WHERE wf.id = wfe.wf_id AND wf.name = ? AND wfe.host_id = ?");
3326        }
3327
3328        if(_psLastExecutionStartTimeForWorkflowId == null)
3329        {
3330            _psLastExecutionStartTimeForWorkflowId = 
3331                _dbType.getSQLSelect("workflow_exec", "MAX(start_time)",
3332                    "wf_id = ? AND host_id = ?");
3333        }
3334
3335        if(_psLastChangeTime == null)
3336        {
3337            _psLastChangeTime = _dbType.getPrepStatement(
3338                "SELECT MAX(wfchange.time) " +
3339                "FROM " + wfChangeStr + " wfchange, " + wfStr + " wf " +
3340                "WHERE wf.id = wfchange.wf_id AND wf.name = ? AND " +
3341                "wfchange.host_id = ?");
3342        }
3343
3344        if(_psLastChangeTimeForWorkflowId == null)
3345        {
3346            _psLastChangeTimeForWorkflowId = 
3347                _dbType.getSQLSelect("workflow_change", "MAX(time)",
3348                "wf_id = ? AND host_id = ?");
3349        }
3350        
3351        if(_psPortEventData == null) {
3352            
3353            _psPortEventData = _dbType.getSQLSelect(portEventStr,
3354                    "data, data_id", "id = ?");            
3355        }
3356        
3357        if(_psDataContents == null)
3358        {
3359            _psDataContents = _dbType.getSQLSelect(dataStr, "contents", "md5 = ?");
3360        }
3361        
3362        if(_psTokenType == null)
3363        {
3364            _psTokenType = _dbType.getSQLSelect("port_event",
3365                "type", "id = ?");
3366        }
3367
3368        if(_psChannelForTokenRead == null) {
3369            _psChannelForTokenRead = _dbType.getSQLSelect("port_event",
3370                    "channel", "write_event_id = ? AND fire_id = ?");
3371        }
3372
3373        if(_psChannelForTokenWrite == null) {
3374            _psChannelForTokenWrite = _dbType.getSQLSelect("port_event",
3375                    "channel", "id = ?");
3376        }
3377        
3378        if(_psActorFiringsForTokenRead == null)
3379        {
3380            _psActorFiringsForTokenRead = 
3381                _dbType.getSQLSelect("port_event", "fire_id",
3382                    "write_event_id = ?");
3383        }
3384        
3385        if(_psActorFiringForTokenWrite == null)
3386        {
3387            _psActorFiringForTokenWrite = 
3388                _dbType.getSQLSelect("port_event", "fire_id", "id = ?");
3389        }
3390
3391        if(_psActorFiringsForExecution == null)
3392        {
3393            _psActorFiringsForExecution = _dbType.getSQLSelect("actor_fire",
3394                "id", "wf_exec_id = ?");
3395        }
3396
3397        if(_psMomlForExecutionId == null)
3398        {
3399            String queryStr = "SELECT d.contents " +
3400                "FROM " + dataStr + " d, " + wfExecStr + " wfe " +
3401                "WHERE wfe.id = ? AND d.md5 = wfe.wf_contents_id";
3402            _psMomlForExecutionId = _dbType.getPrepStatement(queryStr);
3403        }
3404        
3405        if(_psMomlForExecutionLSID == null)
3406        {
3407            String queryStr = "SELECT d.contents " +
3408                "FROM " + dataStr + " d, " + wfExecStr + " wfe " +
3409                "WHERE wfe.lsid = ? AND d.md5 = wfe.wf_contents_id";
3410            _psMomlForExecutionLSID = _dbType.getPrepStatement(queryStr);
3411        }
3412
3413 /* XXX no arrays for hsql 1.7.2
3414        if(_psMoMLMD5sForExecutions == null)
3415        {
3416            _psMoMLMD5sForExecutions = _dbType.getPrepStatement(
3417                "SELECT d.md5 " +
3418                "FROM " + dataStr + " d, " + wfExecStr + " wfexec " +
3419                "WHERE wfexec.wf_contents_id = d.id AND wfexec.id IN ( ? )");
3420        }
3421*/
3422        _psMoMLMD5sForExecutions = _dbType.getStatement();
3423
3424        //if(_psWorkflowRunsForExecutions == null)
3425        //{
3426        //    String queryStr = "SELECT wf.name, wfexec.user, wfexec.id, wfexec.start_time, wfexec.end_time FROM " +
3427           //        wfStr + " wf INNER JOIN " + wfExecStr + " wfexec " +
3428           //        "ON wf.id=wfexec.wf_id WHERE wfexec.id in (?)";
3429        //    _psWorkflowRunsForExecutions = _dbType.getPrepStatement(queryStr);
3430        //}
3431        
3432        //if(_psWorkflowRunsForExecutionLSIDs == null)
3433        //{
3434        //    String queryStr = "SELECT wf.name, wfexec.user, wfexec.id, wfexec.start_time, wfexec.end_time FROM " +
3435           //        wfStr + " wf INNER JOIN " + wfExecStr + " wfexec " +
3436           //        "ON wf.id=wfexec.wf_id WHERE wfexec.lsid in (?)";
3437        //    _psWorkflowRunsForExecutionLSIDs = _dbType.getPrepStatement(queryStr);
3438        //}
3439
3440        if(_psDataMD5s == null)
3441        {
3442            _psDataMD5s = _dbType.getSQLSelect("data", "md5");
3443        }
3444        
3445        if(_psEntityId == null)
3446        {
3447            String queryStr = "SELECT e.id FROM "
3448                + entityStr + " e, " 
3449                + wfStr + " w "
3450                + "WHERE e.wf_id = w.id "
3451                + "AND deleted = 0 "
3452                + "AND e.name = ? "
3453                + "AND w.name = ?";
3454            _psEntityId = _dbType.getPrepStatement(queryStr);
3455        }
3456
3457        if(_psEntityIdLSID == null)
3458        {
3459            String queryStr = "SELECT e.id FROM "
3460                + entityStr + " e, "
3461                + wfStr + " w "
3462                + "WHERE e.wf_id = w.id "
3463                + "AND deleted = " + _dbType.getFalseValue()
3464                + " AND e.name = ? "
3465                + "AND w.lsid = ?";
3466            _psEntityIdLSID = _dbType.getPrepStatement(queryStr);
3467        }
3468        
3469        if(_psEntityType == null)
3470        {
3471            String queryStr = "SELECT e.type FROM "
3472                + entityStr + " e " 
3473                + "WHERE e.id = ? "
3474                + "AND deleted = " + _dbType.getFalseValue();
3475            _psEntityType = _dbType.getPrepStatement(queryStr);
3476        }
3477        
3478        if(_psEntityWorkflowId == null)
3479        {
3480            String queryStr = "SELECT e.wf_id FROM "
3481                + entityStr + " e " 
3482                + "WHERE e.id = ? ";
3483            _psEntityWorkflowId = _dbType.getPrepStatement(queryStr);
3484        }
3485
3486        if(_psErrorForExecution == null)
3487        {
3488            String queryStr = "SELECT e.message FROM " +
3489                errorStr + " e, " +
3490                wfExecStr + " wfe " +
3491                "WHERE e.exec_id = wfe.id AND " +
3492                "wfe.lsid = ?";
3493            _psErrorForExecution = _dbType.getPrepStatement(queryStr);
3494        }
3495        
3496        if(_psExecutionsForType == null)
3497        {
3498            _psExecutionsForType = _dbType.getSQLSelect("workflow_exec", "lsid", "type = ?");
3499        }
3500
3501        if(_psDoesExecutionHaveError == null)
3502        {
3503            String queryStr = "SELECT e.id FROM " +
3504                errorStr + " e, " +
3505                wfExecStr + " wfe " +
3506                "WHERE e.exec_id = wfe.id AND " +
3507                "wfe.lsid = ?";
3508            _psDoesExecutionHaveError = _dbType.getPrepStatement(queryStr);
3509        }
3510
3511        if(_psIsExecutionImported == null)
3512        {
3513            String queryStr = "SELECT pe.parameter_id FROM " +
3514                parameterExecStr + " pe, " +
3515                wfExecStr + " wfe " +
3516                "WHERE pe.wf_exec_id = wfe.id AND " +
3517                "wfe.lsid = ?";
3518            _psIsExecutionImported = _dbType.getPrepStatement(queryStr);
3519        }
3520        
3521        if(_psPortsForActor == null)
3522        {
3523            String queryStr = "SELECT DISTINCT ent.name " +
3524                "FROM " + entityStr + " ent, " + wfStr + " wf " +
3525                "WHERE wf.lsid = ? AND ent.wf_id = wf.id AND " +
3526                "(ent.type = 'port' OR ent.type = 'portparameter') AND " +
3527                "ent.name LIKE ? ORDER BY ent.name ASC";
3528            _psPortsForActor = _dbType.getPrepStatement(queryStr);
3529        }
3530        
3531        if(_psHostIdForExecution == null)
3532        {
3533            String queryStr = "SELECT wfexec.host_id " +
3534                "FROM " + wfExecStr + " wfexec " +
3535                "WHERE wfexec.id = ?";
3536            
3537            //System.out.println("queryStr= "+queryStr);
3538            _psHostIdForExecution = _dbType.getPrepStatement(queryStr);
3539        }
3540        
3541        if(_psUserForExecution == null)
3542        {
3543            String queryStr = "SELECT wfexec." + userColumnStr +
3544                " FROM " + wfExecStr + " wfexec " +
3545                "WHERE wfexec.id = ?";
3546            
3547            //System.out.println("queryStr= "+queryStr);
3548            _psUserForExecution = _dbType.getPrepStatement(queryStr);
3549        } 
3550        
3551        if(_psUserForExecutionLSID == null) {
3552            String queryStr = "SELECT wfexec." + userColumnStr +
3553                    " FROM " + wfExecStr + " wfexec " +
3554                    "WHERE wfexec.lsid = ?";
3555                
3556            _psUserForExecutionLSID = _dbType.getPrepStatement(queryStr);            
3557        }
3558        
3559        if(_psOutputRoleToken == null)
3560        {
3561            String queryStr = "SELECT e.name " +
3562                "FROM " + entityStr + " e, " + portEventStr + " pe " +
3563                "WHERE e.id = pe.port_id AND pe.id = ?";
3564            _psOutputRoleToken = _dbType.getPrepStatement(queryStr);
3565        }   
3566        
3567        if(_psInputRoleToken == null)
3568        {
3569            String queryStr = "SELECT e.name " +
3570                "FROM " + entityStr + " e, " + portEventStr + " pe " +
3571                "WHERE e.id = pe.port_id AND pe.write_event_id = ? AND " +
3572                "pe.fire_id = ?";
3573            _psInputRoleToken = _dbType.getPrepStatement(queryStr);
3574        }    
3575        
3576        if(_psParamNameValuesOfSpecificTypeForExecution == null)
3577        {
3578                String queryStr = "SELECT name, value " +
3579                "FROM " + entityStr + " e, " + parameterStr + " p, " +
3580                parameterExecStr + " pe " + 
3581                "WHERE pe.wf_exec_id = ? AND pe.parameter_id = e.id AND " +
3582                "pe.parameter_id = p.id AND p.type LIKE '%Parameter'";
3583
3584                _psParamNameValuesOfSpecificTypeForExecution = _dbType.getPrepStatement(queryStr);
3585        }  
3586        
3587        if(_psTimestampsForActorFiring == null)
3588        {
3589            _psTimestampsForActorFiring = _dbType.getSQLSelect("actor_fire",
3590                "start_time, end_time", "id = ?");
3591        } 
3592        
3593        if(_psTimestampsForTokenRead == null) {
3594            _psTimestampsForTokenRead = _dbType.getSQLSelect("port_event",
3595                    "time", "write_event_id = ?");
3596        }
3597
3598        if(_psTimestampForTokenWrite == null) {
3599            _psTimestampForTokenWrite = _dbType.getSQLSelect("port_event",
3600                    "time", "id = ?");
3601        }
3602
3603        if(_psParamAndPortParamNameValuesForFiring == null)
3604        {
3605            String queryStr = "SELECT name, value " +
3606                "FROM " + entityStr + " e, " + parameterStr + " p, " +
3607                parameterExecStr + " pe, " + actorFireStr + " af " +
3608                "WHERE e.type LIKE '%parameter' AND e.id = p.id AND " +
3609                "e.name LIKE ? AND e.id = pe.parameter_id AND pe.wf_exec_id = af.wf_exec_id AND af.id = ?";
3610            
3611            _psParamAndPortParamNameValuesForFiring = _dbType.getPrepStatement(queryStr);
3612        } 
3613        
3614        if(_psActorFireId == null)
3615        {
3616            String queryStr = "SELECT a.id " +
3617                    "FROM " + entityStr + " e, " + actorFireStr + " a " +
3618                        "WHERE e.id = a.actor_id " +
3619                    "AND a.wf_exec_id = ? " +
3620                    "AND e.name = ?";
3621                
3622            _psActorFireId = _dbType.getPrepStatement(queryStr);        
3623        }
3624        
3625        if(_psActorFirings == null) {
3626            String queryStr = "SELECT a.id " +
3627                    "FROM " + actorFireStr + " a " +
3628                    "WHERE a.wf_exec_id = ? " +
3629                    "ORDER BY a.id ASC";
3630            _psActorFirings = _dbType.getPrepStatement(queryStr);
3631        }
3632
3633        if(_psActorExecutionTimes == null)
3634        {
3635            String queryStr = "SELECT af.id, af.start_time, af.end_time " +
3636                "FROM " + actorFireStr + " af, " + entityStr + " ent " +
3637                "WHERE ent.name = ? AND ent.id = af.actor_id AND af.wf_exec_id = ?";
3638            _psActorExecutionTimes = _dbType.getPrepStatement(queryStr);
3639        }
3640        
3641        if(_psPortIOBytesRead == null)
3642        {
3643            String queryStr = "SELECT pe.id, d.contents " +
3644                "FROM " + dataStr + " d, " + portEventStr + " pe, " +
3645                entityStr + " ent, " + actorFireStr + "  af " +
3646                "WHERE ent.name = ? AND ent.id = pe.port_id AND " +
3647                "af.id = pe.fire_id AND af.wf_exec_id = ? AND " +
3648                "d.md5 = pe.data_id AND pe.write_event_id != -1";
3649            
3650            _psPortIOBytesRead = _dbType.getPrepStatement(queryStr);
3651        }
3652        
3653        if(_psPortIOBytesWrite == null)
3654        {
3655            String queryStr = "SELECT pe.id, d.contents " +
3656                "FROM " + dataStr + " d, " + portEventStr + " pe, " +
3657                entityStr + " ent, " + actorFireStr + "  af " +
3658                "WHERE ent.name = ? AND ent.id = pe.port_id AND " +
3659                "af.id = pe.fire_id AND af.wf_exec_id = ? AND " +
3660                "d.md5 = pe.data_id AND pe.write_event_id = -1";
3661            
3662            _psPortIOBytesWrite = _dbType.getPrepStatement(queryStr);
3663        }
3664        
3665        if(_psAssociatedKeysValuesForExecution == null) {
3666            String queryStr = "SELECT name, val " +
3667                "FROM " + associatedDataStr +
3668                " WHERE data_id is NULL and wf_exec_id = ?";
3669            _psAssociatedKeysValuesForExecution = _dbType.getPrepStatement(queryStr);
3670        }
3671    }
3672
3673    /** Get the entities for a workflow change. */
3674    protected List<Integer> _getEntitiesForChange(int changeId)
3675        throws QueryException
3676    {
3677        try
3678        {
3679            _psEntitiesForChange.setInt(1, changeId);
3680            return _getIntResults(_psEntitiesForChange, 1);
3681        }
3682        catch(SQLException e)
3683        {
3684            throw new QueryException("Error querying entities for " +
3685                "change: ", e);
3686        }
3687    }
3688
3689    /** Get the last change time for a workflow. */
3690    protected Date _getLastChangeTime(String workflowName, String hostName)
3691        throws QueryException
3692    {   
3693        if(workflowName == null || hostName == null)        
3694        {
3695            throw new QueryException("not implemented yet."); 
3696        }
3697        
3698        try
3699        {
3700            Date retval = null;
3701
3702            _psLastChangeTime.setString(1, workflowName); 
3703            _psLastChangeTime.setString(2, hostName); 
3704            
3705            ResultSet result = null;
3706            try
3707            {
3708                result = _psLastChangeTime.executeQuery();
3709                if(result.next())
3710                {
3711                    retval = result.getTimestamp(1);
3712                }
3713            }
3714            finally
3715            {
3716                if(result != null)
3717                {
3718                    result.close();
3719                }
3720            }
3721            return retval;
3722        }
3723        catch(SQLException e)
3724        {
3725            throw new QueryException("Error querying last change time: ", e);
3726        }
3727    }
3728
3729    /** Get the last change time for a workflow. */
3730    protected Date _getLastChangeTime(int workflowId, String hostName)
3731        throws QueryException
3732    {
3733        if(hostName == null)        
3734        {
3735            throw new QueryException("not implemented yet."); 
3736        }
3737
3738        try
3739        {
3740            Date retval = null;
3741
3742            _psLastChangeTimeForWorkflowId.setInt(1, workflowId); 
3743            _psLastChangeTimeForWorkflowId.setString(2, hostName); 
3744
3745            ResultSet result = null;
3746            try
3747            {
3748                result = _psLastChangeTimeForWorkflowId.executeQuery();
3749                if(result.next())
3750                {
3751                    retval = result.getTimestamp(1);
3752                }
3753            }
3754            finally
3755            {
3756                if(result != null)
3757                {
3758                    result.close();
3759                }
3760            }
3761            return retval;
3762        }
3763        catch(SQLException e)
3764        {
3765            throw new QueryException("Error querying last change time: ", e);
3766        }
3767    }
3768
3769    /** Get changes performed since a specific time. */
3770    protected List<Integer> _getChangesSince(Date timeStamp,
3771        /* XXX boolean including */ String workflowName, String hostName)
3772        throws QueryException
3773    {
3774        Timestamp startTime = null;
3775        if(timeStamp != null)
3776        {
3777            startTime = new Timestamp(timeStamp.getTime());
3778        }
3779        return _getChangesForTimespan(startTime, _maxEndTime, workflowName,
3780            hostName);
3781    }
3782
3783    /** Get changes performed on a host within a timespan. If host is null,
3784     *  returns changes for all hosts.
3785     */
3786    protected List<Integer> _getChangesForTimespan(Date start,
3787        Date end, String workflow, String host)
3788        throws QueryException
3789    {
3790
3791        // sanity checks
3792        if(start == null)
3793        {
3794            start = _minStartTime;
3795        }
3796
3797        if(end == null)
3798        {
3799            end = _maxEndTime;
3800        }
3801
3802        if(start.after(end))
3803        {
3804            throw new QueryException("Start time " + start + " is after " +
3805                " end time " + end);
3806        }
3807
3808        try
3809        {
3810            PreparedStatement query;
3811
3812            if(host == null)
3813            {
3814                query = _psChangesForTimespan;
3815            }
3816            else if(workflow == null)
3817            {
3818                throw new QueryException("not implemented yet");
3819            }
3820            else // host != null && workflow != null
3821            {
3822                query = _psChangesForTimespanOnHost;
3823                query.setString(3, host);
3824                query.setString(4, workflow);
3825            }
3826
3827            query.setTimestamp(1, new Timestamp(start.getTime()));
3828            query.setTimestamp(2, new Timestamp(end.getTime()));
3829
3830            return _getIntResults(query, 1);
3831        }
3832        catch(SQLException e)
3833        {
3834            throw new QueryException("Unable to query changes: ", e);
3835        }
3836    }
3837
3838    /** Get the last execution start time. */
3839    protected Date _getLastExecutionStartTime(String workflowName,
3840        String hostName) throws QueryException
3841    {
3842        if(workflowName == null || hostName == null) 
3843        {
3844            throw new QueryException("not implemented yet.");
3845        }
3846
3847        try
3848        {
3849            Timestamp retval = null;
3850
3851            _psLastExecutionStartTime.setString(1, workflowName);
3852            _psLastExecutionStartTime.setString(2, hostName);
3853            
3854            ResultSet result = null;
3855            try
3856            {
3857                result = _psLastExecutionStartTime.executeQuery();
3858                if(result.next())
3859                {
3860                    retval = result.getTimestamp(1);
3861                }
3862            }
3863            finally
3864            {
3865                if(result != null)
3866                {
3867                    result.close();
3868                }
3869            }
3870            return retval;
3871        }
3872        catch(SQLException e)
3873        {
3874            throw new QueryException("Error querying last execution start " +
3875                "time: ", e);
3876        }
3877    }
3878
3879    /** Get the last execution start time. */
3880    protected Date _getLastExecutionStartTime(int workflowId,
3881        String hostName) throws QueryException
3882    {
3883        if(hostName == null)
3884        {
3885            throw new QueryException("not implemented yet.");
3886        }
3887
3888        try
3889        {
3890            Timestamp retval = null;
3891
3892            _psLastExecutionStartTimeForWorkflowId.setInt(1, workflowId);
3893            _psLastExecutionStartTimeForWorkflowId.setString(2, hostName);
3894
3895            ResultSet result = null;
3896            try
3897            {
3898                result = 
3899                    _psLastExecutionStartTimeForWorkflowId.executeQuery();
3900                if(result.next())
3901                {
3902                    retval = result.getTimestamp(1);
3903                }
3904            }
3905            finally
3906            {
3907                if(result != null)
3908                {
3909                    result.close();
3910                }
3911            }
3912            return retval;
3913        }
3914        catch(SQLException e)
3915        {
3916            throw new QueryException("Error querying last execution start " +
3917                "time: ", e);
3918        }
3919    }
3920
3921    /** Get executions started since a specific time. 
3922     *  @param timeStamp get executions started after this time.
3923     *  @param including if true, include executions starting at timeStamp.
3924     *  if false, do not include executions starting at timeStamp.
3925     *  @param workflowName get executions for a specific workflow.
3926     *  @param hostName get executions for a specific host.
3927     */
3928    protected List<Integer> _getExecutionsStartedSince(Date timeStamp,
3929        boolean including, String workflowName, String hostName) 
3930        throws QueryException
3931    {
3932        Timestamp startTime = null;
3933        if(timeStamp != null)
3934        {
3935            if(including)
3936            {
3937                startTime = new Timestamp(timeStamp.getTime());
3938            }
3939            else
3940            {
3941                startTime = new Timestamp(timeStamp.getTime() + 1);
3942            }
3943        }
3944        return _getExecutionsForTimespan(startTime, _maxEndTime, workflowName,
3945            hostName);
3946    }
3947
3948    /** Get executions for a worfkflow run on a host within a timespan.  */
3949    protected List<Integer> _getExecutionsForTimespan(Date start,
3950        Date end, String workflow, String host) throws QueryException
3951    {
3952        
3953        // sanity checks
3954        if(start == null)
3955        {
3956            start = _minStartTime;
3957            //_debug("setting start time to min");
3958        }
3959
3960        if(end == null)
3961        {
3962            end = _maxEndTime;
3963            //_debug("setting end time to max");
3964        }
3965
3966        if(start.after(end))
3967        {
3968            throw new QueryException("Start time " + start + " is after " +
3969                " end time " + end);
3970        }
3971
3972        //_debug("start = " + start + " | end = " + end);
3973
3974        try
3975        {
3976            PreparedStatement query;
3977            if(host == null)
3978            {
3979                query = _psExecutionsForTimespan;
3980            }
3981            else if(workflow == null)
3982            {
3983                throw new QueryException("not implemented yet");
3984            }
3985            else // host != null && workflow != null
3986            {
3987                query = _psExecutionsForTimespanOnHost;
3988                query.setString(3, host);
3989                query.setString(4, workflow);
3990
3991            }
3992
3993            query.setTimestamp(1, new Timestamp(start.getTime()));
3994            query.setTimestamp(2, new Timestamp(end.getTime()));
3995
3996            return _getIntResults(query, 1);
3997        }
3998        catch(SQLException e)
3999        {
4000            throw new QueryException("Unable to query executions for " +
4001            "timespan " + start + " - " + end + " :", e);
4002        }
4003    }
4004
4005    /** Get actor firings for an execution. */
4006    /*
4007    protected List<Integer> _getActorFiringsForExecution(int execId)
4008        throws QueryException
4009    {
4010        try        
4011        {
4012            _psActorFiringsForExecution.setInt(1, execId);
4013            return _getIntResults(_psActorFiringsForExecution, 1);
4014        }
4015        catch(SQLException e)
4016        {
4017            throw new QueryException("Error querying actor firings for " +
4018                "execution: ", e);
4019        }
4020    }
4021    */
4022
4023    /** Get the id for a workflow. If workflow does not exist, returns an
4024     *  an integer less than 0.
4025     */
4026    protected int _getWorkflow(String workflow) throws QueryException
4027    {
4028        int retval = -1;
4029
4030        try
4031        {
4032            ResultSet result = null;
4033            try
4034            {
4035                result = _psWorkflows.executeQuery();
4036                while(result.next())
4037                {
4038                    if(result.getString("name").equals(workflow))
4039                    {
4040                        retval = result.getInt("id");
4041                        break;
4042                    }
4043                }
4044            }
4045            finally
4046            {
4047                if(result != null)
4048                {
4049                    result.close();
4050                }
4051            }
4052        }
4053        catch(SQLException e)
4054        {
4055            throw new QueryException("Unable to query workflow: ", e);
4056        }
4057        return retval;
4058    }
4059    
4060    /*
4061    protected List<Integer> _getTokensForFiring(int fireId)
4062        throws QueryException
4063    {
4064        try 
4065        {
4066            _psTokensForFiring.setInt(1, fireId);
4067            return _getIntResults(_psTokensForFiring, 1);
4068        }
4069        catch(SQLException e)
4070        {
4071            throw new QueryException("Error querying tokens for firing: ", e);
4072        }
4073    }
4074    */
4075
4076    /** Get all the data MD5 checksums. */
4077    protected List<String> _getDataMD5s() throws QueryException
4078    {
4079        try
4080        {
4081            return _getStringResults(_psDataMD5s, 1);
4082        }
4083        catch(SQLException e)
4084        {
4085            throw new QueryException("Unable to query data md5s: ", e);
4086        }
4087    }
4088
4089    /** Get a list of MoML MD5 checksums for a set of execution ids.*/
4090    /* XXX need Arrays
4091    protected List<String> _getMoMLMD5sForExecutions(List<Integer> execList)
4092        throws QueryException
4093    {
4094        try
4095        {
4096            String dataStr = _dbType.getTableName("data");
4097            String wfExecStr = _dbType.getTableName("workflow_exec");
4098            String queryStr = "SELECT DISTINCT d.md5 " +
4099                "FROM " + dataStr + " d, " + wfExecStr + " wfexec " +
4100                "WHERE wfexec.wf_contents_id = d.md5 AND wfexec.id IN ( " +
4101                StringUtil.listToString(execList) + " )";
4102           
4103            //_debug(queryStr);
4104
4105            return _getStringResults(_psMoMLMD5sForExecutions, queryStr, 1);
4106
4107            // XXX no arrays
4108            // _psMoMLMD5sForExecutions.setString(1, buf.toString());
4109            //return _getStringResults(_psMoMLMD5sForExecutions, 1);
4110        }
4111        catch(SQLException e)
4112        {
4113            throw new QueryException("Unable to query moml md5s for " +
4114                "executions: ", e);
4115        }
4116    }
4117    */
4118
4119    /** Convenience routine to get a list of integer SQL results.
4120     *  @param query the query to execute
4121     *  @param index column number of result
4122     */
4123    protected List<Integer> _getIntResults(PreparedStatement query, int index)
4124        throws SQLException
4125    {
4126        List<Integer> retval = new LinkedList<Integer>();
4127        try(ResultSet result = query.executeQuery();)
4128        {
4129            /*
4130            java.sql.ResultSetMetaData md = result.getMetaData();
4131            for(int i = 1; i <= md.getColumnCount(); i++)
4132            {
4133                _debug("column " + i + " = " + md.getColumnName(i));
4134            }
4135            */
4136
4137            while(result.next())
4138            {
4139                //_debug("got result row; adding " + result.getInt(index));
4140                retval.add(result.getInt(index));
4141            }
4142        }
4143        return retval;
4144    }
4145   
4146    /** A convenience method that executes a query returning parameter name
4147     *  value pairs.
4148     */
4149    protected static Map<String,String> _getParameterNameValues(PreparedStatement statement)
4150        throws QueryException, SQLException
4151    {
4152        // use a linked map for predictable iteration order
4153        Map<String,String> retval = new LinkedHashMap<String,String>();
4154        ResultSet result = null;
4155
4156        try
4157        {
4158            result = statement.executeQuery();
4159            while(result.next())
4160            {
4161                String name = result.getString("name");
4162                String value = result.getString("value");
4163
4164                if(name == null)
4165                {
4166                    throw new QueryException("No name column.");
4167                }
4168
4169                if(value == null)
4170                {
4171                    // NOTE: oracle returns null for empty strings
4172                    value = "";
4173                }
4174
4175                retval.put(name, value);
4176            }
4177        }
4178        catch(SQLException e)
4179        {
4180            throw new QueryException("Unable to get name value pairs: ", e);
4181        }
4182        finally
4183        {
4184            if(result != null)
4185            {
4186                result.close();
4187            }
4188        }
4189
4190        return retval;
4191    }
4192 
4193    /** Convenience routine to get a list of string SQL results. 
4194     *  @param query the query to execute
4195     *  @param index column number of result
4196     */
4197    protected List<String> _getStringResults(PreparedStatement query, int index)
4198        throws SQLException
4199    {
4200        List<String> retval = new LinkedList<String>();
4201        ResultSet result = null;
4202        try
4203        {
4204            result = query.executeQuery();
4205            while(result.next())
4206            {
4207                retval.add(result.getString(index));
4208            }
4209        }
4210        finally
4211        {
4212            if(result != null)
4213            {
4214                result.close();
4215            }
4216        }
4217        return retval;
4218    }
4219
4220    /** Convenience routine to get a list of string SQL results.
4221     *  @param statement statement to execute
4222     *  @param queryStr the query string to execute
4223     *  @param index column number of result
4224     */
4225    protected List<String> _getStringResults(Statement statement,
4226        String queryStr, int index) throws SQLException
4227    {
4228        List<String> retval = new LinkedList<String>();
4229        ResultSet result = null;
4230        try
4231        {
4232            result = statement.executeQuery(queryStr);
4233            while(result.next())
4234            {
4235                retval.add(result.getString(index));
4236            }
4237        }
4238        finally
4239        {
4240            if(result != null)
4241            {
4242                result.close();
4243            }
4244        }
4245        return retval;
4246    }
4247
4248
4249    /** Set our prepared statements to null. */
4250    protected void _nullPreparedStatements()
4251    {
4252
4253        _psActorFiringForTokenWrite = null;
4254        _psActorFiringsForExecution = null;
4255        _psActorFiringsForTokenRead = null;
4256        _psActorNameForFiring = null;
4257        _psActorTypeForFiring = null;
4258        _psChangesForTimespan = null;
4259        _psChangesForTimespanOnHost = null;
4260        _psDataMD5s = null;
4261        _psEntitiesForChange = null;
4262        _psEntityId = null;
4263        _psEntityIdLSID = null;
4264        _psEntityTokensForExecution = null;
4265        _psErrorForExecution = null;
4266        _psExecutions = null;
4267        _psExecutionsForAnnotation = null;
4268        _psExecutionsForTimespan = null;
4269        _psExecutionsForTimespanOnHost = null;
4270        _psExecutionsForWorkflow = null;
4271        _psExecutionsForWorkflowNameContains = null;
4272        _psExecutionsForWorkflowRuns = null;
4273        _psExecutionsForWorkflowRunsAfter = null;
4274        _psExecutionsForWorkflowRunsAt = null;
4275        _psExecutionsForWorkflowRunsBefore = null;
4276        _psExecutionsForWorkflowRuns2 = null;
4277        _psExecutionLSIDsForWorkflowRuns = null;
4278        _psExecutionIdsForTags = null;
4279        _psExecutionLSIDs = null;
4280        _psTagsForExecutionId = null;
4281        _psTagURNsForExecutionId = null;
4282        _psTypeForTag = null;
4283        _psLastChangeTime = null;
4284        _psLastChangeTimeForWorkflowId = null;
4285        _psLastExecutionForWorkflow = null;
4286        _psLastExecutionForWorkflowLSID = null;
4287        _psLastExecutionLSIDForWorkflowName = null;
4288        _psLastExecutionStartTime = null;
4289        _psLastExecutionStartTimeForWorkflowId = null;
4290        _psMoMLMD5sForExecutions = null;
4291        _psMomlForExecutionId = null;
4292        _psParamNameValuesForExecution = null;
4293        _psParamNameValuesForFiring = null;
4294        _psParamValueAtTimeForWorkflowLSID = null;
4295        _psTimestampsForExecution = null;
4296        _psTokenType = null;
4297        _psDataContents = null;
4298        _psPortEventData = null;
4299        _psTokensForExecution = null;
4300        _psTokensForFiring = null;
4301        _psTokensReadForFiring = null;
4302        _psTokensWrittenForFiring = null;
4303        _psWorkflows = null;
4304        _psWorkflowNameForLSID = null;
4305        _psWorkflowNameForExecutionLSID = null;
4306        _psPortsForActor = null;
4307        _psDoesExecutionHaveError = null;
4308        _psExecutionsForType = null;
4309        _psIsExecutionImported = null;        
4310        _psUserForExecution = null;
4311        _psUserForExecutionLSID = null;
4312        _psHostIdForExecution = null;
4313        _psOutputRoleToken = null;
4314        _psInputRoleToken = null;
4315        _psParamNameValuesOfSpecificTypeForExecution = null;
4316        _psTimestampsForActorFiring = null;
4317        _psParamAndPortParamNameValuesForFiring = null;
4318        _psActorFireId = null;
4319        _psActorFirings = null;
4320        _psActorExecutionTimes = null;
4321        _psPortIOBytesRead = null;
4322        _psPortIOBytesWrite = null;
4323        //_psIOLinkRead = null;
4324        _psTimestampsForTokenRead = null;
4325        _psTimestampForTokenWrite = null;
4326        _psAssociatedKeysValuesForExecution = null;
4327    }
4328
4329    protected void _debug(String str)
4330    {
4331        System.out.println("DEBUG: " + str);
4332    }
4333
4334    /** Returns true since SQLRecordingV8 saves StringToken values
4335     *  using StringToken.toString(), which adds surrounding
4336     *  double-quotes.
4337     */
4338    @Override
4339    protected boolean _stringTokenValuesHaveSurroundingQuotes() {
4340        return true;
4341    }
4342
4343    ///////////////////////////////////////////////////////////////////////
4344    //// protected fields                                              ////
4345
4346    /** The connection to the database. */
4347    protected DatabaseType _dbType;
4348
4349    // jdbc prepared statements for queries
4350
4351    protected PreparedStatement _psActorFiringForTokenWrite;
4352    protected PreparedStatement _psActorFiringsForExecution;
4353    protected PreparedStatement _psActorFiringsForTokenRead;
4354    protected PreparedStatement _psActorFireId;
4355    protected PreparedStatement _psActorFirings;
4356    protected PreparedStatement _psActorNameForFiring;
4357    protected PreparedStatement _psActorTypeForFiring;
4358    protected PreparedStatement _psChannelForTokenRead;
4359    protected PreparedStatement _psChannelForTokenWrite;
4360    protected PreparedStatement _psChangesForTimespan;
4361    protected PreparedStatement _psChangesForTimespanOnHost;
4362    protected PreparedStatement _psDataMD5s;
4363    protected PreparedStatement _psDoesExecutionHaveError;
4364    protected PreparedStatement _psEntitiesForChange;
4365    protected PreparedStatement _psEntityId;
4366    protected PreparedStatement _psEntityWorkflowId;
4367    protected PreparedStatement _psEntityIdLSID;
4368    protected PreparedStatement _psEntityTokensForExecution;
4369    protected PreparedStatement _psEntityType;
4370    protected PreparedStatement _psErrorForExecution;
4371    protected PreparedStatement _psExecutions;
4372    protected PreparedStatement _psExecutionsForAnnotation;
4373    protected PreparedStatement _psExecutionsForTimespan;
4374    protected PreparedStatement _psExecutionsForTimespanOnHost;
4375    protected PreparedStatement _psExecutionsForType;
4376    protected PreparedStatement _psExecutionsForWorkflow;
4377    protected PreparedStatement _psExecutionForExecutionLSID;
4378    protected PreparedStatement _psExecutionForExecutionLSIDWithoutRevision;
4379    protected PreparedStatement _psExecutionForOldestReferralExecutionLSIDWithoutRevision;
4380    protected PreparedStatement _psExecutionLSIDForExecution;
4381    protected PreparedStatement _psExecutionsForWorkflowNameContains;
4382    protected PreparedStatement _psExecutionsForWorkflowRuns;
4383    protected PreparedStatement _psExecutionsForWorkflowRunsAfter;
4384    protected PreparedStatement _psExecutionsForWorkflowRunsAt;
4385    protected PreparedStatement _psExecutionsForWorkflowRunsBefore;
4386    protected PreparedStatement _psExecutionsForWorkflowRuns2;
4387    protected PreparedStatement _psExecutionLSIDsForWorkflowRuns;
4388    protected PreparedStatement _psExecutionIdsForTags;
4389    protected PreparedStatement _psExecutionLSIDs;
4390    protected PreparedStatement _psIsExecutionImported;
4391    protected PreparedStatement _psTagsForExecutionId;
4392    protected PreparedStatement _psTagURNsForExecutionId;
4393    protected PreparedStatement _psTypeForTag;
4394    protected PreparedStatement _psLastChangeTime;
4395    protected PreparedStatement _psLastChangeTimeForWorkflowId;
4396    protected PreparedStatement _psLastExecutionForWorkflow;
4397    protected PreparedStatement _psLastExecutionForWorkflowLSID;
4398    protected PreparedStatement _psLastExecutionLSIDForWorkflowName;
4399    protected PreparedStatement _psLastExecutionLSIDForWorkflowLSID;
4400    protected PreparedStatement _psLastExecutionStartTime;
4401    protected PreparedStatement _psLastExecutionStartTimeForWorkflowId;
4402    protected PreparedStatement _psMomlForExecutionId;
4403    protected PreparedStatement _psMomlForExecutionLSID;
4404    protected PreparedStatement _psParamNameValuesForExecution;
4405    protected PreparedStatement _psParamNameValuesForFiring;
4406    protected PreparedStatement _psParamValueAtTimeForWorkflowLSID;
4407    protected PreparedStatement _psTimestampsForExecution;
4408    protected PreparedStatement _psTimestampForTokenWrite;
4409    protected PreparedStatement _psTimestampsForTokenRead;
4410    protected PreparedStatement _psTokenType;
4411    protected PreparedStatement _psPortEventData;
4412    protected PreparedStatement _psDataContents;
4413    protected PreparedStatement _psTokensForExecution;
4414    protected PreparedStatement _psTokensForFiring;
4415    protected PreparedStatement _psTokensReadForFiring;
4416    protected PreparedStatement _psTokensWrittenForFiring;
4417    protected PreparedStatement _psWorkflowRunsForExecutions;
4418    protected PreparedStatement _psWorkflowRunsForExecutionLSIDs;
4419    protected PreparedStatement _psWorkflows;
4420    protected PreparedStatement _psWorkflowNameForLSID;
4421    protected PreparedStatement _psWorkflowNameForExecutionLSID;
4422    protected Statement _psMoMLMD5sForExecutions;
4423    protected PreparedStatement _psPortsForActor;
4424    protected PreparedStatement _psHostIdForExecution;
4425    protected PreparedStatement _psUserForExecution;
4426    protected PreparedStatement _psUserForExecutionLSID;
4427    protected PreparedStatement _psOutputRoleToken;
4428    protected PreparedStatement _psInputRoleToken;
4429    protected PreparedStatement _psParamNameValuesOfSpecificTypeForExecution;
4430    protected PreparedStatement _psTimestampsForActorFiring;
4431    protected PreparedStatement _psParamAndPortParamNameValuesForFiring;
4432    protected PreparedStatement _psActorExecutionTimes;
4433    protected PreparedStatement _psPortIOBytesRead;
4434    protected PreparedStatement _psPortIOBytesWrite;
4435    protected PreparedStatement _psAssociatedKeysValuesForExecution;
4436    
4437    //protected PreparedStatement _psIOLinkRead;
4438
4439    /** The most futuristic Timestamp possible! */
4440    protected static final Timestamp _maxEndTime = new Timestamp(Long.MAX_VALUE);
4441
4442    /** The earliest Timestamp possible. */
4443    protected static final Timestamp _minStartTime = new Timestamp(0);
4444    
4445    ///////////////////////////////////////////////////////////////////////
4446    //// private methods                                               ////
4447
4448    /** If the name of the workflow is Unnamed, change the name to the
4449     *  name stored in the workflow table.
4450     */
4451    private String _changeUnnamedWorkflow(String workflowStr)
4452        throws QueryException
4453    {
4454        String retval = workflowStr;
4455
4456        String wfName = null;
4457        Element rootElement = null;
4458        Document jdomDoc = null;
4459        
4460        try
4461        {
4462            // convert to xml and get the top level name
4463            XMLParser parser = new XMLParser();
4464            org.w3c.dom.Document doc = parser.parser(workflowStr);
4465            jdomDoc = _domBuilder.build(doc);
4466            rootElement = jdomDoc.getRootElement();
4467            wfName = rootElement.getAttributeValue("name");
4468        }
4469        catch(Exception e)
4470        {
4471            throw new QueryException("XML error: ", e);
4472        }
4473
4474        if(wfName == null)
4475        {
4476            throw new QueryException("No name found in workflow.");
4477        }
4478        
4479        //System.out.println("name = " + wfName);
4480        
4481        Matcher matcher = RenameUtil.unnamedIdPattern.matcher(wfName);
4482        if(matcher.matches())
4483        {
4484            //System.out.println("workflow is unnamed");
4485            
4486            // get the lsid
4487            List<Element> children = rootElement.getChildren("property");
4488            if(children.size() == 0)
4489            {
4490                throw new QueryException("No properties in workflow.");
4491            }
4492            
4493            String lsidStr = null;
4494            for(Element element : children)
4495            {
4496                String name = element.getAttributeValue("name");
4497                if(name != null && name.equals(NamedObjId.NAME))
4498                {
4499                    lsidStr = element.getAttributeValue("value");
4500                    break;
4501                }
4502            }
4503            
4504            if(lsidStr == null)
4505            {
4506                throw new QueryException("Could not find LSID in workflow.");
4507            }
4508            
4509            //System.out.println("lsid = " + lsidStr);
4510            
4511            KeplerLSID lsid = null;
4512            try
4513            {
4514                lsid = new KeplerLSID(lsidStr);
4515            }
4516            catch(Exception e)
4517            {
4518                throw new QueryException("Error creating LSID: " + e);
4519            }
4520            
4521            // get the name from the workflow table.
4522            String newName = getWorkflowName(lsid);
4523            
4524            // if it's different replace it
4525            if(!wfName.equals(newName))
4526            {
4527                rootElement.setAttribute("name", newName);
4528                retval = _outputter.outputString(jdomDoc);
4529            }
4530        }
4531
4532        return retval;
4533    }
4534        
4535    /** Used to convert w3c DOM to JDOM. */
4536    private DOMBuilder _domBuilder = new DOMBuilder();
4537    
4538    /** Used to convert XML to string. */
4539    private XMLOutputter _outputter = new XMLOutputter();
4540    
4541    private static final int numMillisecondsInASecond = 1000;
4542    
4543}