All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.trino.filesystem.azure.AzureLocation Maven / Gradle / Ivy

The newest version!
/*
 * Licensed 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 io.trino.filesystem.azure;

import com.google.common.base.CharMatcher;
import io.trino.filesystem.Location;

import java.util.Optional;

import static com.google.common.base.Preconditions.checkArgument;
import static java.util.Objects.requireNonNull;

class AzureLocation
{
    private static final String INVALID_ABFS_LOCATION_MESSAGE = "Invalid Azure ABFS location. Expected form is 'abfs[s]://[@].dfs./': %s";
    private static final String INVALID_WASB_LOCATION_MESSAGE = "Invalid Azure WASB location. Expected form is 'wasb[s]://[@].blob./': %s";

    // https://learn.microsoft.com/en-us/azure/azure-resource-manager/management/resource-name-rules
    private static final CharMatcher CONTAINER_VALID_CHARACTERS = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9')).or(CharMatcher.is('-'));
    private static final CharMatcher STORAGE_ACCOUNT_VALID_CHARACTERS = CharMatcher.inRange('a', 'z').or(CharMatcher.inRange('0', '9'));

    private final Location location;
    private final String scheme;
    private final String account;
    private final String endpoint;

    /**
     * Creates a new location based on the endpoint, storage account, container and blob path parsed from the location.
     * 

* Locations use the * ABFS URI syntax: *

{@code abfs[s]://@.dfs./}
*/ public AzureLocation(Location location) { this.location = requireNonNull(location, "location is null"); // wasb and wasbs are also supported but not documented scheme = location.scheme().orElseThrow(() -> new IllegalArgumentException(String.format(INVALID_ABFS_LOCATION_MESSAGE, location))); String invalidLocationMessage; if ("abfs".equals(scheme) || "abfss".equals(scheme)) { invalidLocationMessage = INVALID_ABFS_LOCATION_MESSAGE; } else if ("wasb".equals(scheme) || "wasbs".equals(scheme)) { invalidLocationMessage = INVALID_WASB_LOCATION_MESSAGE; } else { // only mention abfs in error message as the other forms are deprecated throw new IllegalArgumentException(String.format(INVALID_ABFS_LOCATION_MESSAGE, location)); } // container is interpolated into the URL path, so perform extra checks location.userInfo().ifPresent(container -> { checkArgument(!container.isEmpty(), invalidLocationMessage, location); checkArgument( CONTAINER_VALID_CHARACTERS.matchesAllOf(container), "Invalid Azure storage container name. Valid characters are 'a-z', '0-9', and '-': %s", location); checkArgument( !container.startsWith("-") && !container.endsWith("-"), "Invalid Azure storage container name. Cannot start or end with a hyphen: %s", location); checkArgument( !container.contains("--"), "Invalid Azure storage container name. Cannot contain consecutive hyphens: %s", location); }); // storage account is the first label of the host checkArgument(location.host().isPresent(), invalidLocationMessage, location); String host = location.host().get(); int accountSplit = host.indexOf('.'); checkArgument( accountSplit > 0, invalidLocationMessage, this.location); this.account = host.substring(0, accountSplit); // abfs[s] host must contain ".dfs.", and wasb[s] host must contain ".blob." before endpoint if (scheme.equals("abfs") || scheme.equals("abfss")) { checkArgument(host.substring(accountSplit).startsWith(".dfs."), invalidLocationMessage, location); // endpoint does not include dfs this.endpoint = host.substring(accountSplit + ".dfs.".length()); } else { checkArgument(host.substring(accountSplit).startsWith(".blob."), invalidLocationMessage, location); // endpoint does not include blob this.endpoint = host.substring(accountSplit + ".blob.".length()); } checkArgument(!endpoint.isEmpty(), invalidLocationMessage, location); // storage account is interpolated into URL host name, so perform extra checks checkArgument(STORAGE_ACCOUNT_VALID_CHARACTERS.matchesAllOf(account), "Invalid Azure storage account name. Valid characters are 'a-z' and '0-9': %s", location); } public Location location() { return location; } public Optional container() { return location.userInfo(); } public String account() { return account; } public String endpoint() { return endpoint; } public String path() { return location.path(); } public String directoryPath() { String path = location.path(); if (!path.isEmpty() && !path.endsWith("/")) { path += "/"; } return path; } @Override public String toString() { return location.toString(); } public Location baseLocation() { return Location.of("%s://%s%s.dfs.%s/".formatted( scheme, container().map(container -> container + "@").orElse(""), account(), endpoint)); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy