001 /*
002 * This file is part of McIDAS-V
003 *
004 * Copyright 2007-2013
005 * Space Science and Engineering Center (SSEC)
006 * University of Wisconsin - Madison
007 * 1225 W. Dayton Street, Madison, WI 53706, USA
008 * https://www.ssec.wisc.edu/mcidas
009 *
010 * All Rights Reserved
011 *
012 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and
013 * some McIDAS-V source code is based on IDV and VisAD source code.
014 *
015 * McIDAS-V is free software; you can redistribute it and/or modify
016 * it under the terms of the GNU Lesser Public License as published by
017 * the Free Software Foundation; either version 3 of the License, or
018 * (at your option) any later version.
019 *
020 * McIDAS-V is distributed in the hope that it will be useful,
021 * but WITHOUT ANY WARRANTY; without even the implied warranty of
022 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023 * GNU Lesser Public License for more details.
024 *
025 * You should have received a copy of the GNU Lesser Public License
026 * along with this program. If not, see http://www.gnu.org/licenses.
027 */
028 package edu.wisc.ssec.mcidasv.servermanager;
029
030 import static javax.swing.GroupLayout.DEFAULT_SIZE;
031 import static javax.swing.GroupLayout.PREFERRED_SIZE;
032 import static javax.swing.GroupLayout.Alignment.BASELINE;
033 import static javax.swing.GroupLayout.Alignment.LEADING;
034 import static javax.swing.GroupLayout.Alignment.TRAILING;
035 import static javax.swing.LayoutStyle.ComponentPlacement.RELATED;
036 import static javax.swing.LayoutStyle.ComponentPlacement.UNRELATED;
037
038 import static edu.wisc.ssec.mcidasv.util.Contract.notNull;
039 import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashSet;
040 import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newMap;
041 import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.set;
042 import static edu.wisc.ssec.mcidasv.util.McVGuiUtils.runOnEDT;
043
044 import java.awt.Color;
045 import java.util.Collection;
046 import java.util.Collections;
047 import java.util.EnumSet;
048 import java.util.LinkedHashSet;
049 import java.util.LinkedHashMap;
050 import java.util.List;
051 import java.util.Map;
052 import java.util.Set;
053 import java.util.StringTokenizer;
054 import java.util.concurrent.Callable;
055 import java.util.concurrent.CompletionService;
056 import java.util.concurrent.ExecutionException;
057 import java.util.concurrent.ExecutorCompletionService;
058 import java.util.concurrent.ExecutorService;
059 import java.util.concurrent.Executors;
060
061 import javax.swing.SwingUtilities;
062
063 import org.slf4j.Logger;
064 import org.slf4j.LoggerFactory;
065
066 import ucar.unidata.util.LogUtil;
067
068 import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EditorAction;
069 import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntrySource;
070 import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType;
071 import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryValidity;
072 import edu.wisc.ssec.mcidasv.util.CollectionHelpers;
073 import edu.wisc.ssec.mcidasv.util.Contract;
074 import edu.wisc.ssec.mcidasv.util.McVTextField;
075
076 /**
077 * Simple dialog that allows the user to define or modify {@link RemoteAddeEntry}s.
078 */
079 @SuppressWarnings("serial")
080 public class RemoteEntryEditor extends javax.swing.JDialog {
081
082 /** Logger object. */
083 private static final Logger logger = LoggerFactory.getLogger(RemoteEntryEditor.class);
084
085 /** Possible entry verification states. */
086 public enum AddeStatus { PREFLIGHT, BAD_SERVER, BAD_ACCOUNTING, NO_METADATA, OK, BAD_GROUP };
087
088 /** Number of threads in the thread pool. */
089 private static final int POOL = 5;
090
091 /** Whether or not to input in the dataset, username, and project fields should be uppercased. */
092 private static final String PREF_FORCE_CAPS = "mcv.servers.forcecaps";
093
094 /** Background {@link java.awt.Color Color} of an {@literal "invalid"} {@link javax.swing.JTextField JTextField}. */
095 private static final Color ERROR_FIELD_COLOR = Color.PINK;
096
097 /** Text {@link java.awt.Color Color} of an {@literal "invalid"} {@link javax.swing.JTextField JTextField}. */
098 private static final Color ERROR_TEXT_COLOR = Color.WHITE;
099
100 /** Background {@link java.awt.Color Color} of a {@literal "valid"} {@link javax.swing.JTextField JTextField}. */
101 private static final Color NORMAL_FIELD_COLOR = Color.WHITE;
102
103 /** Text {@link java.awt.Color Color} of a {@literal "valid"} {@link javax.swing.JTextField JTextField}. */
104 private static final Color NORMAL_TEXT_COLOR = Color.BLACK;
105
106 /**
107 * Contains any {@code JTextField}s that may be in an invalid
108 * (to McIDAS-V) state.
109 */
110 private final Set<javax.swing.JTextField> badFields = newLinkedHashSet();
111
112 /** Reference back to the server manager. */
113 private final EntryStore entryStore;
114
115 // private final TabbedAddeManager manager;
116
117 /** Current contents of the editor. */
118 private final Set<RemoteAddeEntry> currentEntries = newLinkedHashSet();
119
120 /** The last dialog action performed by the user. */
121 private EditorAction editorAction = EditorAction.INVALID;
122
123 /** Initial contents of {@link #serverField}. Be aware that {@code null} is allowed. */
124 private final String serverText;
125
126 /** Initial contents of {@link #datasetField}. Be aware that {@code null} is allowed. */
127 private final String datasetText;
128
129 /** Whether or not the editor is prompting the user to adjust input. */
130 private boolean inErrorState = false;
131
132 // if we decide to restore error overlays for known "bad" values.
133 // private Set<RemoteAddeEntry> invalidEntries = CollectionHelpers.newLinkedHashSet();
134
135 /**
136 * Populates the server and dataset text fields with given {@link String}s.
137 * This only works if the dialog <b>is not yet visible</b>.
138 *
139 * <p>This is mostly useful when adding an entry from a chooser.
140 *
141 * @param address Should be the address of a server, but empty and
142 * {@code null} values are allowed.
143 * @param group Should be the name of a group/dataset on {@code server},
144 * but empty and {@code null} values are allowed.
145 */
146 public RemoteEntryEditor(EntryStore entryStore, String address, String group) {
147 super((javax.swing.JDialog)null, true);
148 this.entryStore = entryStore;
149 // this.manager = null;
150 this.serverText = address;
151 this.datasetText = group;
152 initComponents(RemoteAddeEntry.INVALID_ENTRIES);
153 }
154
155 // TODO(jon): hold back on javadocs, this is likely to change
156 public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store) {
157 this(parent, modal, manager, store, RemoteAddeEntry.INVALID_ENTRIES);
158 }
159
160 public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store, final RemoteAddeEntry entry) {
161 this(parent, modal, manager, store, CollectionHelpers.list(entry));
162 }
163
164 // TODO(jon): hold back on javadocs, this is likely to change
165 public RemoteEntryEditor(java.awt.Frame parent, boolean modal, final TabbedAddeManager manager, final EntryStore store, final List<RemoteAddeEntry> entries) {
166 super(manager, modal);
167 this.entryStore = store;
168 // this.manager = manager;
169 this.serverText = null;
170 this.datasetText = null;
171 if (entries != RemoteAddeEntry.INVALID_ENTRIES) {
172 currentEntries.addAll(entries);
173 }
174 initComponents(entries);
175 }
176
177 /**
178 * Poll the various UI components and attempt to construct valid ADDE
179 * entries based upon the information provided by the user.
180 *
181 * @param ignoreCheckboxes Whether or not the {@literal "type"} checkboxes
182 * should get ignored. Setting this to {@code true} means that <i>all</i>
183 * types are considered valid--which is useful when attempting to verify
184 * the user's input.
185 *
186 * @return {@link Set} of entries that represent the user's input, or an
187 * empty {@code Set} if the input was invalid somehow.
188 */
189 private Set<RemoteAddeEntry> pollWidgets(final boolean ignoreCheckboxes) {
190 String host = serverField.getText().trim();
191 String dataset = datasetField.getText().trim();
192 String username = RemoteAddeEntry.DEFAULT_ACCOUNT.getUsername();
193 String project = RemoteAddeEntry.DEFAULT_ACCOUNT.getProject();
194 if (acctBox.isSelected()) {
195 username = userField.getText().trim();
196 project = projField.getText().trim();
197 }
198
199 // determine the "valid" types
200 Set<EntryType> selectedTypes = newLinkedHashSet();
201 if (!ignoreCheckboxes) {
202 if (imageBox.isSelected()) {
203 selectedTypes.add(EntryType.IMAGE);
204 }
205 if (pointBox.isSelected()) {
206 selectedTypes.add(EntryType.POINT);
207 }
208 if (gridBox.isSelected()) {
209 selectedTypes.add(EntryType.GRID);
210 }
211 if (textBox.isSelected()) {
212 selectedTypes.add(EntryType.TEXT);
213 }
214 if (navBox.isSelected()) {
215 selectedTypes.add(EntryType.NAV);
216 }
217 if (radarBox.isSelected()) {
218 selectedTypes.add(EntryType.RADAR);
219 }
220 } else {
221 selectedTypes.addAll(set(EntryType.IMAGE, EntryType.POINT, EntryType.GRID, EntryType.TEXT, EntryType.NAV, EntryType.RADAR));
222 }
223
224 if (selectedTypes.isEmpty()) {
225 selectedTypes.add(EntryType.UNKNOWN);
226 }
227
228 // deal with the user trying to add multiple groups at once (even though this UI doesn't work right with it)
229 StringTokenizer tok = new StringTokenizer(dataset, ",");
230 Set<String> newDatasets = newLinkedHashSet();
231 while (tok.hasMoreTokens()) {
232 newDatasets.add(tok.nextToken().trim());
233 }
234
235 // create a new entry for each group and its valid types.
236 Set<RemoteAddeEntry> entries = newLinkedHashSet();
237 for (String newGroup : newDatasets) {
238 for (EntryType type : selectedTypes) {
239 RemoteAddeEntry.Builder builder = new RemoteAddeEntry.Builder(host, newGroup).type(type).validity(EntryValidity.VERIFIED).source(EntrySource.USER);
240 if (acctBox.isSelected()) {
241 builder = builder.account(username, project);
242 }
243 RemoteAddeEntry newEntry = builder.build();
244 List<AddeEntry> matches = entryStore.searchWithPrefix(newEntry.asStringId());
245 if (matches.isEmpty()) {
246 entries.add(newEntry);
247 } else if (matches.size() == 1) {
248 AddeEntry matchedEntry = matches.get(0);
249 if (matchedEntry.getEntrySource() != EntrySource.SYSTEM) {
250 entries.add(newEntry);
251 } else {
252 entries.add((RemoteAddeEntry)matchedEntry);
253 }
254 } else {
255 // results should only be empty or a single entry
256 logger.warn("server manager returned unexpected results={}", matches);
257 }
258 }
259 }
260 return entries;
261 }
262
263 private void disposeDisplayable(final boolean refreshManager) {
264 if (isDisplayable()) {
265 dispose();
266 }
267 TabbedAddeManager tmpController = TabbedAddeManager.getTabbedManager();
268 if (refreshManager && tmpController != null) {
269 tmpController.refreshDisplay();
270 }
271 }
272
273 /**
274 * Creates new {@link RemoteAddeEntry}s based upon the contents of the dialog
275 * and adds {@literal "them"} to the managed servers. If the dialog is
276 * displayed, we call {@link #dispose()} and attempt to refresh the
277 * server manager GUI if it is available.
278 */
279 private void addEntry() {
280 Set<RemoteAddeEntry> addedEntries = pollWidgets(false);
281 entryStore.addEntries(addedEntries);
282 // if (manager != null) {
283 // manager.addEntries(addedEntries);
284 // }
285 disposeDisplayable(true);
286 }
287
288 /**
289 * Replaces the entries within {@link #currentEntries} with new entries
290 * from {@link #pollWidgets(boolean)}. If the dialog is displayed, we call
291 * {@link #dispose()} and attempt to refresh the server manager GUI if it's
292 * available.
293 */
294 private void editEntry() {
295 Set<RemoteAddeEntry> newEntries = pollWidgets(false);
296 entryStore.replaceEntries(currentEntries, newEntries);
297 // if (manager != null) {
298 // manager.replaceEntries(currentEntries, newEntries);
299 // }
300 logger.trace("currentEntries={}", currentEntries);
301 disposeDisplayable(true);
302 }
303
304 /**
305 * Attempts to verify that the current contents of the GUI are
306 * {@literal "valid"}.
307 */
308 private void verifyInput() {
309 resetBadFields();
310 Set<RemoteAddeEntry> unverifiedEntries = pollWidgets(true);
311
312 // the editor GUI only works with one server address at a time. so
313 // although there may be several RemoteAddeEntry objs, they'll all have
314 // the same address and the follow *isn't* as dumb as it looks!
315 if (!unverifiedEntries.isEmpty()) {
316 if (!RemoteAddeEntry.checkHost(unverifiedEntries.toArray(new RemoteAddeEntry[0])[0])) {
317 setStatus("Could not connect to the given server.");
318 setBadField(serverField, true);
319 return;
320 }
321 } else {
322 setStatus("Please specify ");
323 setBadField(serverField, true);
324 return;
325 }
326
327 setStatus("Contacting server...");
328 Set<RemoteAddeEntry> verifiedEntries = checkGroups(unverifiedEntries);
329 EnumSet<EntryType> presentTypes = EnumSet.noneOf(EntryType.class);
330 if (!verifiedEntries.isEmpty()) {
331 for (RemoteAddeEntry verifiedEntry : verifiedEntries) {
332 presentTypes.add(verifiedEntry.getEntryType());
333 }
334 imageBox.setSelected(presentTypes.contains(EntryType.IMAGE));
335 pointBox.setSelected(presentTypes.contains(EntryType.POINT));
336 gridBox.setSelected(presentTypes.contains(EntryType.GRID));
337 textBox.setSelected(presentTypes.contains(EntryType.TEXT));
338 navBox.setSelected(presentTypes.contains(EntryType.NAV));
339 radarBox.setSelected(presentTypes.contains(EntryType.RADAR));
340 }
341 }
342
343 /**
344 * Displays a short status message in {@link #statusLabel}.
345 *
346 * @param msg Status message. Shouldn't be {@code null}.
347 */
348 private void setStatus(final String msg) {
349 assert msg != null;
350 logger.debug("msg={}", msg);
351 runOnEDT(new Runnable() {
352 public void run() {
353 statusLabel.setText(msg);
354 }
355 });
356 statusLabel.revalidate();
357 }
358
359 /**
360 * Marks a {@code JTextField} as {@literal "valid"} or {@literal "invalid"}.
361 * Mostly this just means that the field is highlighted in order to provide
362 * to the user a sense of {@literal "what do I fix"} when something goes
363 * wrong.
364 *
365 * @param field {@code JTextField} to mark.
366 * @param isBad {@code true} means that the field is {@literal "invalid"},
367 * {@code false} means that the field is {@literal "valid"}.
368 */
369 private void setBadField(final javax.swing.JTextField field, final boolean isBad) {
370 assert field != null;
371 assert field == serverField || field == datasetField || field == userField || field == projField;
372
373 if (isBad) {
374 badFields.add(field);
375 } else {
376 badFields.remove(field);
377 }
378
379 runOnEDT(new Runnable() {
380 public void run() {
381 if (isBad) {
382 field.setForeground(ERROR_TEXT_COLOR);
383 field.setBackground(ERROR_FIELD_COLOR);
384 } else {
385 field.setForeground(NORMAL_TEXT_COLOR);
386 field.setBackground(NORMAL_FIELD_COLOR);
387 }
388 }
389 });
390 field.revalidate();
391 }
392
393 /**
394 * Determines whether or not any fields are in an invalid state. Useful
395 * for disallowing the user to add invalid entries to the server manager.
396 *
397 * @return Whether or not any fields are invalid.
398 */
399 private boolean anyBadFields() {
400 assert badFields != null;
401 return !badFields.isEmpty();
402 }
403
404 /**
405 * Clear out {@link #badFields} and {@literal "set"} the field's status to
406 * valid.
407 */
408 private void resetBadFields() {
409 Set<javax.swing.JTextField> fields = new LinkedHashSet<javax.swing.JTextField>(badFields);
410 for (javax.swing.JTextField field : fields) {
411 setBadField(field, false);
412 }
413 }
414
415 /**
416 * @see #editorAction
417 */
418 public EditorAction getEditorAction() {
419 return editorAction;
420 }
421
422 /**
423 * @see #editorAction
424 */
425 private void setEditorAction(final EditorAction editorAction) {
426 this.editorAction = editorAction;
427 }
428
429 /**
430 * Controls the value associated with the {@link #PREF_FORCE_CAPS} preference.
431 *
432 * @param value {@code true} causes user input into the dataset, username,
433 * and project fields to be capitalized.
434 *
435 * @see #getForceMcxCaps()
436 */
437 private void setForceMcxCaps(final boolean value) {
438 entryStore.getIdvStore().put(PREF_FORCE_CAPS, value);
439 }
440
441 /**
442 * Returns the value associated with the {@link #PREF_FORCE_CAPS} preference.
443 *
444 * @see #setForceMcxCaps(boolean)
445 */
446 private boolean getForceMcxCaps() {
447 return entryStore.getIdvStore().get(PREF_FORCE_CAPS, true);
448 }
449
450 // TODO(jon): oh man clean this junk up
451 /** This method is called from within the constructor to
452 * initialize the form.
453 * WARNING: Do NOT modify this code. The content of this method is
454 * always regenerated by the Form Editor.
455 */
456 @SuppressWarnings("unchecked")
457 // <editor-fold defaultstate="collapsed" desc="Generated Code">
458 private void initComponents(final List<RemoteAddeEntry> initEntries) {
459 assert SwingUtilities.isEventDispatchThread();
460 entryPanel = new javax.swing.JPanel();
461 serverLabel = new javax.swing.JLabel();
462 serverField = new javax.swing.JTextField();
463 datasetLabel = new javax.swing.JLabel();
464 datasetField = new McVTextField();
465 acctBox = new javax.swing.JCheckBox();
466 userLabel = new javax.swing.JLabel();
467 userField = new McVTextField();
468 projLabel = new javax.swing.JLabel();
469 projField = new javax.swing.JTextField();
470 capBox = new javax.swing.JCheckBox();
471 typePanel = new javax.swing.JPanel();
472 imageBox = new javax.swing.JCheckBox();
473 pointBox = new javax.swing.JCheckBox();
474 gridBox = new javax.swing.JCheckBox();
475 textBox = new javax.swing.JCheckBox();
476 navBox = new javax.swing.JCheckBox();
477 radarBox = new javax.swing.JCheckBox();
478 statusPanel = new javax.swing.JPanel();
479 statusLabel = new javax.swing.JLabel();
480 verifyAddButton = new javax.swing.JButton();
481 verifyServer = new javax.swing.JButton();
482 addServer = new javax.swing.JButton();
483 cancelButton = new javax.swing.JButton();
484
485 boolean forceCaps = getForceMcxCaps();
486 datasetField.setUppercase(forceCaps);
487 userField.setUppercase(forceCaps);
488
489 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
490 setTitle("Add Remote Dataset");
491 } else {
492 setTitle("Edit Remote Dataset");
493 }
494 setResizable(false);
495 setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);
496 addWindowListener(new java.awt.event.WindowAdapter() {
497 public void windowClosed(java.awt.event.WindowEvent evt) {
498 formWindowClosed(evt);
499 }
500 });
501
502 serverLabel.setText("Server:");
503 if (serverText != null) {
504 serverField.setText(serverText);
505 }
506
507 datasetLabel.setText("Dataset:");
508 if (datasetText != null) {
509 datasetField.setText(datasetText);
510 }
511
512 acctBox.setText("Specify accounting information:");
513 acctBox.addActionListener(new java.awt.event.ActionListener() {
514 public void actionPerformed(java.awt.event.ActionEvent evt) {
515 acctBoxActionPerformed(evt);
516 }
517 });
518
519 userLabel.setText("Username:");
520 userField.setEnabled(acctBox.isSelected());
521
522 projLabel.setText("Project #:");
523 projField.setEnabled(acctBox.isSelected());
524
525 capBox.setText("Automatically capitalize dataset and username?");
526 capBox.setSelected(forceCaps);
527 capBox.addActionListener(new java.awt.event.ActionListener() {
528 public void actionPerformed(java.awt.event.ActionEvent evt) {
529 capBoxActionPerformed(evt);
530 }
531 });
532
533 javax.swing.event.DocumentListener inputListener = new javax.swing.event.DocumentListener() {
534 public void changedUpdate(javax.swing.event.DocumentEvent evt) {
535 reactToValueChanges();
536 }
537 public void insertUpdate(javax.swing.event.DocumentEvent evt) {
538 if (inErrorState) {
539 verifyAddButton.setEnabled(true);
540 verifyServer.setEnabled(true);
541 inErrorState = false;
542 resetBadFields();
543 }
544 }
545 public void removeUpdate(javax.swing.event.DocumentEvent evt) {
546 if (inErrorState) {
547 verifyAddButton.setEnabled(true);
548 verifyServer.setEnabled(true);
549 inErrorState = false;
550 resetBadFields();
551 }
552 }
553 };
554
555 serverField.getDocument().addDocumentListener(inputListener);
556 datasetField.getDocument().addDocumentListener(inputListener);
557 userField.getDocument().addDocumentListener(inputListener);
558 projField.getDocument().addDocumentListener(inputListener);
559
560 javax.swing.GroupLayout entryPanelLayout = new javax.swing.GroupLayout(entryPanel);
561 entryPanel.setLayout(entryPanelLayout);
562 entryPanelLayout.setHorizontalGroup(
563 entryPanelLayout.createParallelGroup(LEADING)
564 .addGroup(entryPanelLayout.createSequentialGroup()
565 .addGroup(entryPanelLayout.createParallelGroup(LEADING)
566 .addComponent(serverLabel, TRAILING)
567 .addComponent(datasetLabel, TRAILING)
568 .addComponent(userLabel, TRAILING)
569 .addComponent(projLabel, TRAILING))
570 .addPreferredGap(RELATED)
571 .addGroup(entryPanelLayout.createParallelGroup(LEADING)
572 .addComponent(serverField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
573 .addComponent(capBox)
574 .addComponent(acctBox)
575 .addComponent(datasetField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
576 .addComponent(userField, DEFAULT_SIZE, 419, Short.MAX_VALUE)
577 .addComponent(projField, DEFAULT_SIZE, 419, Short.MAX_VALUE))
578 .addContainerGap())
579 );
580 entryPanelLayout.setVerticalGroup(
581 entryPanelLayout.createParallelGroup(LEADING)
582 .addGroup(entryPanelLayout.createSequentialGroup()
583 .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
584 .addComponent(serverLabel)
585 .addComponent(serverField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
586 .addPreferredGap(RELATED)
587 .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
588 .addComponent(datasetLabel)
589 .addComponent(datasetField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
590 .addGap(16, 16, 16)
591 .addComponent(acctBox)
592 .addPreferredGap(RELATED)
593 .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
594 .addComponent(userLabel)
595 .addComponent(userField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
596 .addPreferredGap(RELATED)
597 .addGroup(entryPanelLayout.createParallelGroup(BASELINE)
598 .addComponent(projLabel)
599 .addComponent(projField, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE))
600 .addPreferredGap(RELATED)
601 .addComponent(capBox)
602 .addGap(0, 0, Short.MAX_VALUE))
603 );
604
605 typePanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Dataset Types"));
606
607 java.awt.event.ActionListener typeInputListener = new java.awt.event.ActionListener() {
608 public void actionPerformed(java.awt.event.ActionEvent evt) {
609 if (inErrorState) {
610 verifyAddButton.setEnabled(true);
611 verifyServer.setEnabled(true);
612 inErrorState = false;
613 resetBadFields();
614 }
615 }
616 };
617
618 imageBox.setText("Image");
619 imageBox.addActionListener(typeInputListener);
620 typePanel.add(imageBox);
621
622 pointBox.setText("Point");
623 pointBox.addActionListener(typeInputListener);
624 typePanel.add(pointBox);
625
626 gridBox.setText("Grid");
627 gridBox.addActionListener(typeInputListener);
628 typePanel.add(gridBox);
629
630 textBox.setText("Text");
631 textBox.addActionListener(typeInputListener);
632 typePanel.add(textBox);
633
634 navBox.setText("Navigation");
635 navBox.addActionListener(typeInputListener);
636 typePanel.add(navBox);
637
638 radarBox.setText("Radar");
639 radarBox.addActionListener(typeInputListener);
640 typePanel.add(radarBox);
641
642 statusPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Status"));
643
644 statusLabel.setText("Please provide the address of a remote ADDE server.");
645
646 javax.swing.GroupLayout statusPanelLayout = new javax.swing.GroupLayout(statusPanel);
647 statusPanel.setLayout(statusPanelLayout);
648 statusPanelLayout.setHorizontalGroup(
649 statusPanelLayout.createParallelGroup(LEADING)
650 .addGroup(statusPanelLayout.createSequentialGroup()
651 .addContainerGap()
652 .addComponent(statusLabel)
653 .addContainerGap(154, Short.MAX_VALUE))
654 );
655 statusPanelLayout.setVerticalGroup(
656 statusPanelLayout.createParallelGroup(LEADING)
657 .addGroup(statusPanelLayout.createSequentialGroup()
658 .addComponent(statusLabel)
659 .addContainerGap(DEFAULT_SIZE, Short.MAX_VALUE))
660 );
661
662 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
663 verifyAddButton.setText("Verify and Add Server");
664 } else {
665 verifyAddButton.setText("Verify and Save Changes");
666 }
667 verifyAddButton.addActionListener(new java.awt.event.ActionListener() {
668 public void actionPerformed(java.awt.event.ActionEvent evt) {
669 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES)
670 verifyAddButtonActionPerformed(evt);
671 else
672 verifyEditButtonActionPerformed(evt);
673 }
674 });
675
676 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
677 verifyServer.setText("Verify Server");
678 } else {
679 verifyServer.setText("Verify Changes");
680 }
681 verifyServer.addActionListener(new java.awt.event.ActionListener() {
682 public void actionPerformed(java.awt.event.ActionEvent evt) {
683 verifyServerActionPerformed(evt);
684 }
685 });
686
687 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
688 addServer.setText("Add Server");
689 } else {
690 addServer.setText("Save Changes");
691 }
692 addServer.addActionListener(new java.awt.event.ActionListener() {
693 public void actionPerformed(java.awt.event.ActionEvent evt) {
694 if (initEntries == RemoteAddeEntry.INVALID_ENTRIES) {
695 addServerActionPerformed(evt);
696 } else {
697 editServerActionPerformed(evt);
698 }
699 }
700 });
701
702 cancelButton.setText("Cancel");
703 cancelButton.addActionListener(new java.awt.event.ActionListener() {
704 public void actionPerformed(java.awt.event.ActionEvent evt) {
705 cancelButtonActionPerformed(evt);
706 }
707 });
708
709 javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
710 getContentPane().setLayout(layout);
711 layout.setHorizontalGroup(
712 layout.createParallelGroup(LEADING)
713 .addGroup(layout.createSequentialGroup()
714 .addContainerGap()
715 .addGroup(layout.createParallelGroup(LEADING)
716 .addComponent(statusPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
717 .addComponent(typePanel, 0, 0, Short.MAX_VALUE)
718 .addComponent(entryPanel, DEFAULT_SIZE, DEFAULT_SIZE, Short.MAX_VALUE)
719 .addGroup(layout.createSequentialGroup()
720 .addComponent(verifyAddButton)
721 .addPreferredGap(RELATED)
722 .addComponent(verifyServer)
723 .addPreferredGap(RELATED)
724 .addComponent(addServer)
725 .addPreferredGap(RELATED)
726 .addComponent(cancelButton)))
727 .addContainerGap())
728 );
729 layout.setVerticalGroup(
730 layout.createParallelGroup(LEADING)
731 .addGroup(layout.createSequentialGroup()
732 .addContainerGap()
733 .addComponent(entryPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
734 .addPreferredGap(UNRELATED)
735 .addComponent(typePanel, PREFERRED_SIZE, 57, PREFERRED_SIZE)
736 .addGap(18, 18, 18)
737 .addComponent(statusPanel, PREFERRED_SIZE, DEFAULT_SIZE, PREFERRED_SIZE)
738 .addGap(18, 18, 18)
739 .addGroup(layout.createParallelGroup(BASELINE)
740 .addComponent(verifyServer)
741 .addComponent(addServer)
742 .addComponent(cancelButton)
743 .addComponent(verifyAddButton))
744 .addContainerGap(17, Short.MAX_VALUE))
745 );
746
747 if (initEntries != null && !RemoteAddeEntry.INVALID_ENTRIES.equals(initEntries)) {
748 RemoteAddeEntry initEntry = initEntries.get(0);
749 boolean hasSystemEntry = false;
750 for (RemoteAddeEntry entry : initEntries) {
751 if (entry.getEntrySource() == EntrySource.SYSTEM) {
752 initEntry = entry;
753 hasSystemEntry = true;
754 break;
755 }
756 }
757 serverField.setText(initEntry.getAddress());
758 datasetField.setText(initEntry.getGroup());
759
760 if (!RemoteAddeEntry.DEFAULT_ACCOUNT.equals(initEntry.getAccount())) {
761 acctBox.setSelected(true);
762 userField.setEnabled(true);
763 userField.setText(initEntry.getAccount().getUsername());
764 projField.setEnabled(true);
765 projField.setText(initEntry.getAccount().getProject());
766 }
767
768 if (hasSystemEntry) {
769 serverField.setEnabled(false);
770 datasetField.setEnabled(false);
771 acctBox.setEnabled(false);
772 userField.setEnabled(false);
773 projField.setEnabled(false);
774 capBox.setEnabled(false);
775 }
776
777 for (RemoteAddeEntry entry : initEntries) {
778 boolean nonDefaultSource = entry.getEntrySource() != EntrySource.SYSTEM;
779 if (entry.getEntryType() == EntryType.IMAGE) {
780 imageBox.setSelected(true);
781 imageBox.setEnabled(nonDefaultSource);
782 } else if (entry.getEntryType() == EntryType.POINT) {
783 pointBox.setSelected(true);
784 pointBox.setEnabled(nonDefaultSource);
785 } else if (entry.getEntryType() == EntryType.GRID) {
786 gridBox.setSelected(true);
787 gridBox.setEnabled(nonDefaultSource);
788 } else if (entry.getEntryType() == EntryType.TEXT) {
789 textBox.setSelected(true);
790 textBox.setEnabled(nonDefaultSource);
791 } else if (entry.getEntryType() == EntryType.NAV) {
792 navBox.setSelected(true);
793 navBox.setEnabled(nonDefaultSource);
794 } else if (entry.getEntryType() == EntryType.RADAR) {
795 radarBox.setSelected(true);
796 radarBox.setEnabled(nonDefaultSource);
797 }
798 }
799 }
800 pack();
801 }// </editor-fold>
802
803 private void acctBoxActionPerformed(java.awt.event.ActionEvent evt) {
804 assert SwingUtilities.isEventDispatchThread();
805 resetBadFields();
806 boolean enabled = acctBox.isSelected();
807 userField.setEnabled(enabled);
808 projField.setEnabled(enabled);
809 verifyAddButton.setEnabled(true);
810 verifyServer.setEnabled(true);
811 }
812
813 private void capBoxActionPerformed(java.awt.event.ActionEvent evt) {
814 assert SwingUtilities.isEventDispatchThread();
815 boolean forceCaps = capBox.isSelected();
816 datasetField.setUppercase(forceCaps);
817 userField.setUppercase(forceCaps);
818 setForceMcxCaps(forceCaps);
819 if (!forceCaps) {
820 return;
821 }
822 datasetField.setText(datasetField.getText().toUpperCase());
823 userField.setText(userField.getText().toUpperCase());
824 }
825
826 private void verifyAddButtonActionPerformed(java.awt.event.ActionEvent evt) {
827 verifyInput();
828 if (!anyBadFields()) {
829 setEditorAction(EditorAction.ADDED_VERIFIED);
830 addEntry();
831 } else {
832 inErrorState = true;
833 verifyAddButton.setEnabled(false);
834 verifyServer.setEnabled(false);
835 }
836 }
837
838 private void verifyEditButtonActionPerformed(java.awt.event.ActionEvent evt) {
839 verifyInput();
840 if (!anyBadFields()) {
841 setEditorAction(EditorAction.EDITED_VERIFIED);
842 editEntry();
843 } else {
844 inErrorState = true;
845 verifyAddButton.setEnabled(false);
846 verifyServer.setEnabled(false);
847 }
848 }
849
850 private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {
851 setEditorAction(EditorAction.CANCELLED);
852 disposeDisplayable(false);
853 }
854
855 private void formWindowClosed(java.awt.event.WindowEvent evt) {
856 setEditorAction(EditorAction.CANCELLED);
857 disposeDisplayable(false);
858 }
859
860 private void verifyServerActionPerformed(java.awt.event.ActionEvent evt) {
861 verifyInput();
862 if (anyBadFields()) {
863 // save poll widget state
864 // toggle a "listen for *any* input event" switch to on
865 // invalidEntries.clear();
866 // invalidEntries.addAll(pollWidgets(false));
867 inErrorState = true;
868 verifyAddButton.setEnabled(false);
869 verifyServer.setEnabled(false);
870 }
871 }
872
873 private void addServerActionPerformed(java.awt.event.ActionEvent evt) {
874 setEditorAction(EditorAction.ADDED);
875 addEntry();
876 }
877
878 private void editServerActionPerformed(java.awt.event.ActionEvent evt) {
879 setEditorAction(EditorAction.EDITED);
880 editEntry();
881 }
882
883 private void reactToValueChanges() {
884 assert SwingUtilities.isEventDispatchThread();
885 if (inErrorState) {
886 verifyAddButton.setEnabled(true);
887 verifyServer.setEnabled(true);
888 inErrorState = false;
889 resetBadFields();
890 }
891 }
892
893 /**
894 * Attempt to verify a {@link Set} of {@link RemoteAddeEntry}s. Useful for
895 * checking a {@literal "MCTABLE.TXT"} after importing.
896 *
897 * @param entries {@code Set} of remote ADDE entries to validate. Cannot
898 * be {@code null}.
899 *
900 * @return {@code Set} of {@code RemoteAddeEntry}s that McIDAS-V was able
901 * to connect to.
902 *
903 * @throws NullPointerException if {@code entries} is {@code null}.
904 */
905 public Set<RemoteAddeEntry> checkHosts(final Set<RemoteAddeEntry> entries) {
906 Contract.notNull(entries, "entries cannot be null");
907 Set<RemoteAddeEntry> goodEntries = newLinkedHashSet();
908 Set<String> checkedHosts = newLinkedHashSet();
909 Map<String, Boolean> hostStatus = newMap();
910 for (RemoteAddeEntry entry : entries) {
911 String host = entry.getAddress();
912 if (hostStatus.get(host) == Boolean.FALSE) {
913 continue;
914 } else if (hostStatus.get(host) == Boolean.TRUE) {
915 goodEntries.add(entry);
916 } else {
917 checkedHosts.add(host);
918 if (RemoteAddeEntry.checkHost(entry)) {
919 goodEntries.add(entry);
920 hostStatus.put(host, Boolean.TRUE);
921 } else {
922 hostStatus.put(host, Boolean.FALSE);
923 }
924 }
925 }
926 return goodEntries;
927 }
928
929 public Set<RemoteAddeEntry> checkHosts2(final Set<RemoteAddeEntry> entries) {
930 Contract.notNull(entries, "entries cannot be null");
931 if (entries.isEmpty()) {
932 return Collections.emptySet();
933 }
934
935 Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
936 Set<String> hosts = newLinkedHashSet(entries.size());
937 // Set<String> validHosts = newLinkedHashSet(entries.size());
938
939 ExecutorService exec = Executors.newFixedThreadPool(POOL);
940 CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
941 for (RemoteAddeEntry entry : entries) {
942 ecs.submit(new VerifyHostTask(new StatusWrapper(entry)));
943 }
944
945 try {
946 for (int i = 0; i < entries.size(); i++) {
947 StatusWrapper pairing = ecs.take().get();
948 RemoteAddeEntry entry = pairing.getEntry();
949 AddeStatus status = pairing.getStatus();
950 // setStatus(entry.getAddress()+": attempting to connect...");
951 // statuses.add(status);
952 // entry2Status.put(entry, status);
953 if (status == AddeStatus.OK) {
954 verified.add(entry);
955 // setStatus("Found host name "+entry.getAddress());
956 }
957 }
958 } catch (InterruptedException e) {
959 LogUtil.logException("interrupted while checking ADDE entries", e);
960 } catch (ExecutionException e) {
961 LogUtil.logException("ADDE validation execution error", e);
962 } finally {
963 exec.shutdown();
964 }
965 return verified;
966 }
967 // public Set<RemoteAddeEntry> checkHosts2(final Set<RemoteAddeEntry> entries) {
968 // Contract.notNull(entries, "entries cannot be null");
969 // if (entries.isEmpty()) {
970 // return Collections.emptySet();
971 // }
972 //
973 // Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
974 // ExecutorService exec = Executors.newFixedThreadPool(POOL);
975 // CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
976 //// Map<RemoteAddeEntry, AddeStatus> entry2Status = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
977 //
978 // for (RemoteAddeEntry entry : entries) {
979 // StatusWrapper check = new StatusWrapper(entry);
980 // ecs.submit(new VerifyHostTask(check));
981 // }
982 //
983 // try {
984 // for (int i = 0; i < entries.size(); i++) {
985 // StatusWrapper pairing = ecs.take().get();
986 // RemoteAddeEntry entry = pairing.getEntry();
987 // AddeStatus status = pairing.getStatus();
988 // setStatus(entry.getAddress()+": attempting to connect...");
989 // statuses.add(status);
990 //// entry2Status.put(entry, status);
991 // if (status == AddeStatus.OK) {
992 // verified.add(entry);
993 //// setStatus("Found host name "+entry.getAddress());
994 // }
995 // }
996 // } catch (InterruptedException e) {
997 //
998 // } catch (ExecutionException e) {
999 //
1000 // } finally {
1001 // exec.shutdown();
1002 // }
1003 //
1004 // if (statuses.contains(AddeStatus.BAD_SERVER)) {
1005 // setStatus("Could not connect to the server.");
1006 // setBadField(serverField, true);
1007 // } else {
1008 // setStatus("Connected to server.");
1009 // }
1010 ////
1011 //// if (!statuses.contains(AddeStatus.OK)) {
1012 //// if (statuses.contains(AddeStatus.BAD_ACCOUNTING)) {
1013 //// setStatus("Incorrect accounting information.");
1014 //// setBadField(userField, true);
1015 //// setBadField(projField, true);
1016 //// } else if (statuses.contains(AddeStatus.BAD_GROUP)) {
1017 //// setStatus("Dataset does not appear to be valid.");
1018 //// setBadField(datasetField, true);
1019 //// } else if (statuses.contains(AddeStatus.BAD_SERVER)) {
1020 //// setStatus("Could not connect to the ADDE server.");
1021 //// setBadField(serverField, true);
1022 //// } else {
1023 //// logger.warn("guru meditation error: statuses={}", statuses);
1024 //// }
1025 //// } else {
1026 //// setStatus("Finished verifying.");
1027 //// }
1028 //
1029 // return verified;
1030 // }
1031
1032 public Set<RemoteAddeEntry> checkGroups(final Set<RemoteAddeEntry> entries) {
1033 Contract.notNull(entries, "entries cannot be null");
1034 if (entries.isEmpty()) {
1035 return Collections.emptySet();
1036 }
1037
1038 Set<RemoteAddeEntry> verified = newLinkedHashSet(entries.size());
1039 Collection<AddeStatus> statuses = EnumSet.noneOf(AddeStatus.class);
1040 ExecutorService exec = Executors.newFixedThreadPool(POOL);
1041 CompletionService<StatusWrapper> ecs = new ExecutorCompletionService<StatusWrapper>(exec);
1042 Map<RemoteAddeEntry, AddeStatus> entry2Status = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
1043
1044 // submit new verification tasks to the pool's queue ... (apologies for the pun?)
1045 for (RemoteAddeEntry entry : entries) {
1046 StatusWrapper pairing = new StatusWrapper(entry);
1047 ecs.submit(new VerifyEntryTask(pairing));
1048 }
1049
1050 // use completion service magic to only deal with finished verification tasks
1051 try {
1052 for (int i = 0; i < entries.size(); i++) {
1053 StatusWrapper pairing = ecs.take().get();
1054 RemoteAddeEntry entry = pairing.getEntry();
1055 AddeStatus status = pairing.getStatus();
1056 setStatus(entry.getEntryText()+": attempting verification...");
1057 statuses.add(status);
1058 entry2Status.put(entry, status);
1059 if (status == AddeStatus.OK) {
1060 verified.add(entry);
1061 setStatus("Found accessible "+entry.getEntryType().toString().toLowerCase()+" data.");
1062 }
1063 }
1064 } catch (InterruptedException e) {
1065 LogUtil.logException("interrupted while checking ADDE entries", e);
1066 } catch (ExecutionException e) {
1067 LogUtil.logException("ADDE validation execution error", e);
1068 } finally {
1069 exec.shutdown();
1070 }
1071
1072 if (!statuses.contains(AddeStatus.OK)) {
1073 if (statuses.contains(AddeStatus.BAD_ACCOUNTING)) {
1074 setStatus("Incorrect accounting information.");
1075 setBadField(userField, true);
1076 setBadField(projField, true);
1077 } else if (statuses.contains(AddeStatus.BAD_GROUP)) {
1078 setStatus("Dataset does not appear to be valid.");
1079 setBadField(datasetField, true);
1080 } else if (statuses.contains(AddeStatus.BAD_SERVER)) {
1081 setStatus("Could not connect to the ADDE server.");
1082 setBadField(serverField, true);
1083 } else {
1084 logger.warn("guru meditation error: statuses={}", statuses);
1085 }
1086 } else {
1087 setStatus("Finished verifying.");
1088 }
1089 return verified;
1090 }
1091
1092 private static Map<RemoteAddeEntry, AddeStatus> bulkPut(final Collection<RemoteAddeEntry> entries, final AddeStatus status) {
1093 Map<RemoteAddeEntry, AddeStatus> map = new LinkedHashMap<RemoteAddeEntry, AddeStatus>(entries.size());
1094 for (RemoteAddeEntry entry : entries) {
1095 map.put(entry, status);
1096 }
1097 return map;
1098 }
1099
1100 /**
1101 * Associates a {@link RemoteAddeEntry} with one of the states from
1102 * {@link AddeStatus}.
1103 */
1104 private static class StatusWrapper {
1105 /** */
1106 private final RemoteAddeEntry entry;
1107
1108 /** Current {@literal "status"} of {@link #entry}. */
1109 private AddeStatus status;
1110
1111 /**
1112 * Builds an entry/status pairing.
1113 *
1114 * @param entry The {@code RemoteAddeEntry} to wrap up.
1115 *
1116 * @throws NullPointerException if {@code entry} is {@code null}.
1117 */
1118 public StatusWrapper(final RemoteAddeEntry entry) {
1119 notNull(entry, "cannot create a entry/status pair with a null descriptor");
1120 this.entry = entry;
1121 }
1122
1123 /**
1124 * Set the {@literal "status"} of this {@link #entry} to a given
1125 * {@link AddeStatus}.
1126 *
1127 * @param status New status of {@code entry}.
1128 */
1129 public void setStatus(AddeStatus status) {
1130 this.status = status;
1131 }
1132
1133 /**
1134 * Returns the current {@literal "status"} of {@link #entry}.
1135 *
1136 * @return One of {@link AddeStatus}.
1137 */
1138 public AddeStatus getStatus() {
1139 return status;
1140 }
1141
1142 /**
1143 * Returns the {@link RemoteAddeEntry} stored in this wrapper.
1144 *
1145 * @return {@link #entry}
1146 */
1147 public RemoteAddeEntry getEntry() {
1148 return entry;
1149 }
1150 }
1151
1152 /**
1153 * Represents an ADDE entry verification task. These are executed asynchronously
1154 * by the completion service within {@link RemoteEntryEditor#checkGroups(Set)}.
1155 */
1156 private class VerifyEntryTask implements Callable<StatusWrapper> {
1157 private final StatusWrapper entryStatus;
1158 public VerifyEntryTask(final StatusWrapper descStatus) {
1159 notNull(descStatus, "cannot verify or set status of a null descriptor/status pair");
1160 this.entryStatus = descStatus;
1161 }
1162
1163 public StatusWrapper call() throws Exception {
1164 entryStatus.setStatus(RemoteAddeEntry.checkEntry(entryStatus.getEntry()));
1165 return entryStatus;
1166 }
1167 }
1168
1169 private class VerifyHostTask implements Callable<StatusWrapper> {
1170 private final StatusWrapper entryStatus;
1171 public VerifyHostTask(final StatusWrapper descStatus) {
1172 entryStatus = notNull(descStatus, "cannot verify or set status of a null descriptor/status pair");
1173 }
1174 public StatusWrapper call() throws Exception {
1175 boolean validHost = RemoteAddeEntry.checkHost(entryStatus.getEntry());
1176 if (validHost) {
1177 entryStatus.setStatus(AddeStatus.OK);
1178 } else {
1179 entryStatus.setStatus(AddeStatus.BAD_SERVER);
1180 }
1181 return entryStatus;
1182 }
1183 }
1184
1185 // Variables declaration - do not modify
1186 private javax.swing.JCheckBox acctBox;
1187 private javax.swing.JButton addServer;
1188 private javax.swing.JButton cancelButton;
1189 private javax.swing.JCheckBox capBox;
1190 private McVTextField datasetField;
1191 private javax.swing.JLabel datasetLabel;
1192 private javax.swing.JPanel entryPanel;
1193 private javax.swing.JCheckBox gridBox;
1194 private javax.swing.JCheckBox imageBox;
1195 private javax.swing.JCheckBox navBox;
1196 private javax.swing.JCheckBox pointBox;
1197 private javax.swing.JTextField projField;
1198 private javax.swing.JLabel projLabel;
1199 private javax.swing.JCheckBox radarBox;
1200 private javax.swing.JTextField serverField;
1201 private javax.swing.JLabel serverLabel;
1202 private javax.swing.JLabel statusLabel;
1203 private javax.swing.JPanel statusPanel;
1204 private javax.swing.JCheckBox textBox;
1205 private javax.swing.JPanel typePanel;
1206 private McVTextField userField;
1207 private javax.swing.JLabel userLabel;
1208 private javax.swing.JButton verifyAddButton;
1209 private javax.swing.JButton verifyServer;
1210 // End of variables declaration
1211 }