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 }