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