001/*
002 * Copyright (c) 2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2016-02-16 22:50:54 +0000 (Tue, 16 Feb 2016) $' 
007 * '$Revision: 34438 $'
008 * 
009 * Permission is hereby granted, without written agreement and without
010 * license or royalty fees, to use, copy, modify, and distribute this
011 * software and its documentation for any purpose, provided that the above
012 * copyright notice and the following two paragraphs appear in all copies
013 * of this software.
014 *
015 * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY
016 * FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
017 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
018 * THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF
019 * SUCH DAMAGE.
020 *
021 * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES,
022 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
023 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE
024 * PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF
025 * CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES,
026 * ENHANCEMENTS, OR MODIFICATIONS.
027 *
028 */
029
030package org.kepler.workflow;
031
032import java.awt.event.WindowAdapter;
033import java.awt.event.WindowEvent;
034import java.lang.ref.WeakReference;
035import java.util.HashMap;
036import java.util.LinkedList;
037import java.util.List;
038import java.util.Map;
039
040import org.apache.commons.logging.Log;
041import org.apache.commons.logging.LogFactory;
042import org.kepler.gui.ModelToFrameManager;
043import org.kepler.moml.NamedObjId;
044import org.kepler.moml.NamedObjIdChangeRequest;
045import org.kepler.moml.NamedObjIdReferralList;
046import org.kepler.objectmanager.ObjectManager;
047import org.kepler.objectmanager.lsid.KeplerLSID;
048import org.kepler.reporting.roml.ReportLayout;
049
050import ptolemy.actor.gui.TableauFrame;
051import ptolemy.kernel.util.ChangeListener;
052import ptolemy.kernel.util.ChangeRequest;
053import ptolemy.kernel.util.NamedObj;
054
055/**
056 * This singleton class manages a list of workflows.
057 * TODO add more information about this class and comment all methods
058 */
059public class WorkflowManager implements ChangeListener {
060        private static final Log log = LogFactory.getLog(WorkflowManager.class);
061
062        private static WorkflowManager instance;
063        private static TableauFrame _rankingTableauFrame = null;
064        
065        public static WorkflowManager getInstance() {
066                if (instance == null) {
067                        instance = new WorkflowManager();
068                }
069                return instance;
070        }
071        
072        private Map<String, Workflow> workflows = new HashMap<String, Workflow>();
073        
074        /**
075         * 
076         * @param workflowId
077         * @return
078         */
079        public Workflow getWorkflow(TableauFrame tableauFrame, KeplerLSID workflowId) {
080                intialize(tableauFrame, workflowId);
081                String key = workflowId.toStringWithoutRevision();
082                Workflow wf = workflows.get(key);
083                return wf;
084        }
085        
086        /**
087         * Return a List of LSIDS that are associated with the given TableauFrame.
088         * Because the map stores lsids without revisions the lsids returned here
089         * are always revision 1.
090         * 
091         * @param tableauFrame
092         * @return
093         */
094        public List<KeplerLSID> getKeplerLSID(TableauFrame tableauFrame) {
095                List<KeplerLSID> lsids = new LinkedList<KeplerLSID>();
096                for(String lsid : workflows.keySet()) {
097                        boolean found = false;
098                        Workflow w = workflows.get(lsid);
099                        Map<TableauFrame, ReportLayout> map = w.getReportLayouts();
100                        for(TableauFrame frame : map.keySet()) {
101                                if (frame == tableauFrame) {
102                                        found = true;
103                                }
104                        }
105                        if (found) {
106                                try {
107                                        // need to add revision here, kludgy
108                                        String fullLsid = lsid+":1";
109                                        KeplerLSID klsid = new KeplerLSID(fullLsid);
110                                        if (!lsids.contains(klsid)) {
111                                                lsids.add(klsid);
112                                        }
113                                } catch (Exception e) {
114                                        e.printStackTrace();
115                                }
116                        }
117                }
118                return lsids;
119        }
120        
121        /**
122         * 
123         * @param workflowId
124         * @return
125         */
126        public Workflow removeWorkflow(KeplerLSID workflowId) {
127                String key = workflowId.toStringWithoutRevision();
128                Workflow wf = workflows.remove(key);
129                if(wf != null) {
130                    wf._model.removeChangeListener(this);
131                } else {
132                    System.err.println("WARNING: workflow not found for LSID " + workflowId);
133                try {
134                        ObjectManager om = ObjectManager.getInstance();
135                        NamedObj namedObj = om.getObjectRevision(workflowId);
136                        if (namedObj != null) {
137                                namedObj.removeChangeListener(this);
138                        }
139                } catch (Exception e) {
140                        e.printStackTrace();
141                }
142                }
143                return wf;
144        }
145        
146        
147        /**
148         * 
149         * @param workflowId
150         * @return
151         */
152        public boolean workflowExists(KeplerLSID workflowId) {
153                String key = workflowId.toStringWithoutRevision();
154                return workflows.containsKey(key);
155        }
156        
157        /**
158         * 
159         * @param tableauFrame
160         * @param lsid
161         */
162        private void intialize(TableauFrame tableauFrame, KeplerLSID lsid) {
163                String key = lsid.toStringWithoutRevision();
164                Workflow workflow = null;
165                if (workflows.get(key) == null) {
166                        
167                        // add a listener so we can remove it when the frame closes
168                        WorkflowClosingAdapter listener = new WorkflowClosingAdapter(tableauFrame);
169                        // tableauFrame is null when running from command line
170                        if(tableauFrame != null) {
171                                tableauFrame.addWindowListener(listener);
172                        }
173                        
174                        workflow = new Workflow(tableauFrame, lsid);
175                        workflows.put(key, workflow);
176                }
177                // make sure we listen to the obj
178                // NOTE: ObjectManager cares about revision, we do not
179                try {
180                        ObjectManager om = ObjectManager.getInstance();
181                        NamedObj namedObj = om.getObjectRevision(lsid);
182                        if (namedObj != null) {
183                                namedObj.addChangeListener(this);
184                                if (workflow != null){
185                                        workflow.setModel(namedObj);
186                                }
187                        }
188                } catch (Exception e) {
189                        e.printStackTrace();
190                }
191        }
192
193        /**
194         * 
195         */
196        @Override
197    public void changeExecuted(ChangeRequest change) {
198                
199                if (change instanceof NamedObjIdChangeRequest) {
200                        NamedObjIdChangeRequest noicr = (NamedObjIdChangeRequest) change;
201                        Object source = noicr.getSource();
202                        if (source != null && source instanceof NamedObjId){
203                                NamedObjId noid = (NamedObjId) source;
204                                NamedObj parent = noid.getContainer();
205                                NamedObj toplevel = noid.toplevel();
206                                if (!parent.equals(toplevel)) {
207                                        return;
208                                }
209                                KeplerLSID newLsid = noid.getId();
210                                log.debug("Processing Id ChangeRequest for new lsid: " + newLsid);
211                                // check if we are managing the lsid already (revision bump)
212                                if (workflowExists(newLsid)) {
213                                        log.debug("no action needed for new lsid: " + newLsid);
214                                        return;
215                                }
216                                // otherwise we need to look back in the derivedFrom list
217                                KeplerLSID oldLsid = null;
218        
219                                try {
220                                        NamedObjIdReferralList nObjIdList = NamedObjId.getIDListAttributeFor(parent);
221                                        List<KeplerLSID> referrals = nObjIdList.getReferrals();
222                                        if (referrals != null && referrals.size() > 0) {
223                                                oldLsid = referrals.get(0);
224                                        }
225                                } catch (Exception e) {
226                                        e.printStackTrace();
227                                }
228                                
229                                //swap out the old with the new
230                                if (oldLsid != null && newLsid != null) {
231                                        log.debug("using layout from old lsid: " + oldLsid);
232                                        ModelToFrameManager modelToFrameManager = ModelToFrameManager.getInstance();
233                                        TableauFrame tf = modelToFrameManager.getFrame(toplevel);
234                                        ReportLayout oldLayout = WorkflowManager.getInstance().getWorkflow(tf, oldLsid).getReportLayout(tf);
235                                        
236                                        ///we must now change the reportLayout's copy of the workflowLSID
237                                        oldLayout.setWorkflowLSID(newLsid);
238                                        
239                                        WorkflowManager.getInstance().getWorkflow(tf, newLsid).addReportLayout(tf, oldLayout);
240                                        WorkflowManager.getInstance().getWorkflow(tf, newLsid).setModel(parent);
241                                        
242                                }
243                        }
244                }
245                
246        }
247
248        /**
249         * 
250         */
251        @Override
252    public void changeFailed(ChangeRequest change, Exception exception) {
253                // TODO Auto-generated method stub
254                
255        }
256
257        /**
258         * 
259         * @return "ranking" tableauFrame. This is either the tableauFrame associated with the window associated with a ReportDesignerPanel 
260         * that last gained focus or the tableauFrame used in ReportDesignerPanel's initializeTab.
261         */
262        public static TableauFrame getRankingTableauFrame(){
263                return _rankingTableauFrame;
264        }
265        
266        public static void setRankingTableauFrame(TableauFrame tableauFrame){
267                _rankingTableauFrame = tableauFrame;
268        }
269        
270    /** Listener for windowClosing action. */
271    class WorkflowClosingAdapter extends WindowAdapter {
272        public WorkflowClosingAdapter(TableauFrame frame) {
273                _frame = new WeakReference<TableauFrame>(frame);
274        }
275        @Override
276        public void windowClosing(WindowEvent e) {
277            // do nothing if kepler is exiting
278            if(!org.kepler.module.workflowrunmanager.Shutdown.haveShutdown()) {
279                TableauFrame frame = _frame.get();
280                if(frame != null) {
281                    List<KeplerLSID> lsids = getKeplerLSID(frame);
282                        for (KeplerLSID lsid : lsids) {
283                                //System.out.println("Removing workflow for " + lsid.toString());
284                                removeWorkflow(lsid);
285                        }
286                }
287            }
288        }
289        private WeakReference<TableauFrame> _frame;
290    }
291        
292}