blob: 0a3f19db1057fdcb5668dbba9b0333ef430104c5 [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.store.records;
import java.io.IOException;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.SortedMap;
import java.util.TreeMap;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.server.federation.resolver.RemoteLocation;
import org.apache.hadoop.hdfs.server.federation.resolver.order.DestinationOrder;
import org.apache.hadoop.hdfs.server.federation.store.driver.StateStoreSerializer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Data schema for
* {@link org.apache.hadoop.hdfs.server.federation.store.
* MountTableStore FederationMountTableStore} data stored in the
* {@link org.apache.hadoop.hdfs.server.federation.store.
* StateStoreService FederationStateStoreService}. Supports string
* serialization.
*/
public abstract class MountTable extends BaseRecord {
private static final Logger LOG = LoggerFactory.getLogger(MountTable.class);
/** Comparator for paths which considers the /. */
public static final Comparator<String> PATH_COMPARATOR =
new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
String s1 = o1.replace('/', ' ');
String s2 = o2.replace('/', ' ');
return s1.compareTo(s2);
}
};
/** Comparator based on the mount table source. */
public static final Comparator<MountTable> SOURCE_COMPARATOR =
new Comparator<MountTable>() {
public int compare(MountTable m1, MountTable m2) {
String src1 = m1.getSourcePath();
String src2 = m2.getSourcePath();
return PATH_COMPARATOR.compare(src1, src2);
}
};
/**
* Default constructor for a mount table entry.
*/
public MountTable() {
super();
}
public static MountTable newInstance() {
MountTable record = StateStoreSerializer.newRecord(MountTable.class);
record.init();
return record;
}
/**
* Constructor for a mount table entry with a single destinations.
*
* @param src Source path in the mount entry.
* @param destinations Nameservice destination of the mount point.
* @param dateCreated Created date.
* @param dateModified Modified date.
* @throws IOException
*/
public static MountTable newInstance(final String src,
final Map<String, String> destinations,
long dateCreated, long dateModified) throws IOException {
MountTable record = newInstance(src, destinations);
record.setDateCreated(dateCreated);
record.setDateModified(dateModified);
return record;
}
/**
* Constructor for a mount table entry with multiple destinations.
*
* @param src Source path in the mount entry.
* @param destinations Nameservice destinations of the mount point.
* @throws IOException
*/
public static MountTable newInstance(final String src,
final Map<String, String> destinations) throws IOException {
MountTable record = newInstance();
// Normalize the mount path
record.setSourcePath(normalizeFileSystemPath(src));
// Build a list of remote locations
final List<RemoteLocation> locations = new LinkedList<>();
for (Entry<String, String> entry : destinations.entrySet()) {
String nsId = entry.getKey();
String path = normalizeFileSystemPath(entry.getValue());
RemoteLocation location = new RemoteLocation(nsId, path);
locations.add(location);
}
// Set the serialized dest string
record.setDestinations(locations);
// Validate
record.validate();
return record;
}
/**
* Get source path in the federated namespace.
*
* @return Source path in the federated namespace.
*/
public abstract String getSourcePath();
/**
* Set source path in the federated namespace.
*
* @param path Source path in the federated namespace.
*/
public abstract void setSourcePath(String path);
/**
* Get a list of destinations (namespace + path) present for this entry.
*
* @return List of RemoteLocation destinations. Null if no destinations.
*/
public abstract List<RemoteLocation> getDestinations();
/**
* Set the destination paths.
*
* @param paths Destination paths.
*/
public abstract void setDestinations(List<RemoteLocation> dests);
/**
* Add a new destination to this mount table entry.
*/
public abstract boolean addDestination(String nsId, String path);
/**
* Check if the entry is read only.
*
* @return If the entry is read only.
*/
public abstract boolean isReadOnly();
/**
* Set an entry to be read only.
*
* @param ro If the entry is read only.
*/
public abstract void setReadOnly(boolean ro);
/**
* Get the order of the destinations for this mount table entry.
*
* @return Order of the destinations.
*/
public abstract DestinationOrder getDestOrder();
/**
* Set the order of the destinations for this mount table entry.
*
* @param order Order of the destinations.
*/
public abstract void setDestOrder(DestinationOrder order);
/**
* Get the default location.
* @return The default location.
*/
public RemoteLocation getDefaultLocation() {
List<RemoteLocation> dests = this.getDestinations();
if (dests == null || dests.isEmpty()) {
return null;
}
return dests.get(0);
}
@Override
public boolean like(final BaseRecord o) {
if (o instanceof MountTable) {
MountTable other = (MountTable)o;
if (getSourcePath() != null &&
!getSourcePath().equals(other.getSourcePath())) {
return false;
}
if (getDestinations() != null &&
!getDestinations().equals(other.getDestinations())) {
return false;
}
return true;
}
return false;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(this.getSourcePath());
sb.append("->");
List<RemoteLocation> destinations = this.getDestinations();
sb.append(destinations);
if (destinations != null && destinations.size() > 1) {
sb.append("[" + this.getDestOrder() + "]");
}
if (this.isReadOnly()) {
sb.append("[RO]");
}
return sb.toString();
}
@Override
public SortedMap<String, String> getPrimaryKeys() {
SortedMap<String, String> map = new TreeMap<>();
map.put("sourcePath", this.getSourcePath());
return map;
}
@Override
public boolean validate() {
boolean ret = super.validate();
if (this.getSourcePath() == null || this.getSourcePath().length() == 0) {
LOG.error("Invalid entry, no source path specified ", this);
ret = false;
}
if (!this.getSourcePath().startsWith("/")) {
LOG.error("Invalid entry, all mount points must start with / ", this);
ret = false;
}
if (this.getDestinations() == null || this.getDestinations().size() == 0) {
LOG.error("Invalid entry, no destination paths specified ", this);
ret = false;
}
for (RemoteLocation loc : getDestinations()) {
String nsId = loc.getNameserviceId();
if (nsId == null || nsId.length() == 0) {
LOG.error("Invalid entry, invalid destination nameservice ", this);
ret = false;
}
if (loc.getDest() == null || loc.getDest().length() == 0) {
LOG.error("Invalid entry, invalid destination path ", this);
ret = false;
}
if (!loc.getDest().startsWith("/")) {
LOG.error("Invalid entry, all destination must start with / ", this);
ret = false;
}
}
return ret;
}
@Override
public long getExpirationMs() {
return 0;
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 31)
.append(this.getSourcePath())
.append(this.getDestinations())
.append(this.isReadOnly())
.append(this.getDestOrder())
.toHashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof MountTable) {
MountTable other = (MountTable)obj;
if (!this.getSourcePath().equals(other.getSourcePath())) {
return false;
} else if (!this.getDestinations().equals(other.getDestinations())) {
return false;
} else if (this.isReadOnly() != other.isReadOnly()) {
return false;
} else if (!this.getDestOrder().equals(other.getDestOrder())) {
return false;
}
return true;
}
return false;
}
/**
* Normalize a path for that filesystem.
*
* @param path Path to normalize.
* @return Normalized path.
*/
private static String normalizeFileSystemPath(final String path) {
Path normalizedPath = new Path(path);
return normalizedPath.toString();
}
}