/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.sshd.common.util.security;
import java.security.Provider;
import java.security.Security;
import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
import org.apache.sshd.common.util.ValidateUtils;
import org.apache.sshd.common.util.logging.AbstractLoggingBean;
/**
* @author <a href="mailto:dev@mina.apache.org">Apache MINA SSHD Project</a>
*/
public abstract class AbstractSecurityProviderRegistrar
extends AbstractLoggingBean
implements SecurityProviderRegistrar {
protected final Map<String, Object> props = new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
protected final Map<Class<?>, Map<String, Boolean>> supportedEntities = new HashMap<>();
protected final AtomicReference<Provider> providerHolder = new AtomicReference<>(null);
private final String name;
protected AbstractSecurityProviderRegistrar(String name) {
this.name = ValidateUtils.checkNotNullAndNotEmpty(name, "No name provided");
}
@Override
public final String getName() {
return name;
}
@Override
public Map<String, Object> getProperties() {
return props;
}
@Override
public boolean isSecurityEntitySupported(Class<?> entityType, String name) {
Map<String, Boolean> supportMap;
synchronized (supportedEntities) {
supportMap = supportedEntities.computeIfAbsent(
entityType, k -> new TreeMap<>(String.CASE_INSENSITIVE_ORDER));
}
Boolean supportFlag;
synchronized (supportMap) {
supportFlag = supportMap.computeIfAbsent(
name, k -> SecurityProviderRegistrar.super.isSecurityEntitySupported(entityType, name));
}
return supportFlag;
}
/**
* Attempts to see if a provider with this name already registered. If not,
* then uses reflection API in order to load and instantiate the specified
* <tt>providerClassName</tt>
*
* @param providerClassName The fully-qualified class name to instantiate
* if a provider not already registered
* @return The resolved {@link Provider} instance - <B>Note:</B> the result
* is <U>cached</U> - i.e., successful resolution result will not cause
* the code to re-resolve the provider
* @throws ReflectiveOperationException If failed to instantiate the provider
* @throws UnsupportedOperationException If registrar not supported
* @see #isSupported()
* @see Security#getProvider(String)
* @see #createProviderInstance(String)
*/
protected Provider getOrCreateProvider(String providerClassName) throws ReflectiveOperationException {
if (!isSupported()) {
throw new UnsupportedOperationException("Provider not supported");
}
Provider provider;
boolean created = false;
synchronized (providerHolder) {
provider = providerHolder.get();
if (provider != null) {
return provider;
}
provider = Security.getProvider(getName());
if (provider == null) {
provider = createProviderInstance(providerClassName);
created = true;
}
providerHolder.set(provider);
}
if (created) {
log.info("getOrCreateProvider({}) created instance of {}", getName(), providerClassName);
} else {
log.info("getOrCreateProvider({}) resolved instance of {}", getName(), provider.getClass().getName());
}
return provider;
}
protected Provider createProviderInstance(String providerClassName) throws ReflectiveOperationException {
return SecurityProviderChoice.createProviderInstance(getClass(), providerClassName);
}
@Override
public String toString() {
return getClass().getSimpleName() + "[" + getName() + "]";
}
}