1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License, Version 1.0 only
   6  * (the "License").  You may not use this file except in compliance
   7  * with the License.
   8  *
   9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10  * or http://www.opensolaris.org/os/licensing.
  11  * See the License for the specific language governing permissions
  12  * and limitations under the License.
  13  *
  14  * When distributing Covered Code, include this CDDL HEADER in each
  15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16  * If applicable, add the following below this CDDL HEADER, with the
  17  * fields enclosed by brackets "[]" replaced with your own identifying
  18  * information: Portions Copyright [yyyy] [name of copyright owner]
  19  *
  20  * CDDL HEADER END
  21  */
  22 /*
  23  * ident        "%Z%%M% %I%     %E% SMI"
  24  *
  25  * Copyright 2001-2002 by Sun Microsystems, Inc.  All rights reserved.
  26  * Use is subject to license terms.
  27  */
  28 
  29 package com.sun.dhcpmgr.client;
  30 
  31 import javax.swing.*;
  32 import javax.swing.event.*;
  33 import java.awt.*;
  34 import java.text.MessageFormat;
  35 import java.lang.reflect.InvocationTargetException;
  36 
  37 import com.sun.dhcpmgr.server.DhcpMgr;
  38 import com.sun.dhcpmgr.ui.*;
  39 import com.sun.dhcpmgr.data.*;
  40 import com.sun.dhcpmgr.common.*;
  41 import com.sun.dhcpmgr.bridge.ExistsException;
  42 
  43 /**
  44  * ExportWizard provides an easy-to-use interface for exporting the data
  45  * from one DHCP server to be later imported by another DHCP server, typically
  46  * because the administrator wishes to repartition the workload among DHCP
  47  * servers.
  48  */
  49 public class ExportWizard extends Wizard {
  50 
  51     // Step to collect the networks to be exported
  52     class NetworkStep implements WizardStep {
  53         Box stepBox;
  54         ListPair networkLists;
  55 
  56         public NetworkStep() {
  57             stepBox = Box.createVerticalBox();
  58             stepBox.add(Wizard.createTextArea(
  59                 ResourceStrings.getString("exp_wiz_net_explain"), 9, 45));
  60             stepBox.add(Box.createVerticalStrut(10));
  61             // XXX This try/catch goes away at Snakebite integration
  62             try {
  63             networkLists = new ListPair(
  64                 ResourceStrings.getString("exp_wiz_dont_export"),
  65                 DataManager.get().getNetworks(false),
  66                 ResourceStrings.getString("exp_wiz_export"), networks);
  67             } catch (Throwable t) {
  68                 t.printStackTrace();
  69             }
  70             stepBox.add(networkLists);
  71             stepBox.add(Box.createVerticalGlue());
  72         }
  73 
  74         public String getDescription() {
  75             return ResourceStrings.getString("exp_wiz_net_desc");
  76         }
  77 
  78         public Component getComponent() {
  79             return stepBox;
  80         }
  81 
  82         public void setActive(int direction) {
  83             setForwardEnabled(true);
  84         }
  85 
  86         public boolean setInactive(int direction) {
  87             networks =
  88                 (Network [])networkLists.getRightContents(new Network[0]);
  89             return true;
  90         }
  91     }
  92 
  93     // Step to collect the macros to be exported
  94     class MacroStep implements WizardStep {
  95         Box stepBox;
  96         ListPair macroLists;
  97 
  98         public MacroStep() {
  99             stepBox = Box.createVerticalBox();
 100             stepBox.add(Wizard.createTextArea(
 101                 ResourceStrings.getString("exp_wiz_macros_explain"), 4, 45));
 102             stepBox.add(Box.createVerticalStrut(10));
 103             // XXX This try/catch will go away at Snakebite integration
 104             try {
 105             Macro [] macros = DataManager.get().getMacros(false);
 106             macroNames = new String[macros.length];
 107             for (int i = 0; i < macros.length; ++i) {
 108                 macroNames[i] = macros[i].getKey();
 109             }
 110             macroLists = new ListPair(
 111                 ResourceStrings.getString("exp_wiz_dont_export"), macroNames, 
 112                 ResourceStrings.getString("exp_wiz_export"), null);
 113             } catch (Throwable t) {
 114                 t.printStackTrace();
 115             }
 116             stepBox.add(macroLists);
 117             stepBox.add(Box.createVerticalGlue());
 118         }
 119 
 120         public String getDescription() {
 121             return ResourceStrings.getString("exp_wiz_macro_desc");
 122         }
 123 
 124         public Component getComponent() {
 125             return stepBox;
 126         }
 127 
 128         public void setActive(int direction) {
 129             setForwardEnabled(true);
 130         }
 131 
 132         public boolean setInactive(int direction) {
 133             macroNames = (String [])macroLists.getRightContents(new String[0]);
 134             return true;
 135         }
 136     }
 137 
 138     // Step to collect the options to be exported
 139     class OptionStep implements WizardStep {
 140         Box stepBox;
 141         ListPair optionLists;
 142 
 143         public OptionStep() {
 144             stepBox = Box.createVerticalBox();
 145             stepBox.add(Wizard.createTextArea(
 146                 ResourceStrings.getString("exp_wiz_options_explain"), 4, 45));
 147             stepBox.add(Box.createVerticalStrut(10));
 148             // XXX This try/catch will go away at Snakebite integration
 149             try {
 150             Option[] options = DataManager.get().getOptions(false);
 151             optionNames = new String[options.length];
 152             for (int i = 0; i < options.length; ++i) {
 153                 optionNames[i] = options[i].getKey();
 154             }
 155             optionLists = new ListPair(
 156                 ResourceStrings.getString("exp_wiz_dont_export"), optionNames, 
 157                 ResourceStrings.getString("exp_wiz_export"), null);
 158             } catch (Throwable t) {
 159                 t.printStackTrace();
 160             }
 161             stepBox.add(optionLists);
 162             stepBox.add(Box.createVerticalGlue());
 163         }
 164 
 165         public String getDescription() {
 166             return ResourceStrings.getString("exp_wiz_option_desc");
 167         }
 168 
 169         public Component getComponent() {
 170             return stepBox;
 171         }
 172 
 173         public void setActive(int direction) {
 174             setForwardEnabled(true);
 175         }
 176 
 177         public boolean setInactive(int direction) {
 178             optionNames =
 179                 (String [])optionLists.getRightContents(new String[0]);
 180             return true;
 181         }
 182     }
 183 
 184     // Step to collect the name of the file to which data is exported
 185     class FileStep implements WizardStep {
 186         JPanel stepPanel;
 187         NoSpaceField pathField;
 188         JCheckBox deleteBox;
 189 
 190         public FileStep() {
 191             GridBagLayout bag = new GridBagLayout();
 192             GridBagConstraints con = new GridBagConstraints();
 193             con.insets = new Insets(2, 2, 2, 2);
 194             con.gridx = con.gridy = 0;
 195             con.gridwidth = 2;
 196             con.gridheight = 1;
 197             con.weightx = 0;
 198             con.weighty = 0.5;
 199             con.fill = GridBagConstraints.BOTH;
 200             con.anchor = GridBagConstraints.NORTHWEST;
 201             stepPanel = new JPanel(bag);
 202             Component c = Wizard.createTextArea(
 203                 ResourceStrings.getString("exp_wiz_file_explain"), 4, 45);
 204             bag.setConstraints(c, con);
 205             stepPanel.add(c);
 206 
 207             Mnemonic mnFile =
 208                 new Mnemonic(ResourceStrings.getString("exp_wiz_file_label"));
 209             JLabel l = new JLabel(mnFile.getString());
 210             l.setLabelFor(stepPanel);
 211             l.setToolTipText(mnFile.getString());
 212             l.setDisplayedMnemonic(mnFile.getMnemonic());
 213 
 214             con.gridwidth = con.gridheight = 1;
 215             con.fill = GridBagConstraints.NONE;
 216             con.weighty = 0;
 217             ++con.gridy;
 218             bag.setConstraints(l, con);
 219             stepPanel.add(l);
 220 
 221             pathField = new NoSpaceField(exportPath);
 222             l.setLabelFor(pathField);
 223             ++con.gridx;
 224             con.weightx = 1.0;
 225             con.fill = GridBagConstraints.HORIZONTAL;
 226             bag.setConstraints(pathField, con);
 227             stepPanel.add(pathField);
 228 
 229             c = Box.createVerticalStrut(10);
 230             ++con.gridy;
 231             con.gridx = 0;
 232             con.weightx = 0;
 233             bag.setConstraints(c, con);
 234             stepPanel.add(c);
 235 
 236             c = Wizard.createTextArea(
 237                     ResourceStrings.getString("exp_wiz_delete_explain"), 4, 45);
 238             ++con.gridy;
 239             con.gridx = 0;
 240             con.weightx = 1.0;
 241             con.weighty = 0.5;
 242             con.gridwidth = 2;
 243             con.fill = GridBagConstraints.BOTH;
 244             bag.setConstraints(c, con);
 245             stepPanel.add(c);
 246 
 247             deleteBox = new JCheckBox(
 248                 ResourceStrings.getString("exp_wiz_delete_exported"), false);
 249             deleteBox.setToolTipText(
 250                 ResourceStrings.getString("exp_wiz_delete_exported"));
 251             con.gridx = 0;
 252             ++con.gridy;
 253             con.fill = GridBagConstraints.HORIZONTAL;
 254             bag.setConstraints(deleteBox, con);
 255             stepPanel.add(deleteBox);
 256 
 257             c = Box.createVerticalGlue();
 258             ++con.gridy;
 259             con.weighty = 1.0;
 260             bag.setConstraints(c, con);
 261             stepPanel.add(c);
 262 
 263             // Enable forward only if something is entered in the file field
 264             pathField.getDocument().addDocumentListener(new DocumentListener() {
 265                 public void insertUpdate(DocumentEvent e) {
 266                     setForwardEnabled(pathField.getText().length() != 0);
 267                 }
 268                 public void changedUpdate(DocumentEvent e) {
 269                     insertUpdate(e);
 270                 }
 271                 public void removeUpdate(DocumentEvent e) {
 272                     insertUpdate(e);
 273                 }
 274             });
 275 
 276         }
 277 
 278         public String getDescription() {
 279             return ResourceStrings.getString("exp_wiz_file_desc");
 280         }
 281 
 282         public Component getComponent() {
 283             return stepPanel;
 284         }
 285 
 286         public void setActive(int direction) {
 287             pathField.setText(exportPath);
 288             deleteBox.setSelected(deleteData);
 289         }
 290 
 291         public boolean setInactive(int direction) {
 292             exportPath = pathField.getText();
 293             deleteData = deleteBox.isSelected();
 294             return true;
 295         }
 296     }
 297 
 298     // Review everything before doing it
 299     class ReviewStep implements WizardStep {
 300         JPanel mainPanel;
 301         JList networkList, macroList, optionList;
 302         JLabel exportLabel, deleteLabel;
 303 
 304         public ReviewStep() {
 305             mainPanel = new JPanel(new BorderLayout());
 306             mainPanel.add(Wizard.createTextArea(
 307                 ResourceStrings.getString("exp_wiz_review_explain"), 6, 45),
 308                 BorderLayout.NORTH);
 309             JPanel fieldPanel = new JPanel(new FieldLayout());
 310 
 311             Mnemonic mnNets =
 312                 new Mnemonic(ResourceStrings.getString("exp_wiz_review_nets"));
 313             JLabel revNetsLbl = new JLabel(mnNets.getString());
 314             revNetsLbl.setToolTipText(mnNets.getString());
 315             fieldPanel.add(revNetsLbl, FieldLayout.LABELTOP);
 316             networkList = new JList();
 317             revNetsLbl.setLabelFor(networkList);
 318             JScrollPane scrollPane = new JScrollPane(networkList);
 319             fieldPanel.add(scrollPane, FieldLayout.FIELD);
 320             revNetsLbl.setDisplayedMnemonic(mnNets.getMnemonic());
 321 
 322             Mnemonic mnMacros =
 323                 new Mnemonic(ResourceStrings.getString(
 324                 "exp_wiz_review_macros"));
 325             JLabel revMacLbl = new JLabel(mnMacros.getString());
 326             revMacLbl.setToolTipText(mnMacros.getString());
 327             fieldPanel.add(revMacLbl, FieldLayout.LABELTOP);
 328             macroList = new JList();
 329             revMacLbl.setLabelFor(macroList);
 330             scrollPane = new JScrollPane(macroList);
 331             fieldPanel.add(scrollPane, FieldLayout.FIELD);
 332             revMacLbl.setDisplayedMnemonic(mnMacros.getMnemonic());
 333 
 334             Mnemonic mnOpt =
 335                 new Mnemonic(ResourceStrings.getString(
 336                 "exp_wiz_review_options"));
 337             JLabel optLbl = new JLabel(mnOpt.getString());
 338             fieldPanel.add(optLbl, FieldLayout.LABELTOP);
 339             optLbl.setToolTipText(mnOpt.getString());
 340             optionList = new JList();
 341             optLbl.setLabelFor(optionList);
 342             scrollPane = new JScrollPane(optionList);
 343             fieldPanel.add(scrollPane, FieldLayout.FIELD);
 344             optLbl.setDisplayedMnemonic(mnOpt.getMnemonic());
 345 
 346             Mnemonic mnFileRvw =
 347                 new Mnemonic(ResourceStrings.getString("exp_wiz_file_label"));
 348             JLabel fileLbl =
 349                 new JLabel(mnFileRvw.getString());
 350             fileLbl.setLabelFor(fieldPanel);
 351             fileLbl.setToolTipText(mnFileRvw.getString());
 352             fieldPanel.add(fileLbl, FieldLayout.LABEL);
 353             exportLabel = new JLabel();
 354             exportLabel.setForeground(Color.black);
 355             fieldPanel.add(exportLabel, FieldLayout.FIELD);
 356 
 357             JLabel delLbl = 
 358                 new JLabel(ResourceStrings.getString("exp_wiz_delete_label"));
 359             delLbl.setLabelFor(fieldPanel);  
 360             delLbl.setToolTipText(ResourceStrings.getString(
 361                 "exp_wiz_delete_label"));
 362             fieldPanel.add(delLbl, FieldLayout.LABEL);
 363 
 364             deleteLabel = new JLabel();
 365             deleteLabel.setForeground(Color.black);
 366             fieldPanel.add(deleteLabel, FieldLayout.FIELD);
 367 
 368             mainPanel.add(fieldPanel, BorderLayout.CENTER);
 369         }
 370 
 371         public String getDescription() {
 372             return ResourceStrings.getString("exp_wiz_review_desc");
 373         }
 374 
 375         public Component getComponent() {
 376             return mainPanel;
 377         }
 378 
 379         public void setActive(int direction) {
 380             networkList.setListData(networks);
 381             macroList.setListData(macroNames);
 382             optionList.setListData(optionNames);
 383             exportLabel.setText(exportPath);
 384             if (deleteData) {
 385                 deleteLabel.setText(ResourceStrings.getString("yes"));
 386             } else {
 387                 deleteLabel.setText(ResourceStrings.getString("no"));
 388             }
 389             setFinishEnabled(true);
 390         }
 391 
 392         public boolean setInactive(int direction) {
 393             // Nothing to do here
 394             return true;
 395         }
 396     }
 397 
 398     /*
 399      * Display an error message in its own thread.  This allows a task running
 400      * in a non-GUI thread to get the message displayed by the toolkit.
 401      */
 402     class ErrorDisplay implements Runnable {
 403         Object [] objs;
 404 
 405         public ErrorDisplay(Object [] objs) {
 406             this.objs = objs;
 407         }
 408 
 409         public void run() {
 410             JOptionPane.showMessageDialog(ExportWizard.this, objs,
 411                 ResourceStrings.getString("server_error_title"),
 412                 JOptionPane.ERROR_MESSAGE);
 413         }
 414     }
 415 
 416     /*
 417      * Display a warning message in its own thread.  This allows a task running
 418      * in a non-GUI thread to get the message displayed by the toolkit.
 419      */
 420     class WarningDisplay implements Runnable {
 421         Object [] objs;
 422 
 423         public WarningDisplay(Object [] objs) {
 424             this.objs = objs;
 425         }
 426 
 427         public void run() {
 428             JOptionPane.showMessageDialog(ExportWizard.this, objs,
 429                 ResourceStrings.getString("server_warning_title"),
 430                 JOptionPane.WARNING_MESSAGE);
 431         }
 432     }
 433 
 434     /*
 435      * Class to ask the user whether the export file should be forcibly
 436      * overwritten if it already exists.  We default to not overwriting
 437      * export files.  This is a Runnable in order to allow it to be displayed
 438      * by the export thread, which is a non-GUI thread.
 439      */
 440     class OverwritePrompter implements Runnable {
 441         /*
 442          * overwrite member is public so we can access it directly since run()
 443          * can't return the user's input.
 444          */
 445         public boolean overwrite;
 446 
 447         public OverwritePrompter() {
 448             overwrite = false;
 449         }
 450 
 451         public void run() {
 452             int ret = JOptionPane.showConfirmDialog(
 453                 ExportWizard.this,
 454                 ResourceStrings.getString("exp_overwrite"),
 455                 ResourceStrings.getString("exp_overwrite_title"),
 456                 JOptionPane.YES_NO_OPTION,
 457                 JOptionPane.QUESTION_MESSAGE);
 458             // Return true if the user clicked Yes
 459             overwrite = (ret == JOptionPane.YES_OPTION);
 460         }
 461     }
 462 
 463     private Network [] networks = new Network[0];
 464     private String [] macroNames = new String[0];
 465     private String [] optionNames = new String[0];
 466     private boolean deleteData = false;
 467     private String exportPath = "";
 468 
 469     public ExportWizard(Frame owner) {
 470         super(owner, "");
 471         setTitle(ResourceStrings.getString("export_wiz_title"));
 472 
 473         // Insert steps in order of execution
 474         addStep(new NetworkStep());
 475         addStep(new MacroStep());
 476         addStep(new OptionStep());
 477         addStep(new FileStep());
 478         addStep(new ReviewStep());
 479 
 480         showFirstStep();
 481     }
 482 
 483     /*
 484      * Execute the export.  This is relatively complicated because we want to
 485      * run the actual export in a background thread so that the whole GUI isn't
 486      * tied up during the export.  Also, the actual export logic is implemented
 487      * by the ExportController, and we provide an Exporter implementation
 488      * which allows it to interact with the user.  So, the ExportController is
 489      * executed in a background thread, and the callbacks implemented in the
 490      * Exporter must each use SwingUtilities.invoke* methods to ask for the
 491      * UI updates to happen.  In the case of the progress display, the
 492      * ProgressManager class already implements that logic so it's simpler
 493      * than the rest of the interactions.
 494      */
 495     public void doFinish() {
 496         /*
 497          * This runnable serves merely to allow the background thread used for
 498          * export to tell the GUI that it's done.
 499          */
 500         final Runnable finisher = new Runnable() {
 501             public void run() {
 502                 reallyFinish();
 503             }
 504         };
 505 
 506         /*
 507          * Create callback interface used by ExportController to interact with
 508          * the user.
 509          */
 510         Exporter exporter = new Exporter() {
 511             ProgressManager progress;
 512             String [] errObjs = new String[] {
 513                 ResourceStrings.getString("exp_error_occurred"),
 514                 ""
 515             };
 516 
 517             // Set up the progress display
 518             public void initializeProgress(int length) {
 519                 progress = new ProgressManager(ExportWizard.this,
 520                     ResourceStrings.getString("exp_progress_title"), "", 0,
 521                     length);
 522             }
 523 
 524             // Update progress to current point, updating message
 525             public void updateProgress(int done, String message)
 526                     throws InterruptedException {
 527                 progress.update(done, message);
 528             }
 529 
 530             // Display an error message
 531             public void displayError(String message) {
 532                 errObjs[1] = message;
 533                 try {
 534                     SwingUtilities.invokeAndWait(new ErrorDisplay(errObjs));
 535                 } catch (InvocationTargetException ex2) {
 536                     // ErrorDisplay threw an exception; give up!
 537                     ex2.printStackTrace();
 538                 } catch (InterruptedException ex2) {
 539                     // ErrorDisplay was interrupted; give up!
 540                     ex2.printStackTrace();
 541                 }
 542             }
 543 
 544             // Display a bunch of error messages in a table
 545             public void displayErrors(String msg, String label,
 546                     ActionError [] errs) {
 547                 ErrorTable errTable = new ErrorTable(label);
 548                 errTable.setErrors(errs);
 549                 JScrollPane scrollPane = new JScrollPane(errTable);
 550                 Object [] warnObjs = new Object [] { msg, scrollPane };
 551                 try {
 552                     SwingUtilities.invokeAndWait(new WarningDisplay(warnObjs));
 553                 } catch (InvocationTargetException e) {
 554                     // WarningDisplay threw an exception; just dump it
 555                     e.printStackTrace();
 556                 } catch (InterruptedException e) {
 557                     // WarningDisplay was interrupted; just dump it
 558                     e.printStackTrace();
 559                 }
 560             }
 561         };
 562 
 563         /*
 564          * Create the export controller and set parameters.  Use final so
 565          * that the exportThread can reference it.
 566          */
 567         final ExportController exportController = new ExportController(exporter,
 568             DataManager.get().getServer());
 569         exportController.setUser(System.getProperty("user.name"));
 570         exportController.setFile(exportPath);
 571         exportController.setOptions(optionNames);
 572         exportController.setMacros(macroNames);
 573         exportController.setNetworks(networks);
 574 
 575         // Now create the thread that does the exporting
 576         Thread exportThread = new Thread() {
 577             public void run() {
 578                 OverwritePrompter prompter = new OverwritePrompter();
 579                 while (true) {
 580                     try {
 581                         /*
 582                          * Controller will return true if it completed
 583                          * successfully, in which case we want to exit the
 584                          * wizard; if it returns false, just exit this
 585                          * thread but leave the wizard up.
 586                          */
 587                         if (exportController.exportData(deleteData,
 588                                 prompter.overwrite)) {
 589                             SwingUtilities.invokeLater(finisher);
 590                         }
 591                         return;
 592                     } catch (ExistsException e) {
 593                         // Export file already existed and overwrite was false
 594                         try {
 595                             SwingUtilities.invokeAndWait(prompter);
 596                             /*
 597                              * If user said not to overwrite, then exit
 598                              * this thread but leave wizard up.  Otherwise just
 599                              * let the while loop try the export again.
 600                              */
 601                             if (!prompter.overwrite) {
 602                                 return;
 603                             }
 604                         } catch (Throwable t) {
 605                             /*
 606                              * We can get an interrupt or prompter could
 607                              * throw an exception; the only reasonable
 608                              * thing to do at this point is just display
 609                              * the stack and return.
 610                              */
 611                             t.printStackTrace();
 612                             return;
 613                         }
 614                     }
 615                 }
 616             }
 617         };
 618                 
 619         // Now run the export thread    
 620         exportThread.start();
 621     }
 622 
 623     protected void reallyFinish() {
 624         super.doFinish();
 625     }
 626 
 627     public void doHelp() {
 628         DhcpmgrApplet.showHelp("export_wizard");
 629     }
 630 }