001/**
002 * Copyright (c) 2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: tao $'
006 * '$Date: 2010-06-03 16:45:10 -0700 (Thu, 03 Jun 2010) $' 
007 * '$Revision: 24730 $'
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 */
029package org.kepler.workflowscheduler.gui;
030
031import java.io.File;
032import java.util.Stack;
033import java.util.concurrent.atomic.AtomicBoolean;
034
035import javax.swing.Box;
036import javax.swing.BoxLayout;
037import javax.swing.JLabel;
038import javax.swing.JOptionPane;
039import javax.swing.JPanel;
040
041import org.apache.commons.logging.Log;
042import org.apache.commons.logging.LogFactory;
043import org.ecoinformatics.ecogrid.client.IdentifierServiceClient;
044import org.kepler.gui.TabPane;
045import org.kepler.kar.KARFile;
046import org.kepler.objectmanager.lsid.KeplerLSID;
047import org.kepler.objectmanager.repository.Repository;
048import org.kepler.objectmanager.repository.RepositoryManager;
049
050import ptolemy.actor.gui.TableauFrame;
051
052/**
053 * This class represents the right panel of the scheduler dialog object.
054 * It can show one panel of the two options - a JLabel which ask user to
055 * select a remote workflow and a real scheduler panel.
056 * @author tao
057 *
058 */
059public class WorkflowSchedulerParentPanel extends JPanel implements TabPane,
060                                                        UploadingKARFileListenerInterface
061{
062  private static final Log log = LogFactory.getLog(WorkflowSchedulerParentPanel.class
063      .getName());
064  static final String SCHEDULER = "Scheduler";
065  private static final int ITEM = 0;
066  
067  private JLabel message = new JLabel("Select a workflow from the Components panel.");
068  private TableauFrame parent = null;
069  private WorkflowSchedulerPanel schedulerPanel = null;
070  //private boolean showedMessage = true;
071  private ScheduleChangeController scheduleChangeController;
072  //private volatile boolean doneSelectRemoteWorkflow = false;
073  private Stack trackSelectRemoteWorkflow = new Stack();
074  private AtomicBoolean  previousSelectionIsWorkflow = new AtomicBoolean(false);
075  
076  /**
077   * Constructor
078   * @param parent
079   */
080  public WorkflowSchedulerParentPanel(TableauFrame parent, ScheduleChangeController scheduleChangeController)
081  {
082    this.parent = parent;
083    this.scheduleChangeController = scheduleChangeController;
084    if(this.scheduleChangeController != null)
085    {
086      this.scheduleChangeController.addListener(this);
087    }   
088    try
089    {
090      schedulerPanel = new WorkflowSchedulerPanel(parent,this, this.scheduleChangeController);
091    }
092    catch(Exception e)
093    {
094      JOptionPane.showMessageDialog(null, "Workflow scheduler couldn't function correctly -- "+e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
095      //log.error("Workflow scheduler couldn't function correctly -- "+e.getMessage());
096    }
097    
098    initialize();
099  }
100  
101  /*
102   * Initialize the panel
103   */
104  private void initialize()
105  {
106    this.setLayout(new BoxLayout(this,BoxLayout.Y_AXIS));
107    this.add(Box.createVerticalStrut(20));
108    Box box = Box.createHorizontalBox();
109    box.add(Box.createHorizontalStrut(250));
110    box.add(message);
111    box.add(Box.createHorizontalGlue());
112    this.add(box);
113    this.add(Box.createVerticalGlue());
114  }
115
116  /**
117   * Return the parent frame
118   */
119  public TableauFrame getParentFrame()
120  {
121    return parent;
122  }
123
124  /**
125   * This method should return the name of the tab which is used to label the
126   * tab in the TabbedPane
127   */
128  public String getTabName()
129  {    
130    return SCHEDULER;
131  }
132
133  /**
134   * Initialize the tab
135   */
136  public void initializeTab() throws Exception
137  {
138    
139  }
140
141  /**
142   * Set the parent frame of this panel
143   */
144  public void setParentFrame(TableauFrame parent)
145  {
146    this.parent = parent;
147  }
148  
149  /**
150   * Action for selecting a remote workflow on component search panel
151   * @param name name of the workflow
152   * @param lsid  lsid of the workflow 
153   * @param karSourceRepositoryName the name of repository storing the kar file
154   * @param karId the id of the kar file
155   * @param hasReporting if the workflow has a reporting module
156   */
157  public synchronized void selectRemoteWorkflow(String workflowName, String workflowLsid, 
158                                          String karSourceRepositoryName, String karId, boolean hasReportLayout)
159  {
160    //System.out.println("The workflow name is "+workflowName);
161    //System.out.println("The workflow lsid is "+workflowLsid);
162    //System.out.println("The repository name is "+karSourceRepositoryName);
163    //System.out.println("The kar id is "+karId);
164    //System.out.println("Choosing yes=============== ");
165    if(karId != null && workflowLsid != null && karSourceRepositoryName != null && schedulerPanel != null)
166    {
167      if(scheduleChangeController != null)
168      {
169        scheduleChangeController.startProgressBar();
170      }    
171      previousSelectionIsWorkflow.set(true);
172      trackSelectRemoteWorkflow.add(ITEM);
173      schedulerPanel.setWorkflowForScheduling(workflowName, workflowLsid, 
174                                                  karSourceRepositoryName, karId, hasReportLayout);
175    }
176    else
177    {
178      selectNonSchedulalbeItem();
179    }
180    
181  }
182  
183  /**
184   * Set a local workflow on this panel for scheduling
185   * @param karFilePath the path of kar file which contains the workflow
186   * @param workflowName the name of the workflow
187   * @param workflowLSID the id of the workflow
188   */
189  public synchronized void setLocalWorkflow(String karFilePath, String workflowName, String workflowLSID)
190  {
191    
192    selectNonSchedulalbeItem();//set the background 
193    Repository repository = null;
194    
195    if(schedulerPanel == null)
196    {
197      return;
198    }
199    
200    try
201    {
202     repository = RepositoryManager.getInstance().getSaveRepository();     
203    }
204    catch(Exception e)
205    {
206     log.warn("Couldn't find saving repository -"+e.getMessage());
207    }
208   
209     if(repository == null)
210     {
211       JOptionPane.showMessageDialog(parent,  "To schedule this workflow, you must first choose a remote repository.\n"
212           +"Click the \"Sources\" button and mark \"Save\" next to a remote Source, and try again.","Warning" , JOptionPane.WARNING_MESSAGE);
213        return;
214     }
215     
216     
217      
218    //check if the kar file already in LocalRemoteWorkflwoMappingController
219    String remoteKarFileLSID = getRemoteKarLSIDFromLocalRemoteWorkflowController(karFilePath, repository.getName());
220    if(remoteKarFileLSID == null)
221    {
222      //LocalRemoteWorkflwoMappingController doesn't register it,
223      //check remote the server
224      if(scheduleChangeController != null)
225      {
226        scheduleChangeController.startProgressBar();
227      }
228      KARFile karFile = getKarFile(karFilePath);
229      if(karFile == null)
230      {
231        if(scheduleChangeController != null)
232        {
233          scheduleChangeController.stopProgressBar();
234        }
235        return;
236      }
237      //check the remote repository
238      boolean registered = isAlreadyRegistered(karFile.getLSID(), repository);
239      if(!registered)
240      {
241        // has not been uploaded, please upload the kar file
242        int choice = JOptionPane.showConfirmDialog(parent, 
243            "To schedule this workflow on the "+repository.getName()+" repository, it must first"
244           +" be uploaded.\nUpload KAR?", "Warning", JOptionPane.YES_NO_OPTION);
245       
246       if(choice == JOptionPane.NO_OPTION)
247       {
248         if(scheduleChangeController != null)
249         {
250           scheduleChangeController.stopProgressBar();
251         }
252         return;
253       }
254       uploadKarFile(karFile,karFilePath, workflowName, workflowLSID, repository.getName());
255      
256      }
257      else
258      {
259        remoteKarFileLSID = karFile.getLSID().toString();
260        boolean hasReportLayout = true;// we don't warn user again
261        // success upload and we have the remote kar file at hand.
262        selectRemoteWorkflow(workflowName, workflowLSID, 
263            repository.getName(), remoteKarFileLSID, hasReportLayout);
264      }   
265    }
266    else
267    {
268      boolean hasReportLayout = true;// we don't warn user again
269      // success upload and we have the remote kar file at hand.
270      selectRemoteWorkflow(workflowName, workflowLSID, 
271          repository.getName(), remoteKarFileLSID, hasReportLayout);
272    } 
273  }
274  
275  /*
276   * Get the remote kar lsid for the local workflow kar file from LocalRemoteWorkflowController
277   * If null returned, this means the local kar file has not been uploaded to the 
278   * remote repository
279   */
280  private String getRemoteKarLSIDFromLocalRemoteWorkflowController(String localKarFilePath, String repositoryName)
281  {
282    String remoteKarLSID = null;
283    //first to see if the local-remote controller has the copy. This is memory checking.
284    LocalRemoteWorkflowMap map = LocalRemoteWorkflowMappingController.getMap(localKarFilePath, repositoryName);
285    if(map != null)
286    {
287      remoteKarLSID = map.getRemoteKarLSID();
288    }
289    //System.out.println("=========== get kar lisd from the controller "+remoteKarLSID);
290    return remoteKarLSID;
291  }
292  
293  /*
294   * Test if the kar lsid is already registered in the saving repository
295   */
296  private boolean isAlreadyRegistered(KeplerLSID lsid, Repository saveRepository) 
297  {
298    boolean registered = false;
299    try
300    {
301    IdentifierServiceClient identificationClient = new IdentifierServiceClient(
302        saveRepository.getLSIDServerURL());
303    registered = identificationClient.isRegistered(lsid.toString());
304    }
305    catch(Exception e)
306    {
307      log.warn("Couldn't determine if the kar lsid has been registered in the remote repository - "+e.getMessage());
308    }
309    //System.out.println("=========== is already register for the lisd  "+lsid.toString()+ " "+registered);
310    return registered;
311  }
312  
313  /*
314   * Get KARFile object from the karFilePath. null will be returned if kepler
315   * can't create KARFile.
316   */
317  private KARFile getKarFile(String karFilePath)
318  {   
319    KARFile karFile = null; 
320    try
321    {
322      karFile = new KARFile(new File(karFilePath));
323    }
324    catch(Exception e)
325    {
326      JOptionPane.showMessageDialog(parent,  
327          "Kepler couldn't create kar file for path "+karFilePath+
328          " - "+e.getMessage(),"Error" , JOptionPane.ERROR_MESSAGE);
329      
330    }
331    return karFile;
332  }
333  
334  /*
335   * Upload the kar file and kar xml to remote repository. 
336   * The lisd of kar file will be returned. If uploading failed, null will be returned.
337   */
338  private void uploadKarFile( KARFile karFile,String karFilePath, 
339      String workflowName, String workflowLSID, String repositoryName)
340  {
341    UploadingKARFileSwingWorker worker = new UploadingKARFileSwingWorker(karFile,  karFilePath, 
342        workflowName, workflowLSID, repositoryName, scheduleChangeController, parent);
343    worker.execute();
344  }
345  
346  /**
347   * SearchSchedulesListenerInterface calls back this method.
348   * It will update the gui.
349   */
350  public void complete()
351  {
352    this.repaint();
353    this.validate();
354    trackSelectRemoteWorkflow.pop();
355    if(scheduleChangeController != null)
356    {
357      scheduleChangeController.stopProgressBar();
358    }
359    
360    //System.out.println("search is done");
361  }
362  
363  /**
364   * Call back method when the uploading is done. 
365   * It will put the kar file lsid into a map. Then select the remote copy of the kar file to
366   * scheduler gui.
367   * @param KARFileLSID the lisd of the uploaded kar file
368   * @param KARFilePath the path of the local kar file
369   * @param workflowName the name of the workflow 
370   * @param workflowLSID the lsid of the workflow
371   * @param repositoryName the name of the remote repository
372   */
373  public void completeUploading(String KARFileLSID, String KARFilePath, String workflowName, 
374      String workflowLSID, String repositoryName)
375  {
376    if(scheduleChangeController != null)
377    {
378      scheduleChangeController.stopProgressBar();
379    }
380    if(KARFileLSID != null)
381    {
382      LocalRemoteWorkflowMap map = new LocalRemoteWorkflowMap(KARFilePath, 
383          workflowLSID, workflowName, KARFileLSID, repositoryName);
384      LocalRemoteWorkflowMappingController.add(map);
385      boolean hasReportLayout = true;// we don't warn user again
386      // success upload and we have the remote kar file at hand.
387      selectRemoteWorkflow(workflowName, workflowLSID, 
388          repositoryName, KARFileLSID, hasReportLayout);
389    }
390    else
391    {
392      JOptionPane.showMessageDialog(parent,  
393          "Failed to upload kar file to the repository.","Error" , JOptionPane.ERROR_MESSAGE);
394    }
395   
396    
397  }
398  
399  /**
400   * Method for ScheduleChangeListener. Do nothing.
401   * Handle the event that a new schedule is added
402   * @param schedule  the new schedule
403   * @throws Exception
404   */
405  public void addSchedule(Schedule schedule) throws Exception
406  {
407    
408  }
409  
410  /**
411   * Method for ScheduleChangeListener. Do nothing.
412   * Handle the event that a schedule is removed
413   * @param schedule the schedule will be removed
414   * @throws Exception
415   */
416  public void removeSchedule(Schedule schedule) throws Exception
417  {
418    
419  }
420  
421  /**
422   * Method for ScheduleChangeListener. Do nothing.
423   * Handle the event that a schedule will be enabled
424   * @param schedule the schedule will be enabled
425   * @throws Exception
426   */
427  public void enableSchedule(Schedule schedule) throws Exception
428  {
429    
430  }
431  
432  /**
433   * Method for ScheduleChangeListener. Do nothing.
434   * Handle the event that a schedule will be disabled
435   * @param schedule the schedule will be disabled
436   * @throws Exception
437   */
438  public void disableSchedule(Schedule schedule) throws Exception
439  {
440    
441  }
442  
443  /**
444   * Method for ScheduleChangeListener. Do nothing.
445   * Handle the event that new schedules will replace the old ones
446   * @param newSchedule new schedules
447   * @throws Exception
448   */
449  public void update(Schedule[] newSchedules) throws Exception
450  {
451    
452  }
453  
454  /**
455   * Action for  selecting a non remote workflow on component search panel
456   */
457  public void selectNonSchedulalbeItem()
458  {
459    if(previousSelectionIsWorkflow.get() == true)
460    {
461      this.removeAll();
462      initialize();
463      this.repaint();
464      this.validate();
465      previousSelectionIsWorkflow.set(false);
466    }
467     
468   
469  }
470  
471  /**
472   * Gets the status of selecting workflow being done 
473   * @return true if the selecting is done
474   */
475  public boolean isSelectingRemoteWorkflowDone()
476  {
477    return trackSelectRemoteWorkflow.isEmpty();
478  }
479
480}