AbstractVendedCredentialsProvider.java
// 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.doris.datasource.credentials;
import org.apache.doris.datasource.property.metastore.MetastoreProperties;
import org.apache.doris.datasource.storage.StorageAdapter;
import org.apache.doris.datasource.storage.StorageTypeId;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
public abstract class AbstractVendedCredentialsProvider {
private static final Logger LOG = LogManager.getLogger(AbstractVendedCredentialsProvider.class);
/**
* Get temporary storage attribute maps containing vendor credentials
* This is the core method: format conversion via StorageAdapter.ofAll()
*
* @param metastoreProperties Metastore properties
* @param tableObject Table object (generics, support different data sources)
* @return Storage attribute mapping containing temporary credentials
*/
public final <T> Map<StorageTypeId, StorageAdapter> getStoragePropertiesMapWithVendedCredentials(
MetastoreProperties metastoreProperties,
T tableObject) {
try {
if (!isVendedCredentialsEnabled(metastoreProperties) || tableObject == null) {
return null;
}
// 1. Extract original vendored credentials from table objects (such as oss.xxx, s3.xxx format)
Map<String, String> rawVendedCredentials = extractRawVendedCredentials(tableObject);
if (rawVendedCredentials == null || rawVendedCredentials.isEmpty()) {
return null;
}
// 2. Filter cloud storage properties before format conversion
Map<String, String> filteredCredentials =
CredentialUtils.filterCloudStorageProperties(rawVendedCredentials);
if (filteredCredentials.isEmpty()) {
return null;
}
// 3. Key steps: Format conversion via StorageAdapter.ofAll()
// This avoids writing duplicate transformation logic in the VendedCredentials class
List<StorageAdapter> vendedStorageAdapters = StorageAdapter.ofAll(filteredCredentials);
// 4. Convert to Map format. LinkedHashMap keeps the binding order (default HDFS pad
// first, explicit providers after) so downstream values() iteration merges backend
// maps in the same last-writer-wins order as the legacy enum-keyed HashMap.
Map<StorageTypeId, StorageAdapter> vendedPropertiesMap = vendedStorageAdapters.stream()
.collect(Collectors.toMap(StorageAdapter::getType, Function.identity(),
(a, b) -> {
throw new IllegalStateException("Duplicate storage type: " + a.getType());
}, LinkedHashMap::new));
if (LOG.isDebugEnabled()) {
LOG.debug("Successfully applied vended credentials for table: {}", getTableName(tableObject));
}
return vendedPropertiesMap;
} catch (Exception e) {
LOG.warn("Failed to get vended credentials, returning null", e);
// Return null on failure, Fallback is handled by Factory
return null;
}
}
/**
* Check whether to enable vendor credentials (subclass implementation)
*/
public abstract boolean isVendedCredentialsEnabled(MetastoreProperties metastoreProperties);
/**
* Extract original vendored credentials from table objects (subclass implementation)
* Returns the original format attribute, which is responsible for the conversion by StorageAdapter.ofAll()
*/
protected abstract <T> Map<String, String> extractRawVendedCredentials(T tableObject);
/**
* Get the table name (used for logs, subclasses can be rewritable)
*/
protected <T> String getTableName(T tableObject) {
return tableObject != null ? tableObject.toString() : "null";
}
}