001/* 002 * This file is part of McIDAS-V 003 * 004 * Copyright 2007-2016 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 029package edu.wisc.ssec.mcidasv.servermanager; 030 031import static java.util.Objects.requireNonNull; 032import static edu.wisc.ssec.mcidasv.util.CollectionHelpers.newLinkedHashSet; 033import static edu.wisc.ssec.mcidasv.util.Contract.checkArg; 034 035import java.io.IOException; 036 037import java.net.InetSocketAddress; 038import java.net.Socket; 039import java.net.UnknownHostException; 040 041import java.util.Collections; 042import java.util.EnumMap; 043import java.util.List; 044import java.util.Map; 045import java.util.Set; 046 047import org.slf4j.Logger; 048import org.slf4j.LoggerFactory; 049 050import edu.wisc.ssec.mcidas.adde.AddeServerInfo; 051import edu.wisc.ssec.mcidas.adde.AddeTextReader; 052import edu.wisc.ssec.mcidas.adde.AddeURLException; 053import edu.wisc.ssec.mcidas.adde.DataSetInfo; 054 055import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntrySource; 056import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryStatus; 057import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryType; 058import edu.wisc.ssec.mcidasv.servermanager.AddeEntry.EntryValidity; 059import edu.wisc.ssec.mcidasv.servermanager.RemoteEntryEditor.AddeStatus; 060 061public class RemoteAddeEntry implements AddeEntry { 062 063 /** Typical logger object. */ 064 private static final Logger logger = 065 LoggerFactory.getLogger(RemoteAddeEntry.class); 066 067 /** Represents an invalid remote ADDE entry. */ 068 public static final RemoteAddeEntry INVALID_ENTRY = 069 new Builder("localhost", "BIGBAD").invalidate().build(); 070 071 /** Represents a collection of invalid remote ADDE entries. */ 072 public static final List<RemoteAddeEntry> INVALID_ENTRIES = 073 Collections.singletonList(INVALID_ENTRY); 074 075 /** Default port for remote ADDE servers. */ 076 public static final int ADDE_PORT = 112; 077 078 /** 079 * {@link String#format(String, Object...)}-friendly string for building a 080 * request to read a server's {@literal "PUBLIC.SRV"}. 081 */ 082 private static final String publicSrvFormat = "adde://%s/text?compress=gzip&port=112&debug=%s&version=1&user=%s&proj=%s&file=PUBLIC.SRV"; 083 084 /** Holds the accounting information for this entry. */ 085 private final AddeAccount account; 086 087 /** The server {@literal "address"} of this entry. */ 088 private final String address; 089 090 /** The {@literal "dataset"} of this entry. */ 091 private final String group; 092 093 /** Whether or not this entry will persist between McIDAS-V sessions. */ 094 private final boolean isTemporary; 095 096 /** This entry's type. */ 097 private EntryType entryType; 098 099 /** Whether or not this entry is valid. */ 100 private EntryValidity entryValidity; 101 102 /** Where this entry came from. */ 103 private EntrySource entrySource; 104 105 /** Whether or not this entry is in the {@literal "active set"}. */ 106 private EntryStatus entryStatus; 107 108 /** Allows the user to refer to this entry with an arbitrary name. */ 109 private String entryAlias; 110 111 private String asStringId; 112 113 /** 114 * Used so that the hashCode of this entry is not needlessly 115 * recalculated. 116 * 117 * @see #hashCode() 118 */ 119 private volatile int hashCode = 0; 120 121 /** 122 * Creates a new ADDE entry using a give {@literal "ADDE entry builder"}. 123 * 124 * @param builder Object used to build this entry. 125 */ 126 private RemoteAddeEntry(Builder builder) { 127 this.account = builder.account; 128 this.address = builder.address; 129 this.group = builder.group; 130 this.entryType = builder.entryType; 131 this.entryValidity = builder.entryValidity; 132 this.entrySource = builder.entrySource; 133 this.entryStatus = builder.entryStatus; 134 this.isTemporary = builder.temporary; 135 this.entryAlias = builder.alias; 136 } 137 138 /** 139 * @return {@link #address} 140 */ 141 @Override public String getAddress() { 142 return address; 143 } 144 145 /** 146 * @return {@link #group} 147 */ 148 @Override public String getGroup() { 149 return group; 150 } 151 152 @Override public String getName() { 153 return "$"; 154 } 155 156 /** 157 * @return {@link #account} 158 */ 159 @Override public AddeAccount getAccount() { 160 return account; 161 } 162 163 /** 164 * @return {@link #entryType} 165 */ 166 @Override public EntryType getEntryType() { 167 return entryType; 168 } 169 170 /** 171 * @return {@link #entryValidity} 172 */ 173 @Override public EntryValidity getEntryValidity() { 174 return entryValidity; 175 } 176 177 public void setEntryValidity(final EntryValidity entryValidity) { 178 this.entryValidity = entryValidity; 179 } 180 181 /** 182 * @return {@link #entrySource} 183 */ 184 @Override public EntrySource getEntrySource() { 185 return entrySource; 186 } 187 188 /** 189 * @return {@link #entryStatus} 190 */ 191 @Override public EntryStatus getEntryStatus() { 192 return entryStatus; 193 } 194 195 @Override public void setEntryStatus(EntryStatus newStatus) { 196 entryStatus = newStatus; 197 } 198 199 @Override public String getEntryAlias() { 200 return entryAlias; 201 } 202 203 @Override public void setEntryAlias(final String newAlias) { 204 if (newAlias == null) { 205 throw new NullPointerException("Null aliases are not allowable."); 206 } 207 entryAlias = newAlias; 208 } 209 210 @Override public boolean isEntryTemporary() { 211 return isTemporary; 212 } 213 214 /** 215 * Handy {@code String} representation of this ADDE entry. Currently looks 216 * like {@code ADDRESS/GROUP}, but this is subject to change. 217 * 218 * @return Alternate {@code String} representation of this entry. 219 */ 220 @Override public String getEntryText() { 221 return address+'/'+group; 222 } 223 224 /** 225 * Determines whether or not the given object is equivalent to this ADDE 226 * entry. 227 * 228 * @param obj Object to test against. {@code null} values are okay, but 229 * return {@code false}. 230 * 231 * @return {@code true} if the given object is the same as this ADDE 232 * entry, {@code false} otherwise... including when {@code o} is 233 * {@code null}. 234 */ 235 @Override public boolean equals(Object obj) { 236 if (this == obj) { 237 return true; 238 } 239 if (obj == null) { 240 return false; 241 } 242 if (!(obj instanceof RemoteAddeEntry)) { 243 return false; 244 } 245 RemoteAddeEntry other = (RemoteAddeEntry) obj; 246 if (account == null) { 247 if (other.account != null) { 248 return false; 249 } 250 } else if (!account.equals(other.account)) { 251 return false; 252 } 253 if (address == null) { 254 if (other.address != null) { 255 return false; 256 } 257 } else if (!address.equals(other.address)) { 258 return false; 259 } 260 if (entryType == null) { 261 if (other.entryType != null) { 262 return false; 263 } 264 } else if (!entryType.equals(other.entryType)) { 265 return false; 266 } 267 if (group == null) { 268 if (other.group != null) { 269 return false; 270 } 271 } else if (!group.equals(other.group)) { 272 return false; 273 } 274 if (entryAlias == null) { 275 if (other.entryAlias != null) { 276 return false; 277 } 278 } else if (!entryAlias.equals(other.entryAlias)) { 279 return false; 280 } 281 if (isTemporary != other.isTemporary) { 282 return false; 283 } 284 return true; 285 } 286 287 /** 288 * Returns a hash code for this ADDE entry. The hash code is computed 289 * using the values of the following fields: 290 * {@link #address}, {@link #group}, {@link #entryType}, {@link #account}. 291 * 292 * @return Hash code value for this object. 293 */ 294 @Override public int hashCode() { 295 final int prime = 31; 296 int result = 1; 297 result = prime * result + ((account == null) ? 0 : account.hashCode()); 298 result = prime * result + ((address == null) ? 0 : address.hashCode()); 299 result = prime * result + ((entryType == null) ? 0 : entryType.hashCode()); 300 result = prime * result + ((group == null) ? 0 : group.hashCode()); 301 result = prime * result + ((entryAlias == null) ? 0 : entryAlias.hashCode()); 302 result = prime * result + (isTemporary ? 1231 : 1237); 303 return result; 304 } 305 306 @Override public String asStringId() { 307 if (asStringId == null) { 308 asStringId = address+'!'+group+'!'+entryType.name(); 309 } 310 return asStringId; 311 } 312 313 public String toString() { 314 return String.format("[RemoteAddeEntry@%x: address=%s, group=%s, entryType=%s, entryValidity=%s, account=%s, status=%s, source=%s, temporary=%s, alias=%s]", hashCode(), address, group, entryType, entryValidity, account, entryStatus.name(), entrySource, isTemporary, entryAlias); 315 } 316 317 /** 318 * Something of a hack... this approach allows us to build a 319 * {@code RemoteAddeEntry} in a <b>readable</b> way, despite there being 320 * multiple {@code final} fields. 321 * 322 * <p>The only <i>required</i> parameters are 323 * the {@link RemoteAddeEntry#address} and {@link RemoteAddeEntry#group}.</p> 324 * 325 * <p>Some examples:</p> 326 * 327 * <pre> 328 * RemoteAddeEntry e = RemoteAddeEntry.Builder("adde.cool.com", "RTIMAGES").build(); 329 * e = RemoteAddeEntry.Builder("adde.cool.com", "RTIMAGES").type(EntryType.IMAGE).account("user", "1337").build(); 330 * e = RemoteAddeEntry.Builder("adde.cool.com", "RTIMAGES").account("user", "1337").type(EntryType.IMAGE).build() 331 * e = RemoteAddeEntry.Builder("a.c.com", "RTIMGS").validity(EntryValidity.VERIFIED).build(); 332 * </pre> 333 */ 334 public static class Builder { 335 336 /** Hostname or IP of the resulting entry. */ 337 private final String address; 338 339 /** ADDE group to use for the resulting entry. */ 340 private final String group; 341 342 /** 343 * Optional {@link EntryType} of the entry. Defaults to 344 * {@link EntryType#UNKNOWN}. 345 */ 346 private EntryType entryType = EntryType.UNKNOWN; 347 348 /** Optional {@link EntryValidity} of the entry. Defaults to 349 * {@link EntryValidity#UNVERIFIED}. 350 */ 351 private EntryValidity entryValidity = EntryValidity.UNVERIFIED; 352 353 /** 354 * Optional {@link EntrySource} of the entry. Defaults to 355 * {@link EntrySource#SYSTEM}. 356 */ 357 private EntrySource entrySource = EntrySource.SYSTEM; 358 359 /** 360 * Optional {@link EntryStatus} of the entry. Defaults to 361 * {@link EntryStatus#ENABLED}. 362 */ 363 private EntryStatus entryStatus = EntryStatus.ENABLED; 364 365 /** 366 * Optional {@link AddeAccount} of the entry. Defaults to 367 * {@link RemoteAddeEntry#DEFAULT_ACCOUNT}. 368 */ 369 private AddeAccount account = RemoteAddeEntry.DEFAULT_ACCOUNT; 370 371 /** Optional description of the entry. Defaults to {@literal ""}. */ 372 private String description = ""; 373 374 /** 375 * Optional flag for whether or not the entry is temporary. 376 * Defaults to {@code false}. 377 */ 378 private boolean temporary = false; 379 380 /** Optional alias for the entry. Default to {@literal ""}. */ 381 private String alias = ""; 382 383 /** 384 * Creates a new {@literal "builder"} for an ADDE entry. Note that 385 * the two parameters to this constructor are the only <i>required</i> 386 * parameters to create an ADDE entry. 387 * 388 * @param address Address of the ADDE entry. Cannot be null. 389 * @param group Group of the ADDE entry. Cannot be null. 390 * 391 * @throws NullPointerException if either {@code address} or 392 * {@code group} is {@code null}. 393 */ 394 public Builder(final String address, final String group) { 395 if (address == null) { 396 throw new NullPointerException("ADDE address cannot be null"); 397 } 398 if (group == null) { 399 throw new NullPointerException("ADDE group cannot be null"); 400 } 401 402 this.address = address.toLowerCase(); 403 this.group = group; 404 } 405 406 /** 407 * Optional {@literal "parameter"} for an ADDE entry. Allows you to 408 * specify the accounting information. If this method is not called, 409 * the resulting ADDE entry will be built with 410 * {@link RemoteAddeEntry#DEFAULT_ACCOUNT}. 411 * 412 * @param username Username of the ADDE account. Cannot be 413 * {@code null}. 414 * @param project Project number for the ADDE account. Cannot be 415 * {@code null}. 416 * 417 * @return Current {@literal "builder"} for an ADDE entry. 418 * 419 * @see AddeAccount#AddeAccount(String, String) 420 */ 421 public Builder account(final String username, final String project) { 422 account = new AddeAccount(username, project); 423 return this; 424 } 425 426 /** 427 * Optional {@literal "parameter"} for an ADDE entry. Allows you to 428 * set the {@link RemoteAddeEntry#entryType}. If this method is not 429 * called, {@code entryType} will default to {@link EntryType#UNKNOWN}. 430 * 431 * @param entryType ADDE entry {@literal "type"}. 432 * 433 * @return Current {@literal "builder"} for an ADDE entry. 434 */ 435 public Builder type(EntryType entryType) { 436 this.entryType = entryType; 437 return this; 438 } 439 440 /** 441 * Optional {@literal "parameter"} for an ADDE entry. Allows you to 442 * set the {@link RemoteAddeEntry#entryValidity}. If this method is 443 * not called, {@code entryValidity} will default to 444 * {@link EntryValidity#UNVERIFIED}. 445 * 446 * @param entryValidity ADDE entry {@literal "validity"}. 447 * 448 * @return Current {@literal "builder"} for an ADDE entry. 449 */ 450 public Builder validity(EntryValidity entryValidity) { 451 this.entryValidity = entryValidity; 452 return this; 453 } 454 455 /** 456 * Optional {@literal "parameter"} for an ADDE entry. Allows you to 457 * set the {@link RemoteAddeEntry#entrySource}. If this method is not 458 * called, {@code entrySource} will default to 459 * {@link EntrySource#SYSTEM}. 460 * 461 * @param entrySource ADDE entry {@literal "source"}. 462 * 463 * @return Current {@literal "builder"} for an ADDE entry. 464 */ 465 public Builder source(EntrySource entrySource) { 466 this.entrySource = entrySource; 467 return this; 468 } 469 470 /** 471 * Optional {@literal "parameter"} for an ADDE entry. Allows you to 472 * set the {@link RemoteAddeEntry#entryStatus}. If this method is not 473 * called, {@code entryStatus} will default to 474 * {@link EntryStatus#ENABLED}. 475 * 476 * @param entryStatus ADDE entry {@literal "status"}. 477 * 478 * @return Current {@literal "builder"} for an ADDE entry. 479 */ 480 public Builder status(EntryStatus entryStatus) { 481 this.entryStatus = entryStatus; 482 return this; 483 } 484 485 /** 486 * Convenient way to generate a new, invalid entry. 487 * 488 * @return Current {@literal "builder"} for an ADDE entry. 489 */ 490 public Builder invalidate() { 491 this.entryType = EntryType.INVALID; 492 this.entryValidity = EntryValidity.INVALID; 493 this.entrySource = EntrySource.INVALID; 494 this.entryStatus = EntryStatus.INVALID; 495 return this; 496 } 497 498 /** 499 * Optionally control whether or not the resulting entry is 500 * {@literal "temporary"}. 501 * 502 * @param temporary Whether or not the entry is temporary. 503 * 504 * @return Current {@literal "builder"} for an ADDE entry. 505 */ 506 public Builder temporary(boolean temporary) { 507 this.temporary = temporary; 508 return this; 509 } 510 511 /** 512 * Optionally sets the {@literal "alias"} that can be used to refer to 513 * the resulting entry. 514 * 515 * @param alias Alias for the resulting entry. 516 * 517 * @return Current {@literal "builder"} for an ADDE entry. 518 */ 519 public Builder alias(final String alias) { 520 this.alias = alias; 521 return this; 522 } 523 524 /** 525 * Creates an entry based upon the values supplied to the other 526 * methods. 527 * 528 * @return A newly created {@code RemoteAddeEntry}. 529 */ 530 public RemoteAddeEntry build() { 531 return new RemoteAddeEntry(this); 532 } 533 } 534 535 /** 536 * Tries to connect to a given {@code RemoteAddeEntry} and read the list 537 * of ADDE {@literal "groups"} available to the public. 538 * 539 * @param entry The {@code RemoteAddeEntry} to query. Cannot be {@code null}. 540 * 541 * @return {@link Set} of public groups on {@code entry}. 542 * 543 * @throws NullPointerException if {@code entry} is {@code null}. 544 * @throws IllegalArgumentException if the server address is an empty 545 * {@link String}. 546 */ 547 public static Set<String> readPublicGroups(final RemoteAddeEntry entry) { 548 requireNonNull(entry, "entry cannot be null"); 549 requireNonNull(entry.getAddress()); 550 checkArg(!entry.getAddress().isEmpty()); 551 552 String user = entry.getAccount().getUsername(); 553 if ((user == null) || user.isEmpty()) { 554 user = RemoteAddeEntry.DEFAULT_ACCOUNT.getUsername(); 555 } 556 557 String proj = entry.getAccount().getProject(); 558 if ((proj == null) || proj.isEmpty()) { 559 proj = RemoteAddeEntry.DEFAULT_ACCOUNT.getProject(); 560 } 561 562 boolean debugUrl = EntryStore.isAddeDebugEnabled(false); 563 String url = String.format(publicSrvFormat, entry.getAddress(), debugUrl, user, proj); 564 565 Set<String> groups = newLinkedHashSet(); 566 567 AddeTextReader reader = new AddeTextReader(url); 568 if ("OK".equals(reader.getStatus())) { 569 for (String line : (List<String>)reader.getLinesOfText()) { 570 String[] pairs = line.trim().split(","); 571 for (String pair : pairs) { 572 if ((pair == null) || pair.isEmpty() || !pair.startsWith("N1")) { 573 continue; 574 } 575 String[] keyval = pair.split("="); 576 if ((keyval.length != 2) || keyval[0].isEmpty() || keyval[1].isEmpty() || !keyval[0].equals("N1")) { 577 continue; 578 } 579 groups.add(keyval[1]); 580 } 581 } 582 } 583 return groups; 584 } 585 586 /** 587 * Determines whether or not the server specified in {@code entry} is 588 * listening on port 112. 589 * 590 * @param entry Descriptor containing the server to check. 591 * 592 * @return {@code true} if a connection was opened, {@code false} otherwise. 593 * 594 * @throws NullPointerException if {@code entry} is null. 595 */ 596 public static boolean checkHost(final RemoteAddeEntry entry) { 597 requireNonNull(entry, "entry cannot be null"); 598 String host = entry.getAddress(); 599 boolean connected; 600 if (host.startsWith("localhost:")) { 601 connected = true; 602 } else { 603 Socket socket = new Socket(); 604 try { 605 socket.connect(new InetSocketAddress(host, ADDE_PORT), 10); 606 connected = true; 607 socket.close(); 608 } catch (UnknownHostException e) { 609 logger.debug("can't resolve IP for '{}'", host); 610 connected = false; 611 } catch (IOException e) { 612 logger.debug("IO problem while connecting to '{}': {}", entry.getAddress(), e.getMessage()); 613 connected = false; 614 } 615 } 616 logger.trace("host={} result={}", entry.getAddress(), connected); 617 return connected; 618 } 619 620 /** 621 * Attempts to verify whether or not the information in a given 622 * RemoteAddeEntry represents a valid remote ADDE server. If not, the 623 * method tries to determine which parts of the entry are invalid. 624 * 625 * <p>Note that this method uses {@code checkHost(RemoteAddeEntry)} to 626 * verify that the server is listening. To forego the check, simply call 627 * {@code checkEntry(false, entry)}. 628 * 629 * @param entry {@code RemoteAddeEntry} to check. Cannot be 630 * {@code null}. 631 * 632 * @return The {@link AddeStatus} that represents the verification status 633 * of {@code entry}. 634 * 635 * @see #checkHost(RemoteAddeEntry) 636 * @see #checkEntry(boolean, RemoteAddeEntry) 637 */ 638 public static AddeStatus checkEntry(final RemoteAddeEntry entry) { 639 return checkEntry(true, entry); 640 } 641 642 /** 643 * Attempts to verify whether or not the information in a given 644 * RemoteAddeEntry represents a valid remote ADDE server. If not, the 645 * method tries to determine which parts of the entry are invalid. 646 * 647 * @param checkHost {@code true} tries to connect to the remote ADDE server 648 * before doing anything else. 649 * @param entry {@code RemoteAddeEntry} to check. Cannot be 650 * {@code null}. 651 * 652 * @return The {@link AddeStatus} that represents the verification status 653 * of {@code entry}. 654 * 655 * @throws NullPointerException if {@code entry} is {@code null}. 656 * 657 * @see AddeStatus 658 */ 659 public static AddeStatus checkEntry(final boolean checkHost, final RemoteAddeEntry entry) { 660 requireNonNull(entry, "Cannot check a null entry"); 661 662 if (checkHost && !checkHost(entry)) { 663 return AddeStatus.BAD_SERVER; 664 } 665 666 String server = entry.getAddress(); 667 String type = entry.getEntryType().toString(); 668 String username = entry.getAccount().getUsername(); 669 String project = entry.getAccount().getProject(); 670 String[] servers = { server }; 671 AddeServerInfo serverInfo = new AddeServerInfo(servers); 672 673 // I just want to go on the record here: 674 // AddeServerInfo#setUserIDAndProjString(String) was not a good API 675 // decision. 676 serverInfo.setUserIDandProjString("user="+username+"&proj="+project); 677 int status = serverInfo.setSelectedServer(server, type); 678 if (status == -2) { 679 return AddeStatus.NO_METADATA; 680 } 681 if (status == -1) { 682 return AddeStatus.BAD_ACCOUNTING; 683 } 684 685 serverInfo.setSelectedGroup(entry.getGroup()); 686 String[] datasets = serverInfo.getDatasetList(); 687 if ((datasets != null) && (datasets.length > 0)) { 688 // TJJ 7 Nov 2013, not my proudest moment. See Inq #905 689 // if type is NEXR, this is a Radar server, not Image 690 String ff = serverInfo.getFileFormat(); 691 if ("NEXR".equals(ff)) { 692 entry.entryType = AddeEntry.EntryType.RADAR; 693 } 694 return AddeStatus.OK; 695 } 696 // TJJ - see Inq 1975, needed to add this hack because it seems 697 // imagery always technically validates as radar. 698 else if (!"RADAR".equals(type)) { 699 // try dsinfo 700 String addeUrl = "adde://"+server+"/datasetinfo?group="+entry.getGroup()+"&type="+type+"&user="+username+"&proj="+project+"&compress=gzip&port=112&debug=true&version=1"; 701 logger.trace("dsinfo url: '{}'", addeUrl); 702 try { 703 DataSetInfo dsinfo = new DataSetInfo(addeUrl); 704 Map<?, ?> descriptionTable = dsinfo.getDescriptionTable(); 705 if ((descriptionTable != null) && !descriptionTable.isEmpty()) { 706 return AddeStatus.OK; 707 } 708 } catch (AddeURLException e) { 709 logger.trace("dsinfo failed for url: '{}'", addeUrl); 710 } 711 return AddeStatus.BAD_GROUP; 712 } 713 // at this point can only be a bad group 714 else { 715 return AddeStatus.BAD_GROUP; 716 } 717 } 718 719 /** 720 * Determine the types of ADDE data within the given {@code group} on 721 * {@code host}. This method uses the {@literal "default"} ADDE user name 722 * and project number. 723 * 724 * <p>Note: <b>parameters cannot be {@code null}.</b></p> 725 * 726 * @param host Host to check. 727 * @param group ADDE group. 728 * 729 * @return {@link EnumMap} that maps ADDE data type to whether or not it 730 * is available for the given {@code host} and {@code group}. 731 */ 732 public static Map<EntryType, AddeStatus> checkEntryTypes(final String host, final String group) { 733 return checkEntryTypes(host, group, AddeEntry.DEFAULT_ACCOUNT.getUsername(), AddeEntry.DEFAULT_ACCOUNT.getProject()); 734 } 735 736 /** 737 * Determine the types of ADDE data within the given {@code group} on 738 * {@code host}. 739 * 740 * <p>Note: <b>parameters cannot be {@code null}.</b></p> 741 * 742 * @param host Host to check. 743 * @param group ADDE group. 744 * @param user ADDE user name. 745 * @param proj ADDE project number. 746 * 747 * @return {@link EnumMap} that maps ADDE data type to whether or not it 748 * is available for the given set of parameters. 749 * 750 * @see #checkEntry(boolean, RemoteAddeEntry) 751 */ 752 public static Map<EntryType, AddeStatus> checkEntryTypes(final String host, final String group, final String user, final String proj) { 753 // current type count is six. doubling it to be safe. 754 Map<EntryType, AddeStatus> valid = new EnumMap<>(EntryType.class); 755 RemoteAddeEntry entry = new Builder(host, group).account(user, proj).build(); 756 for (RemoteAddeEntry tmp : EntryTransforms.createEntriesFrom(entry)) { 757 valid.put(tmp.entryType, checkEntry(true, tmp)); 758 } 759 return valid; 760 } 761 762 /** 763 * Attempts to determine the {@literal "public"} ADDE groups available on 764 * the given {@code host}. 765 * 766 * <p>Note: this method uses the {@literal "default"} ADDE user name and 767 * project number.</p> 768 * 769 * 770 * @param host Host from which public groups are to be read. Cannot be {@code null}. 771 * 772 * @return {@link Set} of the public groups on {@code host}. The 773 * {@code Set} will be empty if there are no groups. 774 */ 775 public static Set<String> readPublicGroups(final String host) { 776 return readGroups(host, AddeEntry.DEFAULT_ACCOUNT.getUsername(), AddeEntry.DEFAULT_ACCOUNT.getProject()); 777 } 778 779 /** 780 * Attempts to determine which (if any) ADDE groups are available on the 781 * given {@code host}. 782 * 783 * <p>Note: <b>parameters cannot be {@code null}.</b></p> 784 * 785 * @param host Host from which public groups are to be read. 786 * @param user ADDE user name. 787 * @param proj ADDE project number. 788 * 789 * @return {@link Set} of the groups on {@code host}. The {@code Set} will 790 * be empty if there are no groups. 791 */ 792 public static Set<String> readGroups(final String host, final String user, final String proj) { 793 RemoteAddeEntry entry = new Builder(host, "").account(user, proj).build(); 794 return readPublicGroups(entry); 795 } 796}