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 }