blob: 44856cb5e8996316bb5536789a505e947af33af5 [file] [log] [blame]
/**
* 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.hadoop.hdfs.server.federation.resolver;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.apache.hadoop.hdfs.server.federation.resolver.order.DestinationOrder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A map of the properties and target destinations (name space + path) for
* a path in the global/federated name space.
* This data is generated from the @see MountTable records.
*/
public class PathLocation {
private static final Logger LOG = LoggerFactory.getLogger(PathLocation.class);
/** Source path in global namespace. */
private final String sourcePath;
/** Remote paths in the target name spaces. */
private final List<RemoteLocation> destinations;
/** Order for the destinations. */
private final DestinationOrder destOrder;
/**
* Create a new PathLocation.
*
* @param source Source path in the global name space.
* @param dest Destinations of the mount table entry.
* @param order Order of the locations.
*/
public PathLocation(
String source, List<RemoteLocation> dest, DestinationOrder order) {
this.sourcePath = source;
this.destinations = Collections.unmodifiableList(dest);
this.destOrder = order;
}
/**
* Create a new PathLocation with default HASH order.
*
* @param source Source path in the global name space.
* @param dest Destinations of the mount table entry.
*/
public PathLocation(String source, List<RemoteLocation> dest) {
this(source, dest, DestinationOrder.HASH);
}
/**
* Create a path location from another path.
*
* @param other Other path location to copy from.
*/
public PathLocation(final PathLocation other) {
this.sourcePath = other.sourcePath;
this.destinations = Collections.unmodifiableList(other.destinations);
this.destOrder = other.destOrder;
}
/**
* Create a path location from another path with the destinations sorted.
*
* @param other Other path location to copy from.
* @param firstNsId Identifier of the namespace to place first.
*/
@Deprecated
public PathLocation(PathLocation other, String firstNsId) {
this.sourcePath = other.sourcePath;
this.destOrder = other.destOrder;
this.destinations = orderedNamespaces(other.destinations, firstNsId);
}
/**
* Return a path location with the prioritized destinations based on
* the current path location.
*
* @param base The base path location we'd like to prioritize on.
* @param firstNsId Identifier of the namespace to place first.
*/
public static PathLocation prioritizeDestination(
PathLocation base, String firstNsId) {
List<RemoteLocation> prioritizedDestinations = orderedNamespaces(
base.destinations, firstNsId);
return new PathLocation(base.sourcePath, prioritizedDestinations,
base.destOrder);
}
/**
* Prioritize a location/destination by its name space/nameserviceId.
* This destination might be used by other threads, so the source is not
* modifiable.
*
* @param original List of destinations to order.
* @param nsId The name space/nameserviceID to prioritize.
* @return Prioritized list of detinations that cannot be modified.
*/
private static List<RemoteLocation> orderedNamespaces(
final List<RemoteLocation> original, final String nsId) {
if (original.size() <= 1) {
return original;
}
LinkedList<RemoteLocation> newDestinations = new LinkedList<>();
boolean found = false;
for (RemoteLocation dest : original) {
if (dest.getNameserviceId().equals(nsId)) {
found = true;
newDestinations.addFirst(dest);
} else {
newDestinations.add(dest);
}
}
if (!found) {
LOG.debug("Cannot find location with namespace {} in {}",
nsId, original);
}
return Collections.unmodifiableList(newDestinations);
}
/**
* Get the source path in the global namespace for this path location.
*
* @return The path in the global namespace.
*/
public String getSourcePath() {
return this.sourcePath;
}
/**
* Get the subclusters defined for the destinations.
*
* @return Set containing the subclusters.
*/
public Set<String> getNamespaces() {
Set<String> namespaces = new HashSet<>();
List<RemoteLocation> locations = this.getDestinations();
for (RemoteLocation location : locations) {
String nsId = location.getNameserviceId();
namespaces.add(nsId);
}
return namespaces;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (RemoteLocation destination : this.destinations) {
String nsId = destination.getNameserviceId();
String path = destination.getDest();
if (sb.length() > 0) {
sb.append(",");
}
sb.append(nsId + "->" + path);
}
if (this.destinations.size() > 1) {
sb.append(" [")
.append(this.destOrder.toString())
.append("]");
}
return sb.toString();
}
/**
* Check if this location supports multiple clusters/paths.
*
* @return If it has multiple destinations.
*/
public boolean hasMultipleDestinations() {
return this.destinations.size() > 1;
}
/**
* Get the list of locations found in the mount table.
* The first result is the highest priority path.
*
* @return List of remote locations.
*/
public List<RemoteLocation> getDestinations() {
return Collections.unmodifiableList(this.destinations);
}
/**
* Get the order for the destinations.
*
* @return Order for the destinations.
*/
public DestinationOrder getDestinationOrder() {
return this.destOrder;
}
/**
* Get the default or highest priority location.
*
* @return The default location.
*/
public RemoteLocation getDefaultLocation() {
if (destinations.isEmpty() || destinations.get(0).getDest() == null) {
throw new UnsupportedOperationException(
"Unsupported path " + sourcePath + " please check mount table");
}
return destinations.get(0);
}
}