001/* 002 * $Id: EntryStore.java,v 1.59 2011/04/06 20:05:33 jbeavers Exp $ 003 * 004 * This file is part of McIDAS-V 005 * 006 * Copyright 2007-2011 007 * Space Science and Engineering Center (SSEC) 008 * University of Wisconsin - Madison 009 * 1225 W. Dayton Street, Madison, WI 53706, USA 010 * https://www.ssec.wisc.edu/mcidas 011 * 012 * All Rights Reserved 013 * 014 * McIDAS-V is built on Unidata's IDV and SSEC's VisAD libraries, and 015 * some McIDAS-V source code is based on IDV and VisAD source code. 016 * 017 * McIDAS-V is free software; you can redistribute it and/or modify 018 * it under the terms of the GNU Lesser Public License as published by 019 * the Free Software Foundation; either version 3 of the License, or 020 * (at your option) any later version. 021 * 022 * McIDAS-V is distributed in the hope that it will be useful, 023 * but WITHOUT ANY WARRANTY; without even the implied warranty of 024 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 025 * GNU Lesser Public License for more details. 026 * 027 * You should have received a copy of the GNU Lesser Public License 028 * along with this program. If not, see http://www.gnu.org/licenses. 029 */ 030package edu.wisc.ssec.mcidasv.servermanager; 031 032import static edu.wisc.ssec.mcidasv.util.Contract.notNull; 033import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.arrList; 034import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.cast; 035import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashMap; 036import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashSet; 037 038import java.io.File; 039import java.io.IOException; 040import java.util.*; 041 042import org.bushe.swing.event.EventBus; 043import org.bushe.swing.event.annotation.AnnotationProcessor; 044import org.bushe.swing.event.annotation.EventSubscriber; 045 046import org.slf4j.Logger; 047import org.slf4j.LoggerFactory; 048 049import org.w3c.dom.Element; 050 051import ucar.unidata.idv.IdvObjectStore; 052import ucar.unidata.idv.IdvResourceManager; 053import ucar.unidata.idv.chooser.adde.AddeServer; 054import ucar.unidata.xml.XmlResourceCollection; 055 056import edu.wisc.ssec.mcidasv.Constants; 057import edu.wisc.ssec.mcidasv.McIDASV; 058import edu.wisc.ssec.mcidasv.ResourceManager; 059import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntrySource; 060import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryStatus; 061import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType; 062import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryValidity; 063import edu.wisc.ssec.mcidasv.servermanager.AddeThread.McservEvent; 064import edu.wisc.ssec.mcidasv.util.trie.CharSequenceKeyAnalyzer; 065import edu.wisc.ssec.mcidasv.util.trie.PatriciaTrie; 066 067public class EntryStore { 068 069 /** 070 * Property that allows users to supply arbitrary paths to McIDAS-X 071 * binaries used by mcservl. 072 * 073 * @see #getAddeRootDirectory() 074 */ 075 private static final String PROP_DEBUG_LOCALROOT = "debug.localadde.rootdir"; 076 077 /** 078 * Property that allows users to control debug output from ADDE requests. 079 * 080 * @see #isAddeDebugEnabled(boolean) 081 * @see #setAddeDebugEnabled(boolean) 082 */ 083 private static final String PROP_DEBUG_ADDEURL = "debug.adde.reqs"; 084 085 /** Enumeration of the various server manager events. */ 086 public enum Event { REPLACEMENT, REMOVAL, ADDITION, UPDATE, FAILURE, STARTED, UNKNOWN } 087 088 private static final Logger logger = LoggerFactory.getLogger(EntryStore.class); 089 090 private static final String PREF_ADDE_ENTRIES = "mcv.servers.entries"; 091 092 /** The ADDE servers known to McIDAS-V. */ 093 private final PatriciaTrie<String, AddeEntry> trie; 094 095 /** {@literal "Root"} local server directory. */ 096 private final String ADDE_DIRECTORY; 097 098 /** Path to local server binaries. */ 099 private final String ADDE_BIN; 100 101 /** Path to local server data. */ 102 private final String ADDE_DATA; 103 104 /** Path to mcservl. */ 105 private final String ADDE_MCSERVL; 106 107 /** Path to the user's {@literal "userpath"} directory. */ 108 private final String USER_DIRECTORY; 109 110 /** Path to the user's {@literal "RESOLV.SRV"}. */ 111 private final String ADDE_RESOLV; 112 113 /** */ 114 private final String MCTRACE; 115 116 /** Which port is this particular manager operating on */ 117 private static String localPort; 118 119 /** Thread that monitors the mcservl process. */ 120 private static AddeThread thread; 121 122 /** The last {@link AddeEntry}s added to the manager. */ 123 private final List<AddeEntry> lastAdded; 124 125 private final IdvObjectStore idvStore; 126 127 private boolean restartingMcserv; 128 129 public EntryStore(final IdvObjectStore store, final IdvResourceManager rscManager) { 130 notNull(store); 131 notNull(rscManager); 132 133 this.idvStore = store; 134 this.trie = new PatriciaTrie<String, AddeEntry>(new CharSequenceKeyAnalyzer()); 135 this.ADDE_DIRECTORY = getAddeRootDirectory(); 136 this.ADDE_BIN = ADDE_DIRECTORY + File.separator + "bin"; 137 this.ADDE_DATA = ADDE_DIRECTORY + File.separator + "data"; 138 this.localPort = Constants.LOCAL_ADDE_PORT; 139 this.restartingMcserv = false; 140 this.lastAdded = arrList(); 141 AnnotationProcessor.process(this); 142 143 McIDASV mcv = McIDASV.getStaticMcv(); 144 USER_DIRECTORY = mcv.getUserDirectory(); 145 ADDE_RESOLV = mcv.getUserFile("RESOLV.SRV"); 146 MCTRACE = "0"; 147 148 if (McIDASV.isWindows()) { 149 ADDE_MCSERVL = ADDE_BIN + "\\mcservl.exe"; 150 } else { 151 ADDE_MCSERVL = ADDE_BIN + "/mcservl"; 152 } 153 154 try { 155 Set<LocalAddeEntry> locals = EntryTransforms.readResolvFile(ADDE_RESOLV); 156 putEntries(trie, locals); 157 } catch (IOException e) { 158 logger.warn("EntryStore: RESOLV.SRV missing; expected=\"" + ADDE_RESOLV + '"'); 159 } 160 161 XmlResourceCollection userResource = rscManager.getXmlResources(ResourceManager.RSC_NEW_USERSERVERS); 162 XmlResourceCollection sysResource = rscManager.getXmlResources(IdvResourceManager.RSC_ADDESERVER); 163 putEntries(trie, extractFromPreferences(store)); 164 putEntries(trie, extractUserEntries(userResource)); 165 putEntries(trie, extractResourceEntries(EntrySource.SYSTEM, sysResource)); 166 } 167 168 private static void putEntries(final PatriciaTrie<String, AddeEntry> trie, final Collection<? extends AddeEntry> newEntries) { 169 notNull(trie); 170 notNull(newEntries); 171 for (AddeEntry e : newEntries) { 172 trie.put(e.asStringId(), e); 173 } 174 } 175 176 protected IdvObjectStore getIdvStore() { 177 return idvStore; 178 } 179 180 protected String[] getWindowsAddeEnv() { 181 // Drive letters should come from environment 182 // Java drive is not necessarily system drive 183 return new String[] { 184 "PATH=" + ADDE_BIN, 185 "MCPATH=" + USER_DIRECTORY+':'+ADDE_DATA, 186 "MCNOPREPEND=1", 187 "MCTRACE=" + MCTRACE, 188 "MCJAVAPATH=" + System.getProperty("java.home"), 189 "MCBUFRJARPATH=" + ADDE_BIN, 190 "SYSTEMDRIVE=" + System.getenv("SystemDrive"), 191 "SYSTEMROOT=" + System.getenv("SystemRoot"), 192 "HOMEDRIVE=" + System.getenv("HOMEDRIVE"), 193 "HOMEPATH=\\Windows" 194 }; 195 } 196 197 protected String[] getUnixAddeEnv() { 198 return new String[] { 199 "PATH=" + ADDE_BIN, 200 "MCPATH=" + USER_DIRECTORY+':'+ADDE_DATA, 201 "LD_LIBRARY_PATH=" + ADDE_BIN, 202 "DYLD_LIBRARY_PATH=" + ADDE_BIN, 203 "MCNOPREPEND=1", 204 "MCTRACE=" + MCTRACE, 205 "MCJAVAPATH=" + System.getProperty("java.home"), 206 "MCBUFRJARPATH=" + ADDE_BIN 207 }; 208 } 209 210 protected String[] getAddeCommands() { 211 return new String[] { ADDE_MCSERVL, "-p", localPort, "-v" }; 212 } 213 214 /** 215 * Determine the validity of a given {@link edu.wisc.ssec.mcidasv.servermanager.AddeEntry AddeEntry}. 216 * 217 * @param entry Entry to check. Cannot be {@code null}. 218 * 219 * @return {@code true} if {@code entry} is invalid or {@code false} otherwise. 220 * 221 * @throws AssertionError if {@code entry} is somehow neither a {@code RemoteAddeEntry} or {@code LocalAddeEntry}. 222 * 223 * @see edu.wisc.ssec.mcidasv.servermanager.LocalAddeEntry#INVALID_ENTRY 224 * @see edu.wisc.ssec.mcidasv.servermanager.RemoteAddeEntry#INVALID_ENTRY 225 */ 226 public static boolean isInvalidEntry(final AddeEntry entry) { 227 notNull(entry); 228 boolean retVal = true; 229 if (entry instanceof RemoteAddeEntry) { 230 retVal = RemoteAddeEntry.INVALID_ENTRY.equals(entry); 231 } else if (entry instanceof LocalAddeEntry) { 232 retVal = LocalAddeEntry.INVALID_ENTRY.equals(entry); 233 } else { 234 throw new AssertionError("Unknown AddeEntry type: "+entry.getClass().getName()); 235 } 236 return retVal; 237 } 238 239 /** 240 * Returns the {@link edu.wisc.ssec.mcidasv.servermanager.AddeEntry AddeEntrys} stored 241 * in the user's preferences. 242 * 243 * @param store Object store that represents the user's preferences. Cannot be {@code null}. 244 * 245 * @return Either the {@code AddeEntrys} stored in the prefs or an empty {@link java.util.Set Set}. 246 */ 247 private Set<AddeEntry> extractFromPreferences(final IdvObjectStore store) { 248 assert store != null; 249 250 // this is valid--the only thing ever written to 251 // PREF_REMOTE_ADDE_ENTRIES is an ArrayList of RemoteAddeEntry objects. 252 @SuppressWarnings("unchecked") 253 List<AddeEntry> asList = 254 (List<AddeEntry>)store.get(PREF_ADDE_ENTRIES); 255 Set<AddeEntry> entries; 256 if (asList == null) { 257 entries = Collections.emptySet(); 258 } else { 259 entries = newLinkedHashSet(asList.size()); 260 for (AddeEntry entry : asList) { 261 if (entry instanceof RemoteAddeEntry) { 262 entries.add(entry); 263 } 264 } 265 } 266 return entries; 267 } 268 269 /** 270 * Responds to server manager events being passed with the event bus. 271 * 272 * @param evt Event to which this method is responding. 273 */ 274 @EventSubscriber(eventClass=Event.class) 275 public void onEvent(Event evt) { 276 notNull(evt); 277 saveEntries(); 278 } 279 280 /** 281 * Saves the current set of ADDE servers to the user's preferences. 282 */ 283 public void saveEntries() { 284 idvStore.put(PREF_ADDE_ENTRIES, arrList(trie.values())); 285 idvStore.saveIfNeeded(); 286 try { 287 EntryTransforms.writeResolvFile(ADDE_RESOLV, getLocalEntries()); 288 } catch (IOException e) { 289 logger.error("EntryStore: RESOLV.SRV missing; expected=\""+ADDE_RESOLV+"\""); 290 } 291 } 292 293 /** 294 * Searches the newest entries for the entries of the given {@link edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType EntryType}. 295 * 296 * @param type Look for entries matching this {@code EntryType}. Cannot be {@code null}. 297 * 298 * @return Either a {@link java.util.List List} of entries or an empty {@code List}. 299 * 300 * @throws NullPointerException if {@code type} is {@code null}. 301 */ 302 public List<AddeEntry> getLastAddedByType(final EntryType type) { 303 notNull(type); 304 List<AddeEntry> entries = arrList(); 305 for (AddeEntry entry : lastAdded) { 306 if (entry.getEntryType() == type) { 307 entries.add(entry); 308 } 309 } 310 return entries; 311 } 312 313 public List<AddeEntry> getLastAddedByTypes(final EnumSet<EntryType> types) { 314 notNull(types); 315 List<AddeEntry> entries = arrList(); 316 for (AddeEntry entry : lastAdded) { 317 if (types.contains(entry.getEntryType())) { 318 entries.add(entry); 319 } 320 } 321 return entries; 322 } 323 324 public List<AddeEntry> getLastAdded() { 325 return arrList(lastAdded); 326 } 327 328 /** 329 * Returns the {@link Set} of {@link AddeEntry}s that are known to work (for 330 * a given {@link EntryType} of entries). 331 * 332 * @param type The {@code EntryType} you are interested in. 333 * 334 * @return A {@code Set} of matching {@code RemoteAddeEntry}s. If there 335 * were no matches, an empty {@code Set} is returned. 336 */ 337 public Set<AddeEntry> getVerifiedEntries(final EntryType type) { 338 notNull(type); 339 Set<AddeEntry> verified = newLinkedHashSet(trie.size()); 340 for (AddeEntry entry : trie.values()) { 341 if (entry.getEntryType() != type) 342 continue; 343 344 if (entry instanceof LocalAddeEntry) { 345 verified.add(entry); 346 } else if (entry.getEntryValidity() == EntryValidity.VERIFIED) { 347 verified.add(entry); 348 } 349 } 350 return verified; 351 } 352 353 // TODO(jon): better name 354 public Map<EntryType, Set<AddeEntry>> getVerifiedEntriesByTypes() { 355 Map<EntryType, Set<AddeEntry>> entryMap = 356 newLinkedHashMap(EntryType.values().length); 357 int size = trie.size(); 358 for (EntryType type : EntryType.values()) { 359 entryMap.put(type, new LinkedHashSet<AddeEntry>(size)); 360 } 361 362 for (AddeEntry entry : trie.values()) { 363 Set<AddeEntry> entrySet = entryMap.get(entry.getEntryType()); 364 entrySet.add(entry); 365 } 366 return entryMap; 367 } 368 369 /** 370 * Returns the {@link Set} of {@link AddeEntry#group}s that match 371 * the given {@code address} and {@code type}. 372 * 373 * @param address ADDE server address whose groups are needed. 374 * Cannot be {@code null}. 375 * @param type Only include groups that match {@link EntryType}. 376 * Cannot be {@code null}. 377 * 378 * @return Either a set containing the desired groups, or an empty set if 379 * there were no matches. 380 */ 381 public Set<String> getGroupsFor(final String address, EntryType type) { 382 notNull(address); 383 notNull(type); 384 Set<String> groups = newLinkedHashSet(trie.size()); 385 for (AddeEntry entry : trie.getPrefixedBy(address+'!').values()) { 386 if (entry.getAddress().equals(address) && entry.getEntryType() == type) { 387 groups.add(entry.getGroup()); 388 } 389 } 390 return groups; 391 } 392 393 /** 394 * Search the server manager for entries that match {@code prefix}. 395 * 396 * @param prefix {@code String} to match. 397 * 398 * @return {@link List} containing matching entries. If there were no 399 * matches the {@code List} will be empty. 400 * 401 * @see AddeEntry#asStringId() 402 */ 403 public List<AddeEntry> searchWithPrefix(final String prefix) { 404 notNull(prefix); 405 return arrList(trie.getPrefixedBy(prefix).values()); 406 } 407 408 /** 409 * Returns the {@link Set} of {@link AddeEntry} addresses stored 410 * in this {@code EntryStore}. 411 * 412 * @return {@code Set} containing all of the stored addresses. If no 413 * addresses are stored, an empty {@code Set} is returned. 414 */ 415 public Set<String> getAddresses() { 416 Set<String> addresses = newLinkedHashSet(trie.size()); 417 for (AddeEntry entry : trie.values()) { 418 addresses.add(entry.getAddress()); 419 } 420 return addresses; 421 } 422 423 /** 424 * Returns a {@link Set} containing <b>ADDRESS/GROUPNAME</b> {@code String}s 425 * for each {@link RemoteAddeEntry}. 426 * 427 * @return The {@literal "entry text"} representations of each 428 * {@code RemoteAddeEntry}. 429 * 430 * @see RemoteAddeEntry#getEntryText() 431 */ 432 protected Set<String> getRemoteEntryTexts() { 433 Set<String> strs = newLinkedHashSet(trie.size()); 434 for (AddeEntry entry : trie.values()) { 435 if (entry instanceof RemoteAddeEntry) { 436 strs.add(entry.getEntryText()); 437 } 438 } 439 return strs; 440 } 441 442 /** 443 * Returns the {@link Set} of {@literal "groups"} associated with the 444 * given {@code address}. 445 * 446 * @param address Address of a server. 447 * 448 * @return Either all of the {@literal "groups"} on {@code address} or an 449 * empty {@code Set}. 450 */ 451 public Set<String> getGroups(final String address) { 452 notNull(address); 453 Set<String> groups = newLinkedHashSet(trie.size()); 454 for (AddeEntry entry : trie.getPrefixedBy(address+'!').values()) { 455 groups.add(entry.getGroup()); 456 } 457 return groups; 458 } 459 460 /** 461 * Returns the {@link Set} of {@link EntryType}s for a given {@code group} 462 * on a given {@code address}. 463 * 464 * @param address Address of a server. 465 * @param group Group whose {@literal "types"} you want. 466 * 467 * @return Either of all the types for a given {@code address} and 468 * {@code group} or an empty {@code Set} if there were no matches. 469 */ 470 public Set<EntryType> getTypes(final String address, final String group) { 471 Set<EntryType> types = newLinkedHashSet(trie.size()); 472 for (AddeEntry entry : trie.getPrefixedBy(address+'!'+group+'!').values()) { 473 types.add(entry.getEntryType()); 474 } 475 return types; 476 } 477 478 /** 479 * Searches the set of servers in an attempt to locate the accounting 480 * information for the matching server. <b>Note</b> that because the data 481 * structure is a {@link Set}, there <i>cannot</i> be duplicate entries, 482 * so there is no need to worry about our criteria finding multiple 483 * matches. 484 * 485 * <p>Also note that none of the given parameters accept {@code null} 486 * values. 487 * 488 * @param address Address of the server. 489 * @param group Dataset. 490 * @param type Group type. 491 * 492 * @return Either the {@link AddeAccount} for the given criteria, or 493 * {@link AddeEntry#DEFAULT_ACCOUNT} if there was no match. 494 * 495 * @see RemoteAddeEntry#equals(Object) 496 */ 497 public AddeAccount getAccountingFor(final String address, final String group, EntryType type) { 498 Collection<AddeEntry> entries = trie.getPrefixedBy(address+'!'+group+'!'+type.name()).values(); 499 for (AddeEntry entry : entries) { 500 if (!isInvalidEntry(entry)) { 501 return entry.getAccount(); 502 } 503 } 504 return AddeEntry.DEFAULT_ACCOUNT; 505 } 506 507 public AddeAccount getAccountingFor(final AddeServer idvServer, String typeAsStr) { 508 String address = idvServer.getName(); 509 List<AddeServer.Group> groups = cast(idvServer.getGroups()); 510 if (groups != null && !groups.isEmpty()) { 511 EntryType type = EntryTransforms.strToEntryType(typeAsStr); 512 return getAccountingFor(address, groups.get(0).getName(), type); 513 } else { 514 return RemoteAddeEntry.DEFAULT_ACCOUNT; 515 } 516 } 517 518 /** 519 * Returns the complete {@link Set} of {@link AddeEntry}s. 520 */ 521 protected Set<AddeEntry> getEntrySet() { 522 return newLinkedHashSet(trie.values()); 523 } 524 525 /** 526 * Returns the complete {@link Set} of {@link RemoteAddeEntry}s. 527 * 528 * @return The {@code RemoteAddeEntry}s stored within {@link #entries}. 529 */ 530 protected Set<RemoteAddeEntry> getRemoteEntries() { 531 Set<RemoteAddeEntry> remotes = newLinkedHashSet(trie.size()); 532 for (AddeEntry e : trie.values()) { 533 if (e instanceof RemoteAddeEntry) { 534 remotes.add((RemoteAddeEntry)e); 535 } 536 } 537 return remotes; 538 } 539 540 /** 541 * Returns the complete {@link Set} of {@link LocalAddeEntry}s. 542 * 543 * @return The {@code LocalAddeEntry}s stored within {@link #entries}. 544 */ 545 protected Set<LocalAddeEntry> getLocalEntries() { 546 Set<LocalAddeEntry> locals = newLinkedHashSet(trie.size()); 547 for (AddeEntry e : trie.getPrefixedBy("localhost").values()) { 548 if (e instanceof LocalAddeEntry) { 549 locals.add((LocalAddeEntry)e); 550 } 551 } 552 return locals; 553 } 554 555 protected boolean removeEntries( 556 final Collection<? extends AddeEntry> removedEntries) 557 { 558 notNull(removedEntries); 559 560 boolean val = true; 561 boolean tmpVal = true; 562 for (AddeEntry entry : removedEntries) { 563 if (entry.getEntrySource() != EntrySource.SYSTEM) { 564 tmpVal = (trie.remove(entry.asStringId()) != null); 565 logger.trace("attempted bulk remove={} status={}", entry, tmpVal); 566 if (!tmpVal) { 567 val = tmpVal; 568 } 569 } 570 } 571 Event evt = (val) ? Event.REMOVAL : Event.FAILURE; 572 saveEntries(); 573 EventBus.publish(evt); 574 return val; 575 } 576 577 protected boolean removeEntry(final AddeEntry entry) { 578 notNull(entry); 579 boolean val = (trie.remove(entry.asStringId()) != null); 580 logger.trace("attempted remove={} status={}", entry, val); 581 Event evt = (val) ? Event.REMOVAL : Event.FAILURE; 582 saveEntries(); 583 EventBus.publish(evt); 584 return val; 585 } 586 587 /** 588 * Adds a {@link Set} of {@link AddeEntry}s to {@link #trie}. 589 * 590 * @param newEntries New entries to add to the server manager. Cannot be 591 * {@code null}. 592 * 593 * @throws NullPointerException if {@code newEntries} is {@code null}. 594 */ 595 public void addEntries(final Collection<? extends AddeEntry> newEntries) { 596 notNull(newEntries, "Cannot add a null set"); 597 for (AddeEntry newEntry : newEntries) { 598 trie.put(newEntry.asStringId(), newEntry); 599 } 600 saveEntries(); 601 lastAdded.clear(); 602 lastAdded.addAll(newEntries); 603 EventBus.publish(Event.ADDITION); 604 } 605 606 /** 607 * Replaces the {@link AddeEntry}s within {@code trie} with the contents 608 * of {@code newEntries}. 609 * 610 * @param oldEntries Entries to be replaced. Cannot be {@code null}. 611 * @param newEntries Entries to use as replacements. Cannot be 612 * {@code null}. 613 * 614 * @throws NullPointerException if either of {@code oldEntries} or 615 * {@code newEntries} is {@code null}. 616 */ 617 public void replaceEntries(final Collection<? extends AddeEntry> oldEntries, final Collection<? extends AddeEntry> newEntries) { 618 notNull(oldEntries, "Cannot replace a null set"); 619 notNull(newEntries, "Cannot add a null set"); 620 621 for (AddeEntry oldEntry : oldEntries) { 622 trie.remove(oldEntry.asStringId()); 623 } 624 for (AddeEntry newEntry : newEntries) { 625 trie.put(newEntry.asStringId(), newEntry); 626 } 627 lastAdded.clear(); 628 lastAdded.addAll(newEntries); // should probably be more thorough 629 saveEntries(); 630 EventBus.publish(Event.REPLACEMENT); 631 } 632 633 // if true, filters out disabled local groups; if false, returns all local groups 634 public Set<AddeServer.Group> getIdvStyleLocalGroups() { 635 Set<LocalAddeEntry> localEntries = getLocalEntries(); 636 Set<AddeServer.Group> idvGroups = newLinkedHashSet(localEntries.size()); 637 for (LocalAddeEntry entry : localEntries) { 638 if (entry.getEntryStatus() == EntryStatus.ENABLED && entry.getEntryValidity() == EntryValidity.VERIFIED) { 639 String group = entry.getGroup(); 640 AddeServer.Group idvGroup = new AddeServer.Group("IMAGE", group, group); 641 idvGroups.add(idvGroup); 642 } 643 } 644 return idvGroups; 645 } 646 647 public Set<AddeServer.Group> getIdvStyleRemoteGroups(final String server, final String typeAsStr) { 648 return getIdvStyleRemoteGroups(server, EntryTransforms.strToEntryType(typeAsStr)); 649 } 650 651 public Set<AddeServer.Group> getIdvStyleRemoteGroups(final String server, final EntryType type) { 652 Set<AddeServer.Group> idvGroups = newLinkedHashSet(trie.size()); 653 String typeStr = type.name(); 654 for (AddeEntry matched : trie.getPrefixedBy(server).values()) { 655 if (matched == RemoteAddeEntry.INVALID_ENTRY) { 656 continue; 657 } 658 659 if (matched.getEntryStatus() == EntryStatus.ENABLED && matched.getEntryValidity() == EntryValidity.VERIFIED && matched.getEntryType() == type) { 660 String group = matched.getGroup(); 661 idvGroups.add(new AddeServer.Group(typeStr, group, group)); 662 } 663 } 664 return idvGroups; 665 } 666 667 public List<AddeServer> getIdvStyleEntries() { 668 return arrList(EntryTransforms.convertMcvServers(getEntrySet())); 669 } 670 671 public Set<AddeServer> getIdvStyleEntries(final EntryType type) { 672 return EntryTransforms.convertMcvServers(getVerifiedEntries(type)); 673 } 674 675 public Set<AddeServer> getIdvStyleEntries(final String typeAsStr) { 676 return getIdvStyleEntries(EntryTransforms.strToEntryType(typeAsStr)); 677 } 678 679 /** 680 * Process all of the {@literal "IDV-style"} XML resources. 681 * 682 * @param source 683 * @param xmlResources 684 * 685 * @return 686 */ 687 private Set<AddeEntry> extractResourceEntries(EntrySource source, final XmlResourceCollection xmlResources) { 688 Set<AddeEntry> entries = newLinkedHashSet(xmlResources.size()); 689 for (int i = 0; i < xmlResources.size(); i++) { 690 Element root = xmlResources.getRoot(i); 691 if (root == null) { 692 continue; 693 } 694 entries.addAll(EntryTransforms.convertAddeServerXml(root, source)); 695 } 696 return entries; 697 } 698 699 /** 700 * Process all of the {@literal "user"} XML resources. 701 * 702 * @param xmlResources Resource collection. Cannot be {@code null}. 703 * 704 * @return {@link Set} of {@link RemoteAddeEntry}s contained within 705 * {@code resource}. 706 */ 707 private Set<AddeEntry> extractUserEntries(final XmlResourceCollection xmlResources) { 708 int rcSize = xmlResources.size(); 709 Set<AddeEntry> entries = newLinkedHashSet(rcSize); 710 for (int i = 0; i < rcSize; i++) { 711 Element root = xmlResources.getRoot(i); 712 if (root == null) { 713 continue; 714 } 715 entries.addAll(EntryTransforms.convertUserXml(root)); 716 } 717 return entries; 718 } 719 720 /** 721 * Returns the path to where the root directory of the user's McIDAS-X 722 * binaries <b>should</b> be. <b>The path may be invalid.</b> 723 * 724 * <p>The default path is determined like so: 725 * <pre> 726 * System.getProperty("user.dir") + File.separatorChar + "adde" 727 * </pre> 728 * 729 * <p>Users can provide an arbitrary path at runtime by setting the 730 * {@code debug.localadde.rootdir} system property. 731 * 732 * @return {@code String} containing the path to the McIDAS-X root directory. 733 * 734 * @see #PROP_DEBUG_LOCALROOT 735 */ 736 public static String getAddeRootDirectory() { 737 if (System.getProperties().containsKey(PROP_DEBUG_LOCALROOT)) { 738 return System.getProperty(PROP_DEBUG_LOCALROOT); 739 } 740 return System.getProperty("user.dir") + File.separatorChar + "adde"; 741 } 742 743 /** 744 * Checks the value of the {@code debug.adde.reqs} system property to 745 * determine whether or not the user has requested ADDE URL debugging 746 * output. Output is sent to {@link System#out}. 747 * 748 * <p>Please keep in mind that the {@code debug.adde.reqs} can not 749 * force debugging for <i>all</i> ADDE requests. To do so will require 750 * updates to the VisAD ADDE library. 751 * 752 * @param defaultValue Value to return if {@code debug.adde.reqs} has 753 * not been set. 754 * 755 * @return If it exists, the value of {@code debug.adde.reqs}. 756 * Otherwise {@code debug.adde.reqs}. 757 * 758 * @see edu.wisc.ssec.mcidas.adde.AddeURL 759 * @see #PROP_DEBUG_ADDEURL 760 */ 761 // TODO(jon): this sort of thing should *really* be happening within the 762 // ADDE library. 763 public static boolean isAddeDebugEnabled(final boolean defaultValue) { 764 return Boolean.parseBoolean(System.getProperty(PROP_DEBUG_ADDEURL, Boolean.toString(defaultValue))); 765 } 766 767 /** 768 * Sets the value of the {@code debug.adde.reqs} system property so 769 * that debugging output can be controlled without restarting McIDAS-V. 770 * 771 * <p>Please keep in mind that the {@code debug.adde.reqs} can not 772 * force debugging for <i>all</i> ADDE requests. To do so will require 773 * updates to the VisAD ADDE library. 774 * 775 * @param value New value of {@code debug.adde.reqs}. 776 * 777 * @return Previous value of {@code debug.adde.reqs}. 778 * 779 * @see edu.wisc.ssec.mcidas.adde.AddeURL 780 * @see #PROP_DEBUG_ADDEURL 781 */ 782 public static boolean setAddeDebugEnabled(final boolean value) { 783 return Boolean.parseBoolean(System.setProperty(PROP_DEBUG_ADDEURL, Boolean.toString(value))); 784 } 785 786 /** 787 * Change the port we are listening on. 788 * 789 * @param port New port number. 790 */ 791 public static void setLocalPort(final String port) { 792 localPort = port; 793 } 794 795 /** 796 * Ask for the port we are listening on 797 */ 798 public static String getLocalPort() { 799 return localPort; 800 } 801 802 /** 803 * Get the next port by incrementing current port. 804 */ 805 protected static String nextLocalPort() { 806 return Integer.toString(Integer.parseInt(localPort) + 1); 807 } 808 809 /** 810 * start addeMcservl if it exists 811 */ 812 public void startLocalServer(final boolean restarting) { 813 if ((new File(ADDE_MCSERVL)).exists()) { 814 // Create and start the thread if there isn't already one running 815 if (!checkLocalServer()) { 816 thread = new AddeThread(this); 817 thread.start(); 818 EventBus.publish(McservEvent.STARTED); 819 } 820 } 821 } 822 823 /** 824 * stop the thread if it is running 825 */ 826 public void stopLocalServer(final boolean restarting) { 827 if (checkLocalServer()) { 828 //TODO: stopProcess (actually Process.destroy()) hangs on Macs... 829 // doesn't seem to kill the children properly 830 if (!McIDASV.isMac()) { 831 thread.stopProcess(); 832 } 833 834 thread.interrupt(); 835 thread = null; 836 if (!restarting) { 837 EventBus.publish(McservEvent.STOPPED); 838 } 839 } 840 } 841 842 /** 843 * restart the thread 844 */ 845 synchronized public void restartLocalServer() { 846 restartingMcserv = true; 847 if (checkLocalServer()) { 848 stopLocalServer(restartingMcserv); 849 } 850 startLocalServer(restartingMcserv); 851 restartingMcserv = false; 852 } 853 854 synchronized public boolean getRestarting() { 855 return restartingMcserv; 856 } 857 858 /** 859 * check to see if the thread is running 860 */ 861 public boolean checkLocalServer() { 862 if (thread != null && thread.isAlive()) { 863 return true; 864 } else { 865 return false; 866 } 867 } 868}