001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. 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 implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.juneau.commons.secret; 018 019import java.util.*; 020 021import org.apache.juneau.commons.inject.*; 022 023/** 024 * Resolution helpers for selecting the active {@link SecretStore}. 025 * 026 * <p> 027 * The active store is resolved from a {@link BeanStore}, defaulting to an {@link InMemorySecretStore} when none has 028 * been contributed — the same {@code beanStore.getBean(...)}-with-drop-in-default idiom other framework-owned 029 * components use. An explicit {@link BeanStore} contribution always wins. 030 * 031 * <p> 032 * {@link #fromServiceLoader()} is the separate classpath-contribution path, mirroring 033 * {@link org.apache.juneau.commons.settings.Settings}'s opt-in {@code useServiceLoader()}. It is deliberately 034 * <b>not</b> consulted by {@link #resolve(BeanStore)}, so the no-contribution default stays a deterministic 035 * {@link InMemorySecretStore} rather than silently becoming whatever store happens to be on the classpath (which, for 036 * an environment-variable-backed provider, would quietly turn arbitrary env vars into secrets). A consumer that 037 * <i>wants</i> a classpath-contributed store can seed its {@link BeanStore} from {@link #fromServiceLoader()}. 038 * 039 * @since 10.0.0 040 */ 041public final class SecretStores { 042 043 private SecretStores() {} 044 045 /** 046 * Resolves the active secret store from the specified bean store, defaulting to a new 047 * {@link InMemorySecretStore} when none is contributed. 048 * 049 * @param beanStore The bean store to resolve from. Can be <jk>null</jk>, in which case the default is returned. 050 * @return The resolved store. Never <jk>null</jk>. 051 */ 052 public static SecretStore resolve(BeanStore beanStore) { 053 if (beanStore == null) 054 return new InMemorySecretStore(); 055 return beanStore.getBean(SecretStore.class).orElseGet(InMemorySecretStore::new); 056 } 057 058 /** 059 * Discovers a secret store contributed on the classpath via {@link SecretStoreProvider} and {@code ServiceLoader}. 060 * 061 * <p> 062 * Providers are sorted by {@link SecretStoreProvider#order()} (lowest first) and the first one that yields a 063 * non-<jk>null</jk> store wins. 064 * 065 * @return The discovered store, or {@link Optional#empty()} if none is registered on the classpath. 066 */ 067 public static Optional<SecretStore> fromServiceLoader() { 068 return ServiceLoader.load(SecretStoreProvider.class).stream() 069 .map(ServiceLoader.Provider::get) 070 .sorted(Comparator.comparingInt(SecretStoreProvider::order)) 071 .map(SecretStoreProvider::create) 072 .filter(Objects::nonNull) 073 .findFirst(); 074 } 075}