001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- 002 * 003 * Copyright © 2017-2018 microBean. 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 014 * implied. See the License for the specific language governing 015 * permissions and limitations under the License. 016 */ 017package org.microbean.configuration.api; 018 019import java.lang.reflect.Type; 020 021import java.util.Collection; 022import java.util.Collections; // for javadoc only 023import java.util.Iterator; 024import java.util.Map; 025import java.util.ServiceConfigurationError; 026import java.util.ServiceLoader; 027import java.util.Set; 028 029import java.util.logging.Level; 030import java.util.logging.Logger; 031 032/** 033 * A single source for configuration values suitable for an 034 * application. 035 * 036 * @author <a href="https://about.me/lairdnelson" 037 * target="_parent">Laird Nelson</a> 038 * 039 * @see #getValue(Map, String, Type, String) 040 */ 041public abstract class Configurations { 042 043 044 /* 045 * Static fields. 046 */ 047 048 049 private static volatile ServiceLoader<Configurations> configurationsLoader; 050 051 052 /* 053 * Instance fields. 054 */ 055 056 057 /** 058 * A {@link Logger} for this {@link Configurations}. 059 * 060 * <p>This field is never {@code null}.</p> 061 * 062 * @see #createLogger() 063 */ 064 protected final Logger logger; 065 066 067 /* 068 * Constructors. 069 */ 070 071 072 /** 073 * Creates a new {@link Configurations}. 074 * 075 * @exception IllegalStateException if the {@link #createLogger()} 076 * method returns {@code null} 077 * 078 * @see #createLogger() 079 */ 080 protected Configurations() { 081 super(); 082 this.logger = this.createLogger(); 083 if (this.logger == null) { 084 throw new IllegalStateException("createLogger() == null"); 085 } 086 } 087 088 089 /* 090 * Instance methods. 091 */ 092 093 094 /** 095 * Returns a {@link Logger} for use by this {@link Configurations} 096 * implementation. 097 * 098 * <p>This method never returns {@code null}.</p> 099 * 100 * <p>Overrides of this method must not return {@code null}.</p> 101 * 102 * @return a non-{@code null} {@link Logger} 103 */ 104 protected Logger createLogger() { 105 return Logger.getLogger(this.getClass().getName()); 106 } 107 108 /** 109 * Returns a non-{@code null}, {@linkplain 110 * Collections#unmodifiableSet(Set) immutable} {@link Set} of {@link 111 * Type}s representing all the types to which {@link String}-typed 112 * configuration values may be converted by this {@link 113 * Configurations} object. 114 * 115 * <p>This method never returns {@code null}.</p> 116 * 117 * @return a non-{@code null}, {@linkplain 118 * Collections#unmodifiableSet(Set) immutable} {@link Set} of {@link 119 * Type}s 120 * 121 * @see TypeLiteral 122 */ 123 public abstract Set<Type> getConversionTypes(); 124 125 /** 126 * Returns a {@link Map} of <em>configuration 127 * coordinates</em>—aspects and their values that define a 128 * location within which requests for configuration values may take 129 * place. 130 * 131 * <p>Implementations of this method may return {@code null}.</p> 132 * 133 * @return a {@link Map} of configuration coordinates; may be {@code 134 * null} 135 */ 136 public abstract Map<String, String> getConfigurationCoordinates(); 137 138 /** 139 * Returns a configuration value corresponding to the configuration 140 * property with the supplied {@code name}. 141 * 142 * <p>This method may return {@code null}.</p> 143 * 144 * @param name the name of the configuration property for which a 145 * value will be returned; must not be {@code null} 146 * 147 * @return the configuration value, or {@code null} 148 * 149 * @exception NullPointerException if {@code name} is {@code null} 150 * 151 * @exception AmbiguousConfigurationValuesException if two or more 152 * values were found that could be suitable 153 * 154 * @see #getValue(Map, String, Type, String) 155 */ 156 public final String getValue(final String name) { 157 return this.getValue(this.getConfigurationCoordinates(), name, String.class, null); 158 } 159 160 /** 161 * Returns a configuration value corresponding to the configuration 162 * property with the supplied {@code name}, or the supplied {@code 163 * defaultValue} if otherwise {@code null} would be returned. 164 * 165 * <p>This method may return {@code null}.</p> 166 * 167 * @param name the name of the configuration property for which a 168 * value will be returned; must not be {@code null} 169 * 170 * @param defaultValue the value to return if otherwise {@code null} 171 * would be returned; may be {@code null} 172 * 173 * @return the configuration value, or {@code null} 174 * 175 * @exception NullPointerException if {@code name} is {@code null} 176 * 177 * @exception AmbiguousConfigurationValuesException if two or more 178 * values were found that could be suitable 179 * 180 * @see #getValue(Map, String, Type, String) 181 */ 182 public final String getValue(final String name, final String defaultValue) { 183 return this.getValue(this.getConfigurationCoordinates(), name, String.class, defaultValue); 184 } 185 186 /** 187 * Returns a configuration value corresponding to the configuration 188 * property suitable for the supplied {@code 189 * configurationCoordinates} and {@code name}. 190 * 191 * <p>This method may return {@code null}.</p> 192 * 193 * @param configurationCoordinates a {@link Map} representing the 194 * configuration coordinates in effect for this request; may be 195 * {@code null} 196 * 197 * @param name the name of the configuration property for which a 198 * value will be returned; must not be {@code null} 199 * 200 * @return the configuration value, or {@code null} 201 * 202 * @exception NullPointerException if {@code name} is {@code null} 203 * 204 * @exception AmbiguousConfigurationValuesException if two or more 205 * values were found that could be suitable 206 * 207 * @see #getValue(Map, String, Type, String) 208 */ 209 public final String getValue(final Map<String, String> configurationCoordinates, final String name) { 210 return this.getValue(configurationCoordinates, name, String.class, null); 211 } 212 213 /** 214 * Returns a configuration value corresponding to the configuration 215 * property suitable for the supplied {@code 216 * configurationCoordinates} and {@code name}, or the supplied 217 * {@code defaultValue} if otherwise {@code null} would be returned. 218 * 219 * <p>This method may return {@code null}.</p> 220 * 221 * @param configurationCoordinates a {@link Map} representing the 222 * configuration coordinates in effect for this request; may be 223 * {@code null} 224 * 225 * @param name the name of the configuration property for which a 226 * value will be returned; must not be {@code null} 227 * 228 * @param defaultValue the value to return if otherwise {@code null} 229 * would be returned; may be {@code null} 230 * 231 * @return the configuration value, or {@code null} 232 * 233 * @exception NullPointerException if {@code name} is {@code null} 234 * 235 * @exception AmbiguousConfigurationValuesException if two or more 236 * values were found that could be suitable 237 * 238 * @see #getValue(Map, String, Type, String) 239 */ 240 public final String getValue(final Map<String, String> configurationCoordinates, final String name, final String defaultValue) { 241 return this.getValue(configurationCoordinates, name, String.class, defaultValue); 242 } 243 244 /** 245 * Returns a configuration value corresponding to the configuration 246 * property suitable for the supplied {@code name}, converted, if 247 * possible, to the supplied {@code type}. 248 * 249 * <p>This method may return {@code null}.</p> 250 * 251 * @param <T> the type to which a {@link String}-typed configuration 252 * value should be converted 253 * 254 * @param name the name of the configuration property for which a 255 * value will be returned; must not be {@code null} 256 * 257 * @param type a {@link Class} representing the type to which the 258 * configuration value will be converted; must not be {@code null} 259 * 260 * @return the configuration value, or {@code null} 261 * 262 * @exception NullPointerException if {@code name} or {@code type} 263 * is {@code null} 264 * 265 * @exception ConversionException if type conversion could not occur 266 * for any reason 267 * 268 * @exception AmbiguousConfigurationValuesException if two or more 269 * values were found that could be suitable 270 * 271 * @see #getValue(Map, String, Type, String) 272 */ 273 public final <T> T getValue(final String name, final Class<T> type) { 274 return this.getValue(this.getConfigurationCoordinates(), name, type, null); 275 } 276 277 /** 278 * Returns a configuration value corresponding to the configuration 279 * property suitable for the supplied {@code name}, converted, if 280 * possible, to the supplied {@code type}. 281 * 282 * <p>This method may return {@code null}.</p> 283 * 284 * @param <T> the type to which a {@link String}-typed configuration 285 * value should be converted 286 * 287 * @param name the name of the configuration property for which a 288 * value will be returned; must not be {@code null} 289 * 290 * @param type a {@link Class} representing the type to which the 291 * configuration value will be converted; must not be {@code null} 292 * 293 * @param defaultValue the value that will be converted and returned 294 * if {@code null} would otherwise be returned; may be {@code null} 295 * 296 * @return the configuration value, or {@code null} 297 * 298 * @exception NullPointerException if {@code name} or {@code type} 299 * is {@code null} 300 * 301 * @exception ConversionException if type conversion could not occur 302 * for any reason 303 * 304 * @exception AmbiguousConfigurationValuesException if two or more 305 * values were found that could be suitable 306 * 307 * @see #getValue(Map, String, Type, String) 308 */ 309 public final <T> T getValue(final String name, final Class<T> type, final String defaultValue) { 310 return this.getValue(this.getConfigurationCoordinates(), name, type, defaultValue); 311 } 312 313 /** 314 * Returns a configuration value corresponding to the configuration 315 * property suitable for the supplied {@code 316 * configurationCoordinates} and {@code name}, converted, if 317 * possible, to the supplied {@code type}. 318 * 319 * <p>This method may return {@code null}.</p> 320 * 321 * @param <T> the type to which a {@link String}-typed configuration 322 * value should be converted 323 * 324 * @param configurationCoordinates a {@link Map} representing the 325 * configuration coordinates in effect for this request; may be 326 * {@code null} 327 * 328 * @param name the name of the configuration property for which a 329 * value will be returned; must not be {@code null} 330 * 331 * @param type a {@link Class} representing the type to which the 332 * configuration value will be converted; must not be {@code null} 333 * 334 * @return the configuration value, or {@code null} 335 * 336 * @exception NullPointerException if {@code name} or {@code type} 337 * is {@code null} 338 * 339 * @exception ConversionException if type conversion could not occur 340 * for any reason 341 * 342 * @exception AmbiguousConfigurationValuesException if two or more 343 * values were found that could be suitable 344 * 345 * @see #getValue(Map, String, Type, String) 346 */ 347 public final <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final Class<T> type) { 348 return this.getValue(configurationCoordinates, name, (Type)type, null); 349 } 350 351 /** 352 * Returns a configuration value corresponding to the configuration 353 * property suitable for the supplied {@code 354 * configurationCoordinates} and {@code name}, or the supplied 355 * {@code defaultValue} if {@code null} would otherwise be returned, 356 * converted, if possible, to the supplied {@code type}. 357 * 358 * <p>This method may return {@code null}.</p> 359 * 360 * @param <T> the type to which a {@link String}-typed configuration 361 * value should be converted 362 * 363 * @param configurationCoordinates a {@link Map} representing the 364 * configuration coordinates in effect for this request; may be 365 * {@code null} 366 * 367 * @param name the name of the configuration property for which a 368 * value will be returned; must not be {@code null} 369 * 370 * @param type a {@link Class} representing the type to which the 371 * configuration value will be converted; must not be {@code null} 372 * 373 * @param defaultValue the value that will be converted and returned 374 * if {@code null} would otherwise be returned; may be {@code null} 375 * 376 * @return the configuration value, or {@code null} 377 * 378 * @exception NullPointerException if {@code name} or {@code type} 379 * is {@code null} 380 * 381 * @exception ConversionException if type conversion could not occur 382 * for any reason 383 * 384 * @exception AmbiguousConfigurationValuesException if two or more 385 * values were found that could be suitable 386 * 387 * @see #getValue(Map, String, Type, String) 388 */ 389 public final <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final Class<T> type, final String defaultValue) { 390 return this.getValue(configurationCoordinates, name, (Type)type, defaultValue); 391 } 392 393 /** 394 * Returns a configuration value corresponding to the configuration 395 * property suitable for the supplied {@code name}, converted, if 396 * possible, to the type represented by the supplied {@code 397 * typeLiteral}. 398 * 399 * <p>This method may return {@code null}.</p> 400 * 401 * @param <T> the type to which a {@link String}-typed configuration 402 * value should be converted 403 * 404 * @param name the name of the configuration property for which a 405 * value will be returned; must not be {@code null} 406 * 407 * @param typeLiteral a {@link TypeLiteral} representing the type to 408 * which the configuration value will be converted; must not be 409 * {@code null} 410 * 411 * @return the configuration value, or {@code null} 412 * 413 * @exception NullPointerException if {@code name} or {@code type} 414 * is {@code null} 415 * 416 * @exception ConversionException if type conversion could not occur 417 * for any reason 418 * 419 * @exception AmbiguousConfigurationValuesException if two or more 420 * values were found that could be suitable 421 * 422 * @see #getValue(Map, String, Type, String) 423 */ 424 public final <T> T getValue(final String name, final TypeLiteral<T> typeLiteral) { 425 return this.getValue(this.getConfigurationCoordinates(), name, typeLiteral, null); 426 } 427 428 /** 429 * Returns a configuration value corresponding to the configuration 430 * property suitable for the supplied {@code name}, or the supplied 431 * {@code defaultValue} if {@code null} would otherwise be returned, 432 * converted, if possible, to the type represented by the supplied 433 * {@code typeLiteral}. 434 * 435 * <p>This method may return {@code null}.</p> 436 * 437 * @param <T> the type to which a {@link String}-typed configuration 438 * value should be converted 439 * 440 * @param name the name of the configuration property for which a 441 * value will be returned; must not be {@code null} 442 * 443 * @param typeLiteral a {@link TypeLiteral} representing the type to 444 * which the configuration value will be converted; must not be 445 * {@code null} 446 * 447 * @param defaultValue the value that will be converted and returned 448 * if {@code null} would otherwise be returned; may be {@code null} 449 * 450 * @return the configuration value, or {@code null} 451 * 452 * @exception NullPointerException if {@code name} or {@code type} 453 * is {@code null} 454 * 455 * @exception ConversionException if type conversion could not occur 456 * for any reason 457 * 458 * @exception AmbiguousConfigurationValuesException if two or more 459 * values were found that could be suitable 460 * 461 * @see #getValue(Map, String, Type, String) 462 */ 463 public final <T> T getValue(final String name, final TypeLiteral<T> typeLiteral, final String defaultValue) { 464 return this.getValue(this.getConfigurationCoordinates(), name, typeLiteral, defaultValue); 465 } 466 467 /** 468 * Returns a configuration value corresponding to the configuration 469 * property suitable for the supplied {@code 470 * configurationCoordinates} and {@code name}, converted, if 471 * possible, to the type represented by the supplied {@code 472 * typeLiteral}. 473 * 474 * <p>This method may return {@code null}.</p> 475 * 476 * @param <T> the type to which a {@link String}-typed configuration 477 * value should be converted 478 * 479 * @param configurationCoordinates a {@link Map} representing the 480 * configuration coordinates in effect for this request; may be 481 * {@code null} 482 * 483 * @param name the name of the configuration property for which a 484 * value will be returned; must not be {@code null} 485 * 486 * @param typeLiteral a {@link TypeLiteral} representing the type to 487 * which the configuration value will be converted; must not be 488 * {@code null} 489 * 490 * @return the configuration value, or {@code null} 491 * 492 * @exception NullPointerException if {@code name} or {@code type} 493 * is {@code null} 494 * 495 * @exception ConversionException if type conversion could not occur 496 * for any reason 497 * 498 * @exception AmbiguousConfigurationValuesException if two or more 499 * values were found that could be suitable 500 * 501 * @see #getValue(Map, String, Type, String) 502 */ 503 public final <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final TypeLiteral<T> typeLiteral) { 504 return this.getValue(configurationCoordinates, name, typeLiteral == null ? (Type)null : typeLiteral.getType(), null); 505 } 506 507 /** 508 * Returns a configuration value corresponding to the configuration 509 * property suitable for the supplied {@code 510 * configurationCoordinates} and {@code name}, converted, if 511 * possible, to the type represented by the supplied {@code 512 * typeLiteral}. 513 * 514 * <p>This method may return {@code null}.</p> 515 * 516 * @param <T> the type to which a {@link String}-typed configuration 517 * value should be converted 518 * 519 * @param configurationCoordinates a {@link Map} representing the 520 * configuration coordinates in effect for this request; may be 521 * {@code null} 522 * 523 * @param name the name of the configuration property for which a 524 * value will be returned; must not be {@code null} 525 * 526 * @param typeLiteral a {@link TypeLiteral} representing the type to 527 * which the configuration value will be converted; must not be 528 * {@code null} 529 * 530 * @param defaultValue the value that will be converted and returned 531 * if {@code null} would otherwise be returned; may be {@code null} 532 * 533 * @return the configuration value, or {@code null} 534 * 535 * @exception NullPointerException if {@code name} or {@code type} 536 * is {@code null} 537 * 538 * @exception ConversionException if type conversion could not occur 539 * for any reason 540 * 541 * @exception AmbiguousConfigurationValuesException if two or more 542 * values were found that could be suitable 543 * 544 * @see #getValue(Map, String, Type, String) 545 */ 546 public final <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final TypeLiteral<T> typeLiteral, final String defaultValue) { 547 return this.getValue(configurationCoordinates, name, typeLiteral == null ? (Type)null : typeLiteral.getType(), defaultValue); 548 } 549 550 /** 551 * Returns a configuration value corresponding to the configuration 552 * property suitable for the supplied {@code name}, converted, if 553 * possible, to the type represented by the supplied {@code type}. 554 * 555 * <p>This method may return {@code null}.</p> 556 * 557 * @param <T> the type to which a {@link String}-typed configuration 558 * value should be converted 559 * 560 * @param name the name of the configuration property for which a 561 * value will be returned; must not be {@code null} 562 * 563 * @param type a {@link Type} representing the type to 564 * which the configuration value will be converted; must not be {@code null} 565 * 566 * @return the configuration value, or {@code null} 567 * 568 * @exception NullPointerException if {@code name} or {@code type} 569 * is {@code null} 570 571 * 572 * @exception ConversionException if type conversion could not occur 573 * for any reason 574 * 575 * @exception AmbiguousConfigurationValuesException if two or more 576 * values were found that could be suitable 577 * 578 * @see #getValue(Map, String, Type, String) 579 */ 580 public final <T> T getValue(final String name, final Type type) { 581 return this.getValue(this.getConfigurationCoordinates(), name, type, null); 582 } 583 584 /** 585 * Returns a configuration value corresponding to the configuration 586 * property suitable for the supplied {@code name}, or the supplied 587 * {@code defaultValue} if otherwise {@code null} would be returned, 588 * converted, if possible, to the type represented by the supplied 589 * {@code type}. 590 * 591 * <p>This method may return {@code null}.</p> 592 * 593 * @param <T> the type to which a {@link String}-typed configuration 594 * value should be converted 595 * 596 * @param name the name of the configuration property for which a 597 * value will be returned; must not be {@code null} 598 * 599 * @param type a {@link Type} representing the type to 600 * which the configuration value will be converted; must not be {@code null} 601 * 602 * @param defaultValue the value that will be converted and returned 603 * if {@code null} would otherwise be returned; may be {@code null} 604 * 605 * @return the configuration value, or {@code null} 606 * 607 * @exception NullPointerException if {@code name} or {@code type} 608 * is {@code null} 609 610 * 611 * @exception ConversionException if type conversion could not occur 612 * for any reason 613 * 614 * @exception AmbiguousConfigurationValuesException if two or more 615 * values were found that could be suitable 616 * 617 * @see #getValue(Map, String, Type, String) 618 */ 619 public final <T> T getValue(final String name, final Type type, final String defaultValue) { 620 return this.getValue(this.getConfigurationCoordinates(), name, type, defaultValue); 621 } 622 623 /** 624 * Returns a configuration value corresponding to the configuration 625 * property suitable for the supplied {@code 626 * configurationCoordinates} and {@code name}, converted, if 627 * possible, to the type represented by the supplied {@code type}. 628 * 629 * <p>This method may return {@code null}.</p> 630 * 631 * @param <T> the type to which a {@link String}-typed configuration 632 * value should be converted 633 * 634 * @param configurationCoordinates a {@link Map} representing the 635 * configuration coordinates in effect for this request; may be 636 * {@code null} 637 * 638 * @param name the name of the configuration property for which a 639 * value will be returned; must not be {@code null} 640 * 641 * @param type a {@link Type} representing the type to 642 * which the configuration value will be converted; must not be {@code null} 643 * 644 * @return the configuration value, or {@code null} 645 * 646 * @exception NullPointerException if {@code name} or {@code type} 647 * is {@code null} 648 649 * 650 * @exception ConversionException if type conversion could not occur 651 * for any reason 652 * 653 * @exception AmbiguousConfigurationValuesException if two or more 654 * values were found that could be suitable 655 * 656 * @see #getValue(Map, String, Type, String) 657 */ 658 public final <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final Type type) { 659 return this.getValue(configurationCoordinates, name, type, null); 660 } 661 662 /** 663 * Returns a configuration value corresponding to the configuration 664 * property suitable for the supplied {@code 665 * configurationCoordinates} and {@code name}, or the supplied 666 * {@code defaultValue} if {@code null} would otherwise be returned, 667 * converted, if possible, to the type represented by the supplied 668 * {@code type}. 669 * 670 * <p>This method may return {@code null}.</p> 671 * 672 * @param <T> the type to which a {@link String}-typed configuration 673 * value should be converted 674 * 675 * @param configurationCoordinates a {@link Map} representing the 676 * configuration coordinates in effect for this request; may be 677 * {@code null} 678 * 679 * @param name the name of the configuration property for which a 680 * value will be returned; must not be {@code null} 681 * 682 * @param type a {@link Type} representing the type to which the 683 * configuration value will be converted; must not be {@code null} 684 * 685 * @param defaultValue the value that will be converted and returned 686 * if {@code null} would otherwise be returned; may be {@code null} 687 * 688 * @return the configuration value, or {@code null} 689 * 690 * @exception NullPointerException if {@code name} or {@code type} 691 * is {@code null} 692 * 693 * @exception ConversionException if type conversion could not occur 694 * for any reason 695 * 696 * @exception AmbiguousConfigurationValuesException if two or more 697 * values were found that could be suitable 698 */ 699 public abstract <T> T getValue(final Map<String, String> configurationCoordinates, final String name, final Type type, final String defaultValue); 700 701 /** 702 * Returns a configuration value corresponding to the configuration 703 * property suitable for the supplied {@code 704 * configurationCoordinates} and {@code name}, or the supplied 705 * {@code defaultValue} if {@code null} would otherwise be returned. 706 * 707 * <p>This method may return {@code null}.</p> 708 * 709 * @param configurationCoordinates a {@link Map} representing the 710 * configuration coordinates in effect for this request; may be 711 * {@code null} 712 * 713 * @param names names of configuration properties for which a value 714 * will be returned; each element will be tried in turn; must not be 715 * {@code null} 716 * 717 * @param defaultValue the value that will be returned if {@code 718 * null} would otherwise be returned; may be {@code null} 719 * 720 * @return the configuration value, or {@code null} 721 * 722 * @exception NullPointerException if {@code name} or {@code type} 723 * is {@code null} 724 * 725 * @exception AmbiguousConfigurationValuesException if two or more 726 * values were found that could be suitable 727 */ 728 public final String getValue(final Map<String, String> configurationCoordinates, final Collection<String> names, final String defaultValue) { 729 return this.getValue(configurationCoordinates, names, String.class, defaultValue); 730 } 731 732 /** 733 * Returns a configuration value corresponding to the configuration 734 * property suitable for the supplied {@code 735 * configurationCoordinates} and {@code name}, or null. 736 * 737 * <p>This method may return {@code null}.</p> 738 * 739 * @param configurationCoordinates a {@link Map} representing the 740 * configuration coordinates in effect for this request; may be 741 * {@code null} 742 * 743 * @param names names of configuration properties for which a value 744 * will be returned; each element will be tried in turn; must not be 745 * {@code null} 746 * 747 * @return the configuration value, or {@code null} 748 * 749 * @exception NullPointerException if {@code names} or {@code type} 750 * is {@code null} 751 * 752 * @exception AmbiguousConfigurationValuesException if two or more 753 * values were found that could be suitable 754 */ 755 public final String getValue(final Map<String, String> configurationCoordinates, final Collection<String> names) { 756 return this.getValue(configurationCoordinates, names, String.class, null); 757 } 758 759 /** 760 * Returns a configuration value corresponding to the configuration 761 * property suitable for the supplied {@code names}, or null. 762 * 763 * <p>This method may return {@code null}.</p> 764 * 765 * @param names names of configuration properties for which a value 766 * will be returned; each element will be tried in turn; must not be 767 * {@code null} 768 * 769 * @return the configuration value, or {@code null} 770 * 771 * @exception NullPointerException if {@code names} is {@code null} 772 * 773 * @exception AmbiguousConfigurationValuesException if two or more 774 * values were found that could be suitable 775 */ 776 public final String getValue(final Collection<String> names) { 777 return this.getValue(this.getConfigurationCoordinates(), names, String.class, null); 778 } 779 780 /** 781 * Returns a configuration value corresponding to the configuration 782 * property suitable for the supplied {@code names}, or the supplied 783 * {@code defaultValue}. 784 * 785 * <p>This method may return {@code null}.</p> 786 * 787 * @param names names of configuration properties for which a value 788 * will be returned; each element will be tried in turn; must not be 789 * {@code null} 790 * 791 * @param defaultValue the value that will be returned if {@code 792 * null} would otherwise be returned; may be {@code null} 793 * 794 * @return the configuration value, or the supplied {@code 795 * defaultValue} 796 * 797 * @exception NullPointerException if {@code names} is {@code null} 798 * 799 * @exception AmbiguousConfigurationValuesException if two or more 800 * values were found that could be suitable 801 */ 802 public final String getValue(final Collection<String> names, final String defaultValue) { 803 return this.getValue(this.getConfigurationCoordinates(), names, String.class, defaultValue); 804 } 805 806 /** 807 * Returns a configuration value corresponding to the configuration 808 * property suitable for the supplied {@code names}, converted, if 809 * possible, to the type represented by the supplied {@code 810 * type}, or the supplied {@code defaultValue}. 811 * 812 * <p>This method may return {@code null}.</p> 813 * 814 * @param <T> the type to which a {@link String}-typed configuration 815 * value should be converted 816 * 817 * @param names names of configuration properties for which a value 818 * will be returned; each element will be tried in turn; must not be 819 * {@code null} 820 * 821 * @param type a {@link Class} representing the type to which the 822 * configuration value will be converted; must not be {@code null} 823 * 824 * @param defaultValue the value that will be converted and returned 825 * if {@code null} would otherwise be returned; may be {@code null} 826 * 827 * @return the configuration value, or the supplied {@code 828 * defaultValue} 829 * 830 * @exception NullPointerException if {@code names} or {@code type} 831 * is {@code null} 832 * 833 * @exception ConversionException if type conversion could not occur 834 * for any reason 835 * 836 * @exception AmbiguousConfigurationValuesException if two or more 837 * values were found that could be suitable 838 * 839 * @see #getValue(Map, Collection, Type, String) 840 */ 841 public final <T> T getValue(final Collection<String> names, final Class<T> type, final String defaultValue) { 842 return this.getValue(this.getConfigurationCoordinates(), names, type, defaultValue); 843 } 844 845 public final <T> T getValue(final Collection<String> names, final Type type, final String defaultValue) { 846 return this.getValue(this.getConfigurationCoordinates(), names, type, defaultValue); 847 } 848 849 public final <T> T getValue(final Collection<String> names, final TypeLiteral<T> typeLiteral, final String defaultValue) { 850 return this.getValue(this.getConfigurationCoordinates(), names, typeLiteral == null ? (Type)null : typeLiteral.getType(), defaultValue); 851 } 852 853 /** 854 * Returns a configuration value corresponding to the configuration 855 * property suitable for the supplied {@code names}, converted, if 856 * possible, to the type represented by the supplied {@code 857 * type}, or {@code null}. 858 * 859 * <p>This method may return {@code null}.</p> 860 * 861 * @param <T> the type to which a {@link String}-typed configuration 862 * value should be converted 863 * 864 * @param names names of configuration properties for which a value 865 * will be returned; each element will be tried in turn; must not be 866 * {@code null} 867 * 868 * @param type a {@link Class} representing the type to which the 869 * configuration value will be converted; must not be {@code null} 870 * 871 * @return the configuration value, or {@code null} 872 * 873 * @exception NullPointerException if {@code names} or {@code type} 874 * is {@code null} 875 * 876 * @exception ConversionException if type conversion could not occur 877 * for any reason 878 * 879 * @exception AmbiguousConfigurationValuesException if two or more 880 * values were found that could be suitable 881 * 882 * @see #getValue(Map, Collection, Type, String) 883 */ 884 public final <T> T getValue(final Collection<String> names, final Class<T> type) { 885 return this.getValue(this.getConfigurationCoordinates(), names, type, null); 886 } 887 888 /** 889 * Returns a configuration value corresponding to the configuration 890 * property suitable for the supplied {@code names}, converted, if 891 * possible, to the type represented by the supplied {@code 892 * type}, or {@code null}. 893 * 894 * <p>This method may return {@code null}.</p> 895 * 896 * @param <T> the type to which a {@link String}-typed configuration 897 * value should be converted 898 * 899 * @param names names of configuration properties for which a value 900 * will be returned; each element will be tried in turn; must not be 901 * {@code null} 902 * 903 * @param type a {@link Type} representing the type to which the 904 * configuration value will be converted; must not be {@code null} 905 * 906 * @return the configuration value, or {@code null} 907 * 908 * @exception NullPointerException if {@code names} or {@code type} 909 * is {@code null} 910 * 911 * @exception ConversionException if type conversion could not occur 912 * for any reason 913 * 914 * @exception AmbiguousConfigurationValuesException if two or more 915 * values were found that could be suitable 916 * 917 * @see #getValue(Map, Collection, Type, String) 918 */ 919 public final <T> T getValue(final Collection<String> names, final Type type) { 920 return this.getValue(this.getConfigurationCoordinates(), names, type, null); 921 } 922 923 /** 924 * Returns a configuration value corresponding to the configuration 925 * property suitable for the supplied {@code names}, converted, if 926 * possible, to the type represented by the supplied {@code 927 * typeLiteral}, or {@code null}. 928 * 929 * <p>This method may return {@code null}.</p> 930 * 931 * @param <T> the type to which a {@link String}-typed configuration 932 * value should be converted 933 * 934 * @param names names of configuration properties for which a value 935 * will be returned; each element will be tried in turn; must not be 936 * {@code null} 937 * 938 * @param typeLiteral a {@link TypeLiteral} representing the type to which the 939 * configuration value will be converted; must not be {@code null} 940 * 941 * @return the configuration value, or {@code null} 942 * 943 * @exception NullPointerException if {@code name} or {@code type} 944 * is {@code null} 945 * 946 * @exception ConversionException if type conversion could not occur 947 * for any reason 948 * 949 * @exception AmbiguousConfigurationValuesException if two or more 950 * values were found that could be suitable 951 * 952 * @see #getValue(Map, Collection, Type, String) 953 */ 954 public final <T> T getValue(final Collection<String> names, final TypeLiteral<T> typeLiteral) { 955 return this.getValue(this.getConfigurationCoordinates(), names, typeLiteral == null ? (Type)null : typeLiteral.getType(), null); 956 } 957 958 /** 959 * Returns a configuration value corresponding to the configuration 960 * property suitable for the supplied {@code 961 * configurationCoordinates} and {@code names}, or the supplied 962 * {@code defaultValue} if {@code null} would otherwise be returned, 963 * converted, if possible, to the type represented by the supplied 964 * {@code type}. 965 * 966 * <p>This method may return {@code null}.</p> 967 * 968 * @param <T> the type to which a {@link String}-typed configuration 969 * value should be converted 970 * 971 * @param configurationCoordinates a {@link Map} representing the 972 * configuration coordinates in effect for this request; may be 973 * {@code null} 974 * 975 * @param names names of configuration properties for which a value 976 * will be returned; each element will be tried in turn; must not be 977 * {@code null} 978 * 979 * @param type a {@link Class} representing the type to which the 980 * configuration value will be converted; must not be {@code null} 981 * 982 * @param defaultValue the value that will be converted and returned 983 * if {@code null} would otherwise be returned; may be {@code null} 984 * 985 * @return the configuration value, or {@code null} 986 * 987 * @exception NullPointerException if {@code name} or {@code type} 988 * is {@code null} 989 * 990 * @exception ConversionException if type conversion could not occur 991 * for any reason 992 * 993 * @exception AmbiguousConfigurationValuesException if two or more 994 * values were found that could be suitable 995 * 996 * @see #getValue(Map, Collection, Type, String) 997 */ 998 public final <T> T getValue(final Map<String, String> configurationCoordinates, final Collection<String> names, final Class<T> type, final String defaultValue) { 999 return this.getValue(configurationCoordinates, names, (Type)type, defaultValue); 1000 } 1001 1002 /** 1003 * Returns a configuration value corresponding to the configuration 1004 * property suitable for the supplied {@code 1005 * configurationCoordinates} and {@code names}, or the supplied 1006 * {@code defaultValue} if {@code null} would otherwise be returned, 1007 * converted, if possible, to the type represented by the supplied 1008 * {@code type}. 1009 * 1010 * <p>This method may return {@code null}.</p> 1011 * 1012 * @param <T> the type to which a {@link String}-typed configuration 1013 * value should be converted 1014 * 1015 * @param configurationCoordinates a {@link Map} representing the 1016 * configuration coordinates in effect for this request; may be 1017 * {@code null} 1018 * 1019 * @param names names of configuration properties for which a value 1020 * will be returned; each element will be tried in turn; must not be 1021 * {@code null} 1022 * 1023 * @param typeLiteral a {@link TypeLiteral} representing the type to which 1024 * the configuration value will be converted; must not be {@code 1025 * null} 1026 * 1027 * @param defaultValue the value that will be converted and returned 1028 * if {@code null} would otherwise be returned; may be {@code null} 1029 * 1030 * @return the configuration value, or {@code null} 1031 * 1032 * @exception NullPointerException if {@code name} or {@code type} 1033 * is {@code null} 1034 * 1035 * @exception ConversionException if type conversion could not occur 1036 * for any reason 1037 * 1038 * @exception AmbiguousConfigurationValuesException if two or more 1039 * values were found that could be suitable 1040 * 1041 * @see #getValue(Map, Collection, Type, String) 1042 */ 1043 public final <T> T getValue(final Map<String, String> configurationCoordinates, final Collection<String> names, final TypeLiteral<T> typeLiteral, final String defaultValue) { 1044 return this.getValue(configurationCoordinates, names, typeLiteral == null ? (Type)null : typeLiteral.getType(), defaultValue); 1045 } 1046 1047 /** 1048 * Returns a configuration value corresponding to the configuration 1049 * property suitable for the supplied {@code 1050 * configurationCoordinates} and {@code names}, or the supplied 1051 * {@code defaultValue} if {@code null} would otherwise be returned, 1052 * converted, if possible, to the type represented by the supplied 1053 * {@code type}. 1054 * 1055 * <p>This method may return {@code null}.</p> 1056 * 1057 * @param <T> the type to which a {@link String}-typed configuration 1058 * value should be converted 1059 * 1060 * @param configurationCoordinates a {@link Map} representing the 1061 * configuration coordinates in effect for this request; may be 1062 * {@code null} 1063 * 1064 * @param names names of configuration properties for which a value 1065 * will be returned; each element will be tried in turn; must not be 1066 * {@code null} 1067 * 1068 * @param type a {@link Type} representing the type to which the 1069 * configuration value will be converted; must not be {@code null} 1070 * 1071 * @param defaultValue the value that will be converted and returned 1072 * if {@code null} would otherwise be returned; may be {@code null} 1073 * 1074 * @return the configuration value, or {@code null} 1075 * 1076 * @exception NullPointerException if {@code name} or {@code type} 1077 * is {@code null} 1078 * 1079 * @exception ConversionException if type conversion could not occur 1080 * for any reason 1081 * 1082 * @exception AmbiguousConfigurationValuesException if two or more 1083 * values were found that could be suitable 1084 * 1085 * @see #getValue(Map, String, Type, String) 1086 */ 1087 public final <T> T getValue(final Map<String, String> configurationCoordinates, final Collection<String> names, final Type type, final String defaultValue) { 1088 final String cn = this.getClass().getName(); 1089 final Logger logger = Logger.getLogger(cn); 1090 final String mn = "getValue"; 1091 if (logger.isLoggable(Level.FINER)) { 1092 logger.entering(cn, mn, new Object[] { configurationCoordinates, names, type, defaultValue }); 1093 } 1094 1095 T returnValue = null; 1096 if (names == null || names.isEmpty()) { 1097 returnValue = this.getValue(configurationCoordinates, (String)null, type, defaultValue); 1098 } else { 1099 // We need two passes. The first pass will use null as a 1100 // default value and will keep going if null is returned by the 1101 // abstract getValue(Map, String, Type, String) implementation. 1102 for (final String name : names) { 1103 returnValue = this.getValue(configurationCoordinates, name, type, null); 1104 if (returnValue != null) { 1105 break; 1106 } 1107 } 1108 if (returnValue == null) { 1109 // We didn't find any values. Do it again, but this time with 1110 // the defaultValue. 1111 for (final String name : names) { 1112 returnValue = this.getValue(configurationCoordinates, name, type, defaultValue); 1113 if (returnValue != null) { 1114 break; 1115 } 1116 } 1117 } 1118 } 1119 1120 if (logger.isLoggable(Level.FINER)) { 1121 logger.exiting(cn, mn, returnValue); 1122 } 1123 return returnValue; 1124 } 1125 1126 /** 1127 * Returns an {@linkplain Collections#unmodifiableSet(Set) 1128 * unmodifiable <code>Set</code>} of names of {@link 1129 * ConfigurationValue}s that might be returned by this {@link 1130 * Configurations} instance. 1131 * 1132 * <p>Implementations of this method must not return {@code 1133 * null}.</p> 1134 * 1135 * <p>Just because a name appears in the returned {@link Set} does 1136 * <em>not</em> mean that a {@link ConfigurationValue} <em>will</em> 1137 * be returned for it in a location in configuration space 1138 * identified by any arbitrary set of configuration coordinates.</p> 1139 * 1140 * @return a non-{@code null} {@link Set} of names of {@link 1141 * ConfigurationValue}s 1142 */ 1143 public abstract Set<String> getNames(); 1144 1145 1146 /* 1147 * Static methods. 1148 */ 1149 1150 1151 /** 1152 * Returns a {@link Configurations} implementation found using the 1153 * standard {@link ServiceLoader} mechanism. 1154 * 1155 * <p>The first entry in the first classpath resource named {@code 1156 * META-INF/services/org.microbean.configuration.api.Configurations} 1157 * will be treated as a class name, loaded and instantiated, and 1158 * returned.</p> 1159 * 1160 * @return a non-{@code null} {@link Configurations} instance 1161 * 1162 * @exception ConfigurationException if there was no implementation 1163 * found 1164 * 1165 * @see ServiceLoader 1166 */ 1167 public static final Configurations newInstance() { 1168 final String cn = Configurations.class.getName(); 1169 final Logger logger = Logger.getLogger(cn); 1170 final String mn = "newInstance"; 1171 if (logger.isLoggable(Level.FINER)) { 1172 logger.entering(cn, mn); 1173 } 1174 Configurations returnValue = null; 1175 ServiceLoader<Configurations> configurationsLoader = Configurations.configurationsLoader; 1176 if (configurationsLoader == null) { 1177 configurationsLoader = ServiceLoader.load(Configurations.class); 1178 assert configurationsLoader != null; 1179 Configurations.configurationsLoader = configurationsLoader; 1180 } 1181 final Iterator<Configurations> configurationsIterator = configurationsLoader.iterator(); 1182 assert configurationsIterator != null; 1183 while (returnValue == null && configurationsIterator.hasNext()) { 1184 try { 1185 returnValue = configurationsIterator.next(); 1186 } catch (final ServiceConfigurationError badServiceProviderFile) { 1187 throw new ConfigurationException(badServiceProviderFile); 1188 } 1189 } 1190 if (returnValue == null) { 1191 throw new ConfigurationException("No " + Configurations.class.getName() + " implementation found in any META-INF/services/" + Configurations.class.getName() + " service provider resources"); 1192 } 1193 if (logger.isLoggable(Level.FINER)) { 1194 logger.exiting(cn, mn, returnValue); 1195 } 1196 return returnValue; 1197 } 1198 1199}