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 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 */
26 package com.sun.dhcpmgr.client;
27
28 import com.sun.dhcpmgr.ui.*;
29 import com.sun.dhcpmgr.data.*;
30 import com.sun.dhcpmgr.bridge.BridgeException;
31 import com.sun.dhcpmgr.bridge.NoEntryException;
32 import com.sun.dhcpmgr.bridge.ExistsException;
33 import com.sun.dhcpmgr.bridge.HostExistsException;
34 import com.sun.dhcpmgr.bridge.NoTableException;
35 import com.sun.dhcpmgr.server.DhcpNetMgr;
36
37 import javax.swing.*;
38 import javax.swing.table.*;
39 import javax.swing.event.*;
40 import javax.swing.border.*;
41
42 import java.awt.*;
43 import java.awt.event.*;
44 import java.util.*;
45 import java.text.MessageFormat;
46 import java.net.*;
47
48 /**
49 * A wizard to configure a group of addresses.
50 */
51 public class AddressWizard extends Wizard {
52 private Network network;
53 private int number = 10;
54 private String comment = "";
55 private String server = DataManager.get().getShortServerName();
56 private IPAddress serverIP;
57 private IPAddress startAddress;
58 private String macro = DataManager.get().getShortServerName();
59 private boolean unusable = false;
60 private boolean dynamic = true;
61 private WizardTableModel addressTableModel;
62 private Macro noMacro;
63
64 class Address {
65 IPAddress addr;
66 String name;
67
68 public Address() {
69 addr = null;
70 name = "";
71 }
72
73 public Address(int a, String n) {
74 name = n;
75 setAddr(a);
76 }
77
78 public Address(String a, String n) {
79 name = n;
80 setAddr(a);
81 }
82
83 public void setAddr(int a) {
84 addr = new IPAddress(a);
85 }
86
87 public void setAddr(String a) {
88 try {
89 addr = new IPAddress(a);
90 } catch (ValidationException e) {
91 // Do nothing
92 }
93 }
94
95 public String toString() {
96 return addr.getHostAddress();
97 }
98 }
99
100 class WizardTableModel extends AbstractTableModel {
101 private Vector addrs = new Vector();
102
103 public int getRowCount() {
104 return addrs.size();
105 }
106
107 public int getColumnCount() {
108 return 2;
109 }
110
111 public Object getValueAt(int row, int column) {
112 if (column == 0) {
113 return ((Address)addrs.elementAt(row)).addr;
114 } else {
115 return ((Address)addrs.elementAt(row)).name;
116 }
117 }
118
119 public Class getColumnClass(int column) {
120 if (column == 0) {
121 return IPAddress.class;
122 } else {
123 return String.class;
124 }
125 }
126
127 public String getColumnName(int column) {
128 if (column == 0) {
129 return ResourceStrings.getString("address_column");
130 } else {
131 return ResourceStrings.getString("client_name_column");
132 }
133 }
134
135 public long generateAddresses() {
136 if (!network.containsAddress(startAddress)) {
137 return 0;
138 }
139
140 int net = network.getAddress().intValue();
141 int mask = network.getMask().intValue();
142 int start = startAddress.intValue();
143
144 addrs.removeAllElements();
145 long max = (long)(net + ~mask) & 0xffffffffL;
146 int count = 0;
147 int index = start - net;
148 if (index == 0) {
149 // Don't try allocating the network address as a client address
150 ++index;
151 }
152 DhcpClientRecord [] clients = null;
153 try {
154 /*
155 * Sort the data so we can generate the list of addresses
156 * with a minimal number of comparisons here. First, though,
157 * clone the array so that sorting won't affect the original
158 * data set and throw off the main display.
159 */
160 clients = (DhcpClientRecord [])DataManager.get().getClients(
161 network.getAddress().toString(), false).clone();
162 Arrays.sort(clients);
163 } catch (Throwable e) {
164 // XXX What to do here???
165 e.printStackTrace();
166 }
167 int base = 0;
168 long searchAddress = 0;
169 while (count < number) {
170 long address = (long)(net + index) & 0xffffffffL;
171 if (address == max) {
172 // We finished searching before satisfying the request
173 break;
174 }
175 /*
176 * If clients == null then this is an empty network,
177 * so searching for holes is unnecessary
178 */
179 if (clients != null) {
180 // Advance search pointer past lower-numbered addresses
181 while ((base < clients.length)
182 && ((searchAddress =
183 clients[base].getBinaryAddress()) < address)) {
184 ++base;
185 }
186 }
187 if (searchAddress != address) {
188 // found an empty slot; create the address
189 addrs.addElement(new Address((int)address, ""));
190 ++count;
191 }
192 ++index;
193 }
194
195 // Inform UI that the data is ready.
196 fireTableDataChanged();
197 return count;
198 }
199
200 public Address getAddressAt(int index) {
201 return (Address)addrs.elementAt(index);
202 }
203 }
204
205 // This step selects the number of addresses and a comment
206 class NumberStep implements WizardStep {
207 private Box stepBox;
208 private IntegerField addressCount;
209 private JTextField commentField;
210
211 public NumberStep() {
212 stepBox = Box.createVerticalBox();
213
214 // Explanatory text at the top
215 stepBox.add(Wizard.createTextArea(
216 ResourceStrings.getString("add_wiz_explain"), 4, 45));
217 stepBox.add(Box.createVerticalStrut(10));
218 stepBox.add(Box.createVerticalGlue());
219
220 // Get the number of addresses to create
221 stepBox.add(Wizard.createTextArea(
222 ResourceStrings.getString("add_wiz_count_explain"), 1, 45));
223
224 Mnemonic mnCount =
225 new Mnemonic(ResourceStrings.getString("add_wiz_count_label"));
226 JLabel label = new JLabel(mnCount.getString());
227 addressCount = new IntegerField(); // Ensure numeric input
228 addressCount.setMaximumSize(addressCount.getPreferredSize());
229
230 label.setLabelFor(addressCount);
231 label.setToolTipText(mnCount.getString());
232 label.setDisplayedMnemonic(mnCount.getMnemonic());
233
234 Box box = Box.createHorizontalBox();
235 box.add(Box.createHorizontalStrut(10));
236 box.add(label);
237 box.add(Box.createHorizontalStrut(5));
238 box.add(addressCount);
239 box.add(Box.createHorizontalGlue());
240 stepBox.add(box);
241
242 stepBox.add(Box.createVerticalStrut(10));
243 stepBox.add(Box.createVerticalGlue());
244 stepBox.add(Wizard.createTextArea(
245 ResourceStrings.getString("add_wiz_comment_explain"), 2, 45));
246
247 // Let user supply a comment
248 Mnemonic mnComm =
249 new Mnemonic(ResourceStrings.getString(
250 "add_wiz_comment_label"));
251 label = new JLabel(mnComm.getString());
252 commentField = new JTextField("", 20);
253
254 label.setLabelFor(commentField);
255 label.setToolTipText(mnComm.getString());
256 label.setDisplayedMnemonic(mnComm.getMnemonic());
257
258 commentField.setMaximumSize(commentField.getPreferredSize());
259 box = Box.createHorizontalBox();
260 box.add(Box.createHorizontalStrut(10));
261 box.add(label);
262 box.add(Box.createHorizontalStrut(5));
263 box.add(commentField);
264 stepBox.add(box);
265 stepBox.add(Box.createVerticalGlue());
266
267 /*
268 * This listener ensures that the forward button is enabled only
269 * when there is a count of addresses in the addressCount field.
270 */
271 addressCount.getDocument().addDocumentListener(
272 new DocumentListener() {
273 public void insertUpdate(DocumentEvent e) {
274 setForwardEnabled(e.getDocument().getLength() != 0);
275 }
276 public void changedUpdate(DocumentEvent e) {
277 insertUpdate(e);
278 }
279 public void removeUpdate(DocumentEvent e) {
280 insertUpdate(e);
281 }
282 });
283 }
284
285 public String getDescription() {
286 return ResourceStrings.getString("add_wiz_number_desc");
287 }
288
289 public Component getComponent() {
290 return stepBox;
291 }
292
293 public void setActive(int direction) {
294 addressCount.setValue(number);
295 commentField.setText(comment);
296 setForwardEnabled(addressCount.getValue() != 0);
297 }
298
299 public boolean setInactive(int direction) {
300 number = addressCount.getValue();
301 if (number == 0) {
302 /*
303 * Going forward with 0 addresses makes no sense,
304 * display error and veto the move.
305 */
306 JOptionPane.showMessageDialog(AddressWizard.this,
307 ResourceStrings.getString("add_wiz_count_error"),
308 ResourceStrings.getString("error_message"),
309 JOptionPane.ERROR_MESSAGE);
310 return false;
311 }
312 comment = commentField.getText();
313 return true;
314 }
315 }
316
317 // This step selects the server and starting address
318 class ServerStep implements WizardStep {
319 private Box stepBox;
320 private IPAddressField startField;
321 private HostnameField serverField;
322
323 public ServerStep() {
324 stepBox = Box.createVerticalBox();
325
326 // Explanatory text at the top
327 stepBox.add(Wizard.createTextArea(
328 ResourceStrings.getString("add_wiz_server_explain"), 1, 45));
329
330 // Server to own these addresses
331 Mnemonic mnMan =
332 new Mnemonic(ResourceStrings.getString("add_wiz_server_label"));
333 JLabel jl = new JLabel(mnMan.getString());
334 Box box = Box.createHorizontalBox();
335 box.add(jl);
336 box.add(Box.createHorizontalStrut(5));
337 serverField = new HostnameField("", 15);
338 jl.setLabelFor(serverField);
339 jl.setToolTipText(mnMan.getString());
340 jl.setDisplayedMnemonic(mnMan.getMnemonic());
341
342 serverField.setMaximumSize(serverField.getPreferredSize());
343 box.add(serverField);
344 box.add(Box.createHorizontalGlue());
345 stepBox.add(box);
346
347 // Add some spacing
348 stepBox.add(Box.createVerticalStrut(5));
349 stepBox.add(Box.createVerticalGlue());
350
351 // Starting address
352 stepBox.add(Wizard.createTextArea(
353 ResourceStrings.getString("add_wiz_start_explain"), 2, 45));
354 box = Box.createHorizontalBox();
355
356 Mnemonic mnStart =
357 new Mnemonic(ResourceStrings.getString("add_wiz_start_label"));
358 JLabel startLbl = new JLabel(mnStart.getString());
359 box.add(startLbl);
360 box.add(Box.createHorizontalStrut(5));
361 startField = new IPAddressField(); // Ensure it's an IP address
362 startLbl.setLabelFor(startField);
363 startLbl.setToolTipText(mnStart.getString());
364 startLbl.setDisplayedMnemonic(mnStart.getMnemonic());
365
366 startField.setMaximumSize(startField.getPreferredSize());
367 box.add(startField);
368 stepBox.add(box);
369
370 DocumentListener docListener = new DocumentListener() {
371 public void insertUpdate(DocumentEvent e) {
372 setForwardEnabled((startField.getText().length() != 0)
373 && (serverField.getText().length() != 0));
374 }
375 public void changedUpdate(DocumentEvent e) {
376 insertUpdate(e);
377 }
378 public void removeUpdate(DocumentEvent e) {
379 insertUpdate(e);
380 }
381 };
382
383 startField.getDocument().addDocumentListener(docListener);
384 serverField.getDocument().addDocumentListener(docListener);
385 }
386
387 public String getDescription() {
388 return ResourceStrings.getString("add_wiz_server_desc");
389 }
390
391 public Component getComponent() {
392 return stepBox;
393 }
394
395 public void setActive(int direction) {
396 serverField.setText(server);
397 startField.setValue(startAddress);
398 setForwardEnabled(true);
399 }
400
401 public boolean setInactive(int direction) {
402 if (direction == FORWARD) {
403 // Validate that address is on the network we're working on
404 IPAddress a = startField.getValue();
405 if (a == null) {
406 // Not a valid address at all
407 MessageFormat form = new MessageFormat(
408 ResourceStrings.getString("add_wiz_invalid_address"));
409 Object [] args = new Object[1];
410 args[0] = startField.getText();
411 JOptionPane.showMessageDialog(AddressWizard.this,
412 form.format(args),
413 ResourceStrings.getString("input_error"),
414 JOptionPane.ERROR_MESSAGE);
415 return false;
416 } else if (!network.containsAddress(a)) {
417 // Address is not on network
418 MessageFormat form = new MessageFormat(
419 ResourceStrings.getString("bad_network_address"));
420 Object [] args = new Object[2];
421 args[0] = startField.getText();
422 args[1] = network.getAddress();
423 JOptionPane.showMessageDialog(AddressWizard.this,
424 form.format(args),
425 ResourceStrings.getString("input_error"),
426 JOptionPane.ERROR_MESSAGE);
427 return false;
428 }
429 try {
430 serverIP = new IPAddress(serverField.getText());
431 } catch (Throwable e) {
432 /*
433 * Unknown hostname, probably, so put up the message and
434 * decline to continue
435 */
436 MessageFormat form = new MessageFormat(
437 ResourceStrings.getString("bad_server_name"));
438 Object [] args = new Object[1];
439 args[0] = serverField.getText();
440 JOptionPane.showMessageDialog(AddressWizard.this,
441 form.format(args),
442 ResourceStrings.getString("error_message"),
443 JOptionPane.ERROR_MESSAGE);
444 return false;
445 }
446 }
447 server = serverField.getText();
448 startAddress = startField.getValue();
449 return true;
450 }
451 }
452
453 // This step confirms the list of addresses to be generated
454 class ConfirmStep implements WizardStep {
455 private JPanel stepPanel;
456 private JTable addressTable;
457
458 public ConfirmStep() {
459 stepPanel = new JPanel(new BorderLayout(10, 10));
460
461 // Explanatory text at the top
462 stepPanel.add(Wizard.createTextArea(
463 ResourceStrings.getString("add_wiz_confirm_explain"), 3, 45),
464 BorderLayout.NORTH);
465
466 // Label the table
467 JPanel panel = new JPanel(new BorderLayout());
468 panel.setBorder(BorderFactory.createEmptyBorder(0, 15, 0, 15));
469 Mnemonic mnIP =
470 new Mnemonic(ResourceStrings.getString(
471 "add_wiz_confirm_label"));
472 JLabel label = new JLabel(mnIP.getString());
473 panel.add(label, BorderLayout.NORTH);
474
475 // Display the addresses in a table
476 addressTable = new JTable(addressTableModel);
477
478 label.setLabelFor(addressTable);
479 label.setToolTipText(mnIP.getString());
480 label.setDisplayedMnemonic(mnIP.getMnemonic());
481
482 addressTable.setDefaultRenderer(IPAddress.class,
483 new ExtendedCellRenderer());
484
485 // Table is not selectable in any way
486 addressTable.setRowSelectionAllowed(false);
487 addressTable.setColumnSelectionAllowed(false);
488 addressTable.setCellSelectionEnabled(false);
489
490 // Wrap in a scroll pane so column headings display
491 JScrollPane scrollPane = new JScrollPane(addressTable);
492 panel.add(scrollPane, BorderLayout.CENTER);
493 stepPanel.add(panel, BorderLayout.CENTER);
494 }
495
496 public String getDescription() {
497 return ResourceStrings.getString("add_wiz_confirm_desc");
498 }
499
500 public Component getComponent() {
501 return stepPanel;
502 }
503
504 public void setActive(int direction) {
505 /*
506 * If we're activating coming from the previous step,
507 * generate the address list
508 */
509 if (direction == FORWARD) {
510 long count = addressTableModel.generateAddresses();
511 // Error if no addresses could be generated
512 if (count == 0) {
513 JOptionPane.showMessageDialog(AddressWizard.this,
514 ResourceStrings.getString("add_wiz_none_available"),
515 ResourceStrings.getString("error_message"),
516 JOptionPane.ERROR_MESSAGE);
517 setForwardEnabled(false);
518 } else {
519 if (count != number) {
520 /*
521 * Warn if we couldn't generate the number of addresses
522 * requested
523 */
524 MessageFormat form = new MessageFormat(
525 ResourceStrings.getString(
526 "generate_addresses_warning"));
527 Object [] args = new Object[2];
528 args[0] = new Long(count);
529 args[1] = new Long(number);
530 JOptionPane.showMessageDialog(AddressWizard.this,
531 form.format(args),
532 ResourceStrings.getString("warning"),
533 JOptionPane.WARNING_MESSAGE);
534 }
535 setForwardEnabled(true);
536 }
537 } else {
538 setForwardEnabled(true);
539 }
540 }
541
542 public boolean setInactive(int direction) {
543 return true; // Nothing to do when leaving
544 }
545 }
546
547 // This step selects the macro and flags
548 class ConfigureStep implements WizardStep {
549
550 // Model class for the macro list
551 class MacroListModel extends AbstractListModel
552 implements ComboBoxModel {
553 private Object currentValue;
554 private Macro data[] = null;
555
556 public int getSize() {
557 if (data == null) {
558 try {
559 // If we don't have data yet, grab currently cached list
560 data = DataManager.get().getMacros(false);
561 } catch (NoTableException e) {
562 // can function without table
563 } catch (Throwable e) {
564 e.printStackTrace();
565 }
566 }
567
568 if (data == null || data.length == 0) {
569 return 1;
570 } else {
571 return data.length+1;
572 }
573 }
574
575 public Object getElementAt(int index) {
576 if (data == null) {
577 try {
578 // If we don't have data yet, grab currently cached list
579 data = DataManager.get().getMacros(false);
580 } catch (NoTableException e) {
581 // can function without table
582 } catch (Throwable e) {
583 e.printStackTrace();
584 }
585 }
586 if (index == 0) {
587 return noMacro.getKey();
588 } else {
589 return data[index-1].getKey();
590 }
591 }
592
593 public void setSelectedItem(Object anItem) {
594 currentValue = noMacro.getKey();
595 for (int i = 0; data != null && i < data.length; i++) {
596 if (((String)(anItem)).equals(((Macro)data[i]).getKey())) {
597 currentValue = anItem;
598 }
599 }
600 fireContentsChanged(this, -1, -1);
601 }
602
603 public Object getSelectedItem() {
604 return currentValue;
605 }
606
607 public Macro getMacroAt(int index) {
608 if (index == 0) {
609 return noMacro;
610 } else {
611 return data[index-1];
612 }
613 }
614 }
615
616 private Box stepBox;
617 private JComboBox macroBox;
618 private MacroListModel macroBoxModel;
619 private JButton viewButton;
620 private JCheckBox unusableBox;
621
622 public ConfigureStep() {
623 stepBox = Box.createVerticalBox();
624
625 // Start with some explanatory text
626 JComponent component = Wizard.createTextArea(
627 ResourceStrings.getString("add_wiz_macro_explain"), 3, 45);
628 component.setAlignmentX(Component.LEFT_ALIGNMENT);
629 stepBox.add(component);
630 // Add some spacing
631 stepBox.add(Box.createVerticalStrut(10));
632
633 // Let 'em select the macro to use
634 Mnemonic mnConf =
635 new Mnemonic(ResourceStrings.getString("add_wiz_macro_label"));
636 JLabel label = new JLabel(mnConf.getString());
637 label.setAlignmentX(Component.LEFT_ALIGNMENT);
638 stepBox.add(label);
639 JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT));
640 macroBoxModel = new MacroListModel();
641 macroBox = new JComboBox(macroBoxModel);
642
643 label.setLabelFor(macroBox);
644 label.setToolTipText(mnConf.getString());
645 label.setDisplayedMnemonic(mnConf.getMnemonic());
646
647 panel.add(macroBox);
648 // Button to view the contents of the selected macro
649
650 Mnemonic mnView =
651 new Mnemonic(ResourceStrings.getString("add_wiz_view_button"));
652 viewButton = new JButton(mnView.getString());
653 viewButton.setToolTipText(mnView.getString());
654 viewButton.setMnemonic(mnView.getMnemonic());
655
656 panel.add(viewButton);
657 panel.setAlignmentX(Component.LEFT_ALIGNMENT);
658 stepBox.add(panel);
659
660 // Give the option to mark them unusable for now
661 component = Wizard.createTextArea(
662 ResourceStrings.getString("add_wiz_flag_explain"), 2, 45);
663 component.setAlignmentX(Component.LEFT_ALIGNMENT);
664 stepBox.add(component);
665 unusableBox = new JCheckBox(
666 ResourceStrings.getString("add_wiz_unusable_label"));
667 unusableBox.setAlignmentX(Component.LEFT_ALIGNMENT);
668 stepBox.add(unusableBox);
669 stepBox.add(Box.createVerticalGlue());
670
671 // When user presses View, show the macro's contents
672 viewButton.addActionListener(new ActionListener() {
673 public void actionPerformed(ActionEvent e) {
674 /*
675 * viewButton is passed as component relative to which the
676 * dialog should be displayed, keeping user more in
677 * context with the task.
678 */
679 ViewMacroDialog d = new ViewMacroDialog(
680 AddressWizard.this, viewButton,
681 macroBoxModel.getMacroAt(macroBox.getSelectedIndex()));
682 d.pack();
683 d.setVisible(true);
684 }
685 });
686 }
687
688 public String getDescription() {
689 return ResourceStrings.getString("add_wiz_configure_desc");
690 }
691
692 public Component getComponent() {
693 return stepBox;
694 }
695
696 public void setActive(int direction) {
697 macroBox.setSelectedItem(macro);
698 unusableBox.setSelected(unusable);
699 setForwardEnabled(true);
700 }
701
702 public boolean setInactive(int direction) {
703 macro = (String)macroBox.getSelectedItem();
704 unusable = unusableBox.isSelected();
705 return true;
706 }
707 }
708
709 // This step selects the lease type
710 class LeaseStep implements WizardStep {
711 private Box stepBox;
712 private JRadioButton dynamicButton, permanentButton;
713 private ButtonGroup buttonGroup;
714
715 public LeaseStep() {
716 stepBox = Box.createVerticalBox();
717
718 // Start with explanatory text
719 JComponent component = Wizard.createTextArea(
720 ResourceStrings.getString("add_wiz_lease_explain"), 0, 45);
721 component.setAlignmentX(Component.LEFT_ALIGNMENT);
722 stepBox.add(component);
723 stepBox.add(Box.createVerticalStrut(10));
724
725 // User has choice of dynamic or permanent leases
726 Mnemonic mnLease =
727 new Mnemonic(ResourceStrings.getString("add_wiz_lease_label"));
728 JLabel label = new JLabel(mnLease.getString());
729 JPanel panel = new JPanel(new FieldLayout(10, 0));
730 label.setToolTipText(mnLease.getString());
731 label.setDisplayedMnemonic(mnLease.getMnemonic());
732
733 panel.add(FieldLayout.LABEL, label);
734 buttonGroup = new ButtonGroup();
735 dynamicButton = new JRadioButton(
736 ResourceStrings.getString("dynamic"), true);
737 buttonGroup.add(dynamicButton);
738 permanentButton = new JRadioButton(
739 ResourceStrings.getString("permanent"), false);
740 buttonGroup.add(permanentButton);
741 label.setLabelFor(dynamicButton);
742 panel.add(FieldLayout.FIELD, dynamicButton);
743 panel.add(FieldLayout.LABEL, new JLabel(""));
744 panel.add(FieldLayout.FIELD, permanentButton);
745 panel.setAlignmentX(Component.LEFT_ALIGNMENT);
746 stepBox.add(panel);
747 stepBox.add(Box.createVerticalGlue());
748 }
749
750 public String getDescription() {
751 return ResourceStrings.getString("add_wiz_lease_desc");
752 }
753
754 public Component getComponent() {
755 return stepBox;
756 }
757
758 public void setActive(int direction) {
759 dynamicButton.setSelected(dynamic);
760 setForwardEnabled(true);
761 }
762
763 public boolean setInactive(int direction) {
764 dynamic = dynamicButton.isSelected();
765 return true;
766 }
767 }
768
769 // Last chance to check work before committing to it
770 class ReviewStep implements WizardStep {
771 private Box stepBox;
772 private JPanel panel;
773 private JTable addressTable;
774 private JLabel numberLabel;
775 private JLabel commentLabel;
776 private JLabel serverLabel;
777 private JLabel macroLabel;
778 private JLabel flagLabel;
779 private JLabel leaseLabel;
780
781 public ReviewStep() {
782 stepBox = Box.createVerticalBox();
783 stepBox.add(Wizard.createTextArea(
784 ResourceStrings.getString("add_wiz_review_explain"), 4, 45));
785
786 panel = new JPanel(new FieldLayout());
787 JLabel tmpL;
788
789 tmpL = addLabelMnemonic("add_wiz_count_label");
790 numberLabel = addField("20");
791 tmpL.setLabelFor(numberLabel);
792
793 tmpL = addLabelMnemonic("add_wiz_comment_label");
794 commentLabel = addField("Marketing");
795 tmpL.setLabelFor(commentLabel);
796
797 tmpL = addLabelMnemonic("add_wiz_server_label");
798 serverLabel = addField("atlantic");
799 tmpL.setLabelFor(serverLabel);
800
801 tmpL = addLabelMnemonic("add_wiz_macro_label");
802 macroLabel = addField("atlantic");
803 tmpL.setLabelFor(macroLabel);
804
805 tmpL = addLabel("add_wiz_review_unusable");
806 flagLabel = addField("Yes");
807 tmpL.setLabelFor(flagLabel);
808 tmpL.setToolTipText(
809 ResourceStrings.getString("add_wiz_review_unusable"));
810
811 tmpL = addLabelMnemonic("add_wiz_lease_label");
812 leaseLabel = addField(ResourceStrings.getString("dynamic"));
813 tmpL.setLabelFor(leaseLabel);
814
815 panel.setAlignmentX(Component.LEFT_ALIGNMENT);
816 stepBox.add(panel);
817
818 stepBox.add(Box.createVerticalStrut(5));
819
820 Mnemonic mnAdd =
821 new Mnemonic(ResourceStrings.getString(
822 "add_wiz_confirm_label"));
823 JLabel label = new JLabel(mnAdd.getString());
824 stepBox.add(label);
825 stepBox.add(Box.createVerticalStrut(2));
826 addressTable = new JTable(addressTableModel);
827 label.setLabelFor(addressTable);
828 label.setToolTipText(mnAdd.getString());
829 label.setDisplayedMnemonic(mnAdd.getMnemonic());
830
831 addressTable.setDefaultRenderer(IPAddress.class,
832 new ExtendedCellRenderer());
833
834 // Table should not be selectable in any way
835 addressTable.setRowSelectionAllowed(false);
836 addressTable.setColumnSelectionAllowed(false);
837 addressTable.setCellSelectionEnabled(false);
838 JScrollPane scrollPane = new JScrollPane(addressTable);
839 Dimension d = addressTable.getPreferredScrollableViewportSize();
840 d.height = 50;
841 addressTable.setPreferredScrollableViewportSize(d);
842 stepBox.add(scrollPane);
843 stepBox.add(Box.createVerticalGlue());
844 }
845
846 private JLabel addLabel(String s) {
847 JLabel l = new JLabel(ResourceStrings.getString(s));
848 panel.add(FieldLayout.LABEL, l);
849 return l;
850 }
851
852 private JLabel addLabelMnemonic(String s) {
853 Mnemonic mnStr =
854 new Mnemonic(ResourceStrings.getString(s));
855 JLabel l = new JLabel(mnStr.getString());
856 l.setToolTipText(mnStr.getString());
857 panel.add(FieldLayout.LABEL, l);
858 return l;
859 }
860
861 private JLabel addField(String s) {
862 JLabel l = new JLabel(s);
863 l.setForeground(Color.black);
864 panel.add(FieldLayout.FIELD, l);
865 return l;
866 }
867
868 public String getDescription() {
869 return ResourceStrings.getString("add_wiz_review_desc");
870 }
871
872 public Component getComponent() {
873 return stepBox;
874 }
875
876 public void setActive(int direction) {
877 // Use number of addresses actually generated, not requested
878 numberLabel.setText(
879 String.valueOf(addressTableModel.getRowCount()));
880 commentLabel.setText(comment);
881 serverLabel.setText(server);
882 macroLabel.setText(macro);
883 if (unusable) {
884 flagLabel.setText(ResourceStrings.getString("yes"));
885 } else {
886 flagLabel.setText(ResourceStrings.getString("no"));
887 }
888 if (dynamic) {
889 leaseLabel.setText(ResourceStrings.getString("dynamic"));
890 } else {
891 leaseLabel.setText(ResourceStrings.getString("permanent"));
892 }
893 setFinishEnabled(true);
894 }
895
896 public boolean setInactive(int direction) {
897 // Nothing to do
898 return true;
899 }
900 }
901
902 public AddressWizard(Frame owner, Network net) {
903 super(owner, "");
904 setTitle(MessageFormat.format(
905 ResourceStrings.getString("address_wizard_title"), net.toString()));
906
907 network = net;
908 startAddress = network.getAddress();
909
910 try {
911 noMacro = new Macro(ResourceStrings.getString("no_macro_item"));
912 } catch (ValidationException e) {
913 // this should never happen!
914 System.err.println(e.getMessage());
915 }
916 addressTableModel = new WizardTableModel();
917
918 // Create steps in order of appearance
919 addStep(new NumberStep());
920 addStep(new ServerStep());
921 addStep(new ConfirmStep());
922 addStep(new ConfigureStep());
923 addStep(new LeaseStep());
924 addStep(new ReviewStep());
925 showFirstStep();
926 }
927
928 public void doFinish() {
929 /*
930 * Method here is as follows:
931 * 1. Create a ProgressManager which will apprise user of our progress
932 * 2. Create a background thread to execute the add operations
933 * 3. Within the background thread, update the progress monitor
934 * as each address is created.
935 * 4. At completion, the background thread displays the error
936 * output, if any, before it invokes one last runnable which pops
937 * down and cleans up.
938 */
939 // final so that ProgressUpdater can access it
940 final ProgressManager progress = new ProgressManager(this,
941 ResourceStrings.getString("add_wiz_progress"), "", 0,
942 addressTableModel.getRowCount());
943 final Runnable finisher = new Runnable() {
944 public void run() {
945 reallyFinish();
946 }
947 };
948
949 // Here's the thread which does the adds
950 Thread addThread = new Thread() {
951 public void run() {
952 DhcpNetMgr server = DataManager.get().getDhcpNetMgr();
953 // Create a template object which we'll use for all the adds
954 DhcpClientRecord rec = new DhcpClientRecord();
955 rec.setExpiration(new Date(0));
956 rec.setUnusable(unusable);
957 rec.setPermanent(!dynamic);
958 try {
959 rec.setServerIP(serverIP);
960 } catch (ValidationException e) {
961 // Should never happen as we have a valid IP already
962 }
963 if (macro.equals(noMacro.getKey())) {
964 rec.setMacro("");
965 } else {
966 rec.setMacro(macro);
967 }
968 rec.setComment(comment);
969
970 // This is final so it can be used in the errorDisplay Runnable
971 final ErrorTable failedTable = new ErrorTable(
972 ResourceStrings.getString("address_column"),
973 IPAddress.class);
974
975 /*
976 * For each address, create a client record and possibly a
977 * hosts record, log any errors for later consumption.
978 */
979 for (int i = 0; i < addressTableModel.getRowCount(); ++i) {
980 Address addr = addressTableModel.getAddressAt(i);
981 try {
982 rec.setClientIP(addr.addr);
983 rec.setClientName(addr.name);
984 server.addClient(rec, network.toString());
985 progress.update(i+1, addr.addr.toString());
986 } catch (InterruptedException e) {
987 SwingUtilities.invokeLater(finisher);
988 return;
989 } catch (Throwable e) {
990 // Pick the best message for the exception thrown
991 String msg;
992 if (e instanceof ExistsException) {
993 msg = ResourceStrings.getString("address_exists");
994 } else if (e instanceof HostExistsException) {
995 msg = ResourceStrings.getString("host_exists");
996 } else {
997 msg = e.getMessage();
998 }
999 failedTable.addError(addr.addr, msg);
1000 }
1001 }
1002
1003 // If any errors occurred, display them all at once.
1004 if (!failedTable.isEmpty()) {
1005 Runnable errorDisplay = new Runnable() {
1006 public void run() {
1007 Object [] objs = new Object[2];
1008 objs[0] =
1009 ResourceStrings.getString("add_wiz_error");
1010 JScrollPane scrollPane =
1011 new JScrollPane(failedTable);
1012 // Resize the table to something kind of small
1013 Dimension d =
1014 failedTable.
1015 getPreferredScrollableViewportSize();
1016 d.height = 80;
1017 failedTable.setPreferredScrollableViewportSize(d);
1018 objs[1] = scrollPane;
1019 JOptionPane.showMessageDialog(AddressWizard.this,
1020 objs,
1021 ResourceStrings.getString("server_error_title"),
1022 JOptionPane.ERROR_MESSAGE);
1023 }
1024 };
1025 try {
1026 SwingUtilities.invokeAndWait(errorDisplay);
1027 } catch (Throwable e) {
1028 e.printStackTrace();
1029 }
1030 }
1031 SwingUtilities.invokeLater(finisher);
1032 }
1033 };
1034 addThread.start();
1035 }
1036
1037 protected void reallyFinish() {
1038 super.doFinish();
1039 }
1040
1041 public void doHelp() {
1042 DhcpmgrApplet.showHelp("address_wizard");
1043 }
1044 }