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