001/*
002 * Copyright (c) 2009-2010 The Regents of the University of California.
003 * All rights reserved.
004 *
005 * '$Author: crawl $'
006 * '$Date: 2015-04-23 16:56:59 +0000 (Thu, 23 Apr 2015) $' 
007 * '$Revision: 33366 $'
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.modulemanager.gui;
031
032import java.awt.event.ActionEvent;
033import java.awt.event.ActionListener;
034import java.awt.event.ItemEvent;
035import java.awt.event.ItemListener;
036import java.io.FileNotFoundException;
037import java.io.IOException;
038import java.util.ArrayList;
039import java.util.Collections;
040import java.util.List;
041import java.util.Vector;
042
043import javax.swing.DefaultListModel;
044import javax.swing.JButton;
045import javax.swing.JCheckBox;
046import javax.swing.JLabel;
047import javax.swing.JList;
048import javax.swing.JOptionPane;
049import javax.swing.JPanel;
050import javax.swing.JScrollPane;
051import javax.swing.ListModel;
052import javax.swing.ListSelectionModel;
053import javax.swing.SwingWorker;
054
055import org.apache.tools.ant.DefaultLogger;
056import org.apache.tools.ant.Project;
057import org.jdesktop.layout.GroupLayout;
058import org.kepler.build.Run;
059import org.kepler.build.modules.CurrentSuiteTxt;
060import org.kepler.build.modules.ModulesTxt;
061import org.kepler.build.modules.RollbackTxt;
062import org.kepler.build.project.PrintError;
063import org.kepler.build.project.ProjectLocator;
064import org.kepler.configuration.ConfigurationManager;
065import org.kepler.configuration.ConfigurationManagerException;
066import org.kepler.configuration.ConfigurationProperty;
067import org.kepler.modulemanager.ModuleDownloader;
068import org.kepler.modulemanager.gui.patch.PatchChecker;
069import org.kepler.util.ShutdownNotifier;
070
071/**
072 * Created by David Welker.
073 * Date: Sep 18, 2009
074 * Time: 10:26:30 AM
075 */
076public class AvailableModulesPanel extends JPanel 
077{
078  //Component Declarations
079
080  //- List of available released suites.
081  private JLabel suitesLabel = new JLabel("Available Suites:");
082  private SuitesList suitesList = new SuitesList();
083  private JScrollPane suitesListScrollPane = new JScrollPane(suitesList);
084  private String suitesLabelToolTip = "List of Available Suites";
085
086  private JCheckBox showSuitePatchesCheckBox = new JCheckBox("Show suite patches.");
087
088  //- List of all available modules, including suites.
089  private JLabel modulesLabel = new JLabel("Available Modules:                         ");
090  private ModulesList modulesList = new ModulesList();
091  private JScrollPane modulesListScrollPane = new JScrollPane(modulesList);
092  private String modulesLabelToolTip = "List of Available Modules";
093
094  private JCheckBox showTestReleasesCheckBox = new JCheckBox("Show test releases.");
095
096  private JLabel selectedLabel = new JLabel("Selected Modules:");
097  private JList selectedModulesList = new JList();
098  private JScrollPane selectedModulesListScrollPane = new JScrollPane(selectedModulesList);
099  private String selectedLabelToolTip = "The following modules or suites have been selected for installation";
100   
101  // private JButton retrieveButton = new JButton("Retrieve");
102  private JButton restartButton = new JButton("Apply and Restart");
103  private JButton cancelButton = new JButton("Cancel");
104  private JButton selectButton = new JButton("\u2192");
105  private JButton unselectButton = new JButton("\u2190");
106  private JButton upButton = new JButton("\u2191");
107  private JButton downButton = new JButton("\u2193");
108  
109  private ModuleDownloader downloader;
110
111  private JCheckBox shouldCheckForPatchesCheckBox = new JCheckBox("Automatically check for patches on startup.");
112  private JButton checkForPatchesNowButton = new JButton("Check for Patches Now");
113  private JButton rollbackKeplerButton = new JButton("Rollback Kepler");
114
115  public AvailableModulesPanel()
116  {
117      super();
118      downloader = new ModuleDownloader();
119      ModuleDownloadProgressMonitor mdpm = new ModuleDownloadProgressMonitor(this);
120      downloader.addListener(mdpm);
121      initComponents();
122      layoutComponents();
123  }
124
125  private void writeModulesTxt()
126  {
127    ModulesTxt modulesTxt = ModulesTxt.instance();
128    modulesTxt.clear();
129    ListModel listModel = selectedModulesList.getModel();
130    String firstModule = ((String)listModel.getElementAt(0));
131    firstModule = ModuleManagerGuiUtil.writeStringForSuite(firstModule);
132    boolean isSingleSuite = listModel.getSize() == 1 && firstModule.startsWith("*");
133    String newSuite = isSingleSuite ? firstModule.substring(1, firstModule.length()) : "unknown";
134    for (int i = 0; i < listModel.getSize(); i++)
135    {
136      String moduleName = (String) listModel.getElementAt(i);
137      if( moduleName.startsWith("*") )
138      {
139          moduleName = ModuleManagerGuiUtil.writeStringForSuite(moduleName);
140      }
141      modulesTxt.add(moduleName);
142    }
143    modulesTxt.write();
144    CurrentSuiteTxt.delete();
145    CurrentSuiteTxt.setName(newSuite);
146  }
147
148  private void initComponents()
149  {
150    selectedModulesList.setModel(new DefaultListModel());
151    selectedModulesList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
152    //retrieveButton.setEnabled(false);
153    restartButton.setEnabled(false);
154    cancelButton.setEnabled(false);
155    upButton.setEnabled(false);
156    downButton.setEnabled(false);
157
158    restartButton.addActionListener(new ActionListener()
159    {
160        public void actionPerformed(ActionEvent event)
161        {
162            // retrieveButton.setEnabled(false);
163            int result = JOptionPane.showConfirmDialog(
164                  AvailableModulesPanel.this,
165                  "Any unsaved work will be LOST. Continue?",
166                  "Confirm", JOptionPane.YES_NO_OPTION);
167                              if (result == JOptionPane.NO_OPTION) {
168                                        return;
169                              }
170                                              
171            restartButton.setEnabled(false);
172            cancelButton.setEnabled(false);
173            selectButton.setEnabled(false);
174            unselectButton.setEnabled(false);
175            upButton.setEnabled(false);
176            downButton.setEnabled(false);
177
178            final Project project = new Project();
179            project.setBaseDir(ProjectLocator.getProjectDir());
180            
181            // NOTE: before performing sanity checks on the selected suite,
182            // all the modules must be downloaded first so that we know
183            // what modules are in the selected suite.
184
185            SwingWorker<Void, Void> worker = new SwingWorker<Void, Void>()
186            {
187                @Override
188                /** Download all the modules in the selected suite. */
189                public Void doInBackground() throws Exception
190                {
191                    DefaultLogger logger = new DefaultLogger();
192                    logger.setMessageOutputLevel(Project.MSG_INFO);
193                    logger.setOutputPrintStream(System.out);
194                    logger.setErrorPrintStream(System.out);
195                    project.addBuildListener(logger);
196
197                    ListModel listModel = selectedModulesList.getModel();
198                    List<String> moduleList = new ArrayList();
199                    for (int i = 0; i < listModel.getSize(); i++)
200                    {
201                        moduleList.add((String) listModel.getElementAt(i));
202                    }
203                    try {
204                        downloader.downloadModules(moduleList);
205                    } catch (FileNotFoundException e) {
206                        JOptionPane.showMessageDialog(
207                                  AvailableModulesPanel.this,
208                                  e.getMessage());
209                        return null;
210                    }
211                    
212                    return null;
213                }
214                
215                @Override
216                /** Perform sanity checks before restarting with the new suite. */
217                public void done() {
218                    
219                    // perform sanity checks on the suite
220                    try {
221                        RollbackTxt.save();
222                    } catch(IOException e) {
223                        PrintError.message("Error saving modules.txt to rollback file.", e);
224                        _enableButtons();
225                        return;
226                    }
227                    writeModulesTxt();
228                    boolean restart = CurrentSuitePanel.canExit();
229                    if(!restart) {
230                        RollbackTxt.load();
231                        _enableButtons();
232                        return;
233                    }
234
235                    //XXX call ShutdownNotifer.shutdown() before spawning new process,
236                    // to avoid 2nd instance potentially interfering with quitting first.
237                    // see: http://bugzilla.ecoinformatics.org/show_bug.cgi?id=5484
238                    System.out.println("AvailableModulesPanel notifying shutdown listeners " +
239                        "of impending shutdown. Closing any open databases may take awhile...");
240                    ShutdownNotifier.shutdown();
241                    
242                    System.out.println("AvailableModulesPanel Spawning new Kepler process");
243
244                    Run run = new Run();
245                    run.setTaskName("run");
246                    run.setProject(project);
247                    run.init();
248                    run.setSpawn(true);
249                    run.execute();
250
251                    System.out.println("AvailableModulesPanel Ending current Kepler process");
252                    System.out.println(" NOTE: This will probably throw an exception as the current process is terminated while a new Kepler process starts. This is normal and expected.");
253                    
254                    //TODO why call exit with error arg here?
255                    System.exit(1);
256                }
257                
258                /** Enable buttons that were disabled when apply and restart was pushed. */
259                private void _enableButtons() {   
260                    restartButton.setEnabled(true);
261                    cancelButton.setEnabled(true);
262                    selectButton.setEnabled(true);
263                    unselectButton.setEnabled(true);
264                    upButton.setEnabled(true);
265                    downButton.setEnabled(true);
266                }
267            };
268            worker.execute();
269            
270
271        }
272    });
273
274    selectButton.addActionListener(new ActionListener()
275    {
276      public void actionPerformed(ActionEvent e)
277      {
278        for (Object s : suitesList.getSelectedValues())
279        {
280          String suite = (String) s;
281          ((DefaultListModel) selectedModulesList.getModel()).addElement("*" + suite);
282          suitesList.removeElement(suite);
283        }
284
285        for (Object m : modulesList.getSelectedValues())
286        {
287          String module = (String) m;
288          ((DefaultListModel) selectedModulesList.getModel()).addElement(module);
289          ((DefaultListModel) modulesList.getModel()).removeElement(module);
290        }
291        DefaultListModel m = (DefaultListModel) selectedModulesList.getModel();
292        if (m.getSize() > 0)
293        {
294         // retrieveButton.setEnabled(true);
295          restartButton.setEnabled(true);
296          cancelButton.setEnabled(true);
297          upButton.setEnabled(true);
298          downButton.setEnabled(true);
299        }
300      }
301    });
302
303    unselectButton.addActionListener(new ActionListener()
304    {
305      public void actionPerformed(ActionEvent e)
306      {
307        unselect(false);
308        DefaultListModel m = (DefaultListModel) selectedModulesList.getModel();
309        if (m.getSize() == 0)
310        {
311         // retrieveButton.setEnabled(false);
312          restartButton.setEnabled(false);
313          cancelButton.setEnabled(false);
314          upButton.setEnabled(false);
315          downButton.setEnabled(false);
316        }
317      }
318    });
319
320    upButton.addActionListener(new ActionListener()
321    {
322      public void actionPerformed(ActionEvent e)
323      {
324        moveUp();
325      }
326    });
327
328    downButton.addActionListener(new ActionListener()
329    {
330      public void actionPerformed(ActionEvent e)
331      {
332        moveDown();
333      }
334    });
335
336    showSuitePatchesCheckBox.addActionListener(new ActionListener()
337    {
338        public void actionPerformed(ActionEvent e)
339        {
340            suitesList.setShouldShowFullList(showSuitePatchesCheckBox.isSelected());
341        }
342    });
343
344    showTestReleasesCheckBox.addActionListener(new ActionListener()
345    {
346        public void actionPerformed(ActionEvent e)
347        {
348            //suitesList.setShowTestReleases(showTestReleasesCheckBox.isSelected());
349            //modulesList.setShowTestReleases(showTestReleasesCheckBox.isSelected());
350        }
351    });
352    
353    // the Show Test Releases checkbox no longer works, so hiding until
354    // can be fixed.
355    showTestReleasesCheckBox.setVisible(false);
356
357    ConfigurationProperty mm = ConfigurationManager.getInstance().getProperty(ConfigurationManager.getModule("module-manager"));
358    final ConfigurationProperty checkForPatches = mm.getProperty("check-for-patches");
359    boolean shouldCheckForPatches = checkForPatches.getValue().trim().equals("true") ? true : false;
360
361    shouldCheckForPatchesCheckBox.setSelected(shouldCheckForPatches);
362
363    shouldCheckForPatchesCheckBox.addItemListener(new ItemListener()
364    {
365      public void itemStateChanged(ItemEvent e)
366      {
367        try
368        {
369          checkForPatches.setValue(""+shouldCheckForPatchesCheckBox.isSelected());
370          ConfigurationManager.getInstance().saveConfiguration();
371        }
372        catch (ConfigurationManagerException e1)
373        {
374          e1.printStackTrace();
375        }
376
377
378      }
379    });
380
381    checkForPatchesNowButton.addActionListener(new ActionListener()
382    {
383
384      public void actionPerformed(ActionEvent e)
385      {
386          PatchChecker.check(true, false, true);
387      }
388    });
389
390    rollbackKeplerButton.setEnabled(RollbackTxt.exists());
391    rollbackKeplerButton.addActionListener(new ActionListener()
392    {
393
394      public void actionPerformed(ActionEvent e)
395      {
396
397          SwingWorker worker = new SwingWorker<Void, Void>()
398          {
399              public Void doInBackground() throws Exception
400              {
401                  Project project = new Project();
402                  project.setBaseDir(ProjectLocator.getProjectDir());
403                  DefaultLogger logger = new DefaultLogger();
404                  logger.setMessageOutputLevel(Project.MSG_INFO);
405                  logger.setOutputPrintStream(System.out);
406                  logger.setErrorPrintStream(System.out);
407                  project.addBuildListener(logger);
408                  
409                  try {
410                          downloader.downloadModules(RollbackTxt.read());
411                  } catch (FileNotFoundException e) {
412                      JOptionPane.showMessageDialog(
413                                  AvailableModulesPanel.this,
414                                  e.getMessage());
415                      return null;
416                  }
417
418                  RollbackTxt.load();
419
420                  System.out.println("AvailableModulesPanel notifiying shutdown listeners " +
421                                        "of impending shutdown. Closing any open databases may take awhile...");
422                  ShutdownNotifier.shutdown();
423                  
424                  System.out.println("AvailableModulesPanel Spawning new Kepler process");
425                  Run run = new Run();
426                  run.setTaskName("run");
427                  run.setProject(project);
428                  run.init();
429                  run.setSpawn(true);
430                  run.execute();
431
432                  System.out.println("Ending current Kepler process");
433                  System.out.println(" NOTE: This will probably throw an exception as the current process is terminated while a new Kepler process starts. This is normal and expected.");
434                  
435                  //TODO why call exit with error arg here?
436                  System.exit(1);
437
438                  return null;
439              }
440          };
441          worker.execute();
442      }
443    });
444
445
446  }
447
448  private void unselect(boolean all)
449  {
450    if (all)
451    {
452      DefaultListModel selectedModulesListModel = (DefaultListModel) selectedModulesList.getModel();
453      //note that size changes dynamically here, so you can't use .size() in the for loop or
454      //you will always leave one item in the list
455      int size = selectedModulesListModel.size();
456      for (int i = 0; i < size; i++)
457      {
458        String module = (String) selectedModulesListModel.getElementAt(0);
459        if (module.startsWith("*"))
460        {
461          suitesList.addElement(module.substring(1, module.length()));
462        }
463        else
464        {
465          ((DefaultListModel) modulesList.getModel()).addElement(module);
466          ((DefaultListModel) selectedModulesList.getModel()).removeElement(module);
467        }
468      }
469    }
470    else
471    {
472      for (Object m : selectedModulesList.getSelectedValues())
473      {
474        String module = (String) m;
475        if (module.startsWith("*"))
476        {
477            suitesList.addElement(module.substring(1, module.length()));
478        }
479        else
480        {
481            ((DefaultListModel) modulesList.getModel()).addElement(module);
482        }
483        ((DefaultListModel) selectedModulesList.getModel()).removeElement(module);
484      }
485    }
486
487    DefaultListModel suitesListModel = (DefaultListModel) suitesList.getModel();
488
489    suitesList.sort();
490
491    DefaultListModel modulesListModel = (DefaultListModel) modulesList.getModel();
492    Vector sortList = new Vector();
493    for (int i = 0; i < modulesListModel.size(); i++)
494    {
495      sortList.add(modulesListModel.getElementAt(i));
496    }
497    modulesListModel.clear();
498    Collections.sort(sortList);
499    for (Object o : sortList)
500    {
501      modulesListModel.addElement(o);
502    }
503
504  }
505
506  private void moveUp()
507  {
508    DefaultListModel m = (DefaultListModel) selectedModulesList.getModel();
509    if (selectedModulesList.getSelectedIndices().length == 0)
510    {
511        return;
512    }
513    int i = selectedModulesList.getSelectedIndex();
514    if (i == 0)
515    {
516        return;
517    }
518    m.add(i - 1, m.remove(i));
519    selectedModulesList.setSelectedIndex(i - 1);
520  }
521
522  private void moveDown()
523  {
524    DefaultListModel m = (DefaultListModel) selectedModulesList.getModel();
525    if (selectedModulesList.getSelectedIndices().length == 0)
526    {
527        return;
528    }
529    int i = selectedModulesList.getSelectedIndex();
530    if (i == m.getSize() - 1)
531    {
532        return;
533    }
534    m.add(i + 1, m.remove(i));
535    selectedModulesList.setSelectedIndex(i + 1);
536  }
537
538  private void layoutComponents()
539  {
540    JPanel buttonPanel = new JPanel();
541    buttonPanel.add(upButton);
542    buttonPanel.add(downButton);
543
544    suitesLabel.setToolTipText(suitesLabelToolTip);
545    modulesLabel.setToolTipText(modulesLabelToolTip);
546    selectedLabel.setToolTipText(selectedLabelToolTip);
547    
548    GroupLayout layout = new GroupLayout(this);
549    setLayout(layout);
550    layout.setAutocreateContainerGaps(true);
551    layout.setAutocreateGaps(true);
552
553    layout.setHorizontalGroup(layout.createParallelGroup(GroupLayout.TRAILING)
554        .add(
555            layout.createSequentialGroup().add(
556                layout.createParallelGroup().add(suitesLabel).add(
557                    suitesListScrollPane).add(showSuitePatchesCheckBox).add(modulesLabel).add(
558                    modulesListScrollPane).add(showTestReleasesCheckBox)).add(
559                layout.createParallelGroup().add(selectButton).add(
560                    unselectButton)).add(
561                layout.createParallelGroup().add(selectedLabel).add(
562                    selectedModulesListScrollPane).add(
563                    layout.createSequentialGroup().add(upButton).add(downButton))
564                    .add(shouldCheckForPatchesCheckBox)
565                    .add(checkForPatchesNowButton)
566                    .add(rollbackKeplerButton))).add(
567            layout.createSequentialGroup().add(restartButton)));//.add(cancelButton)));
568
569    layout.setVerticalGroup(layout.createSequentialGroup().add(
570        layout.createParallelGroup(GroupLayout.CENTER).add(
571            layout.createSequentialGroup().add(suitesLabel).add(
572                suitesListScrollPane).add(showSuitePatchesCheckBox).add(10).add(modulesLabel).add(
573                modulesListScrollPane).add(showTestReleasesCheckBox)).add(
574            layout.createSequentialGroup().add(selectButton)
575                .add(unselectButton)).add(
576            layout.createSequentialGroup().add(selectedLabel).add(
577                selectedModulesListScrollPane).add(
578                layout.createParallelGroup().add(upButton).add(downButton))
579                .add(shouldCheckForPatchesCheckBox)
580                .add(checkForPatchesNowButton)
581                .add(rollbackKeplerButton)))
582//        .add(
583//
584//        )
585        .add(
586            layout.createParallelGroup().add(restartButton)//.add(cancelButton)));
587    //.add(
588         //   layout.createParallelGroup().add(lblrestartMsg)
589            ));
590  }
591}