| /* |
| * 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.fs.swift.util; |
| |
| import org.apache.commons.logging.Log; |
| import org.apache.hadoop.fs.FileStatus; |
| import org.apache.hadoop.fs.FileSystem; |
| import org.apache.hadoop.fs.Path; |
| |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| |
| /** |
| * Various utility classes for SwiftFS support |
| */ |
| public final class SwiftUtils { |
| |
| public static final String READ = "read(buffer, offset, length)"; |
| |
| /** |
| * Join two (non null) paths, inserting a forward slash between them |
| * if needed |
| * |
| * @param path1 first path |
| * @param path2 second path |
| * @return the combined path |
| */ |
| public static String joinPaths(String path1, String path2) { |
| StringBuilder result = |
| new StringBuilder(path1.length() + path2.length() + 1); |
| result.append(path1); |
| boolean insertSlash = true; |
| if (path1.endsWith("/")) { |
| insertSlash = false; |
| } else if (path2.startsWith("/")) { |
| insertSlash = false; |
| } |
| if (insertSlash) { |
| result.append("/"); |
| } |
| result.append(path2); |
| return result.toString(); |
| } |
| |
| /** |
| * This test contains the is-directory logic for Swift, so if |
| * changed there is only one place for it. |
| * |
| * @param fileStatus status to examine |
| * @return true if we consider this status to be representative of a |
| * directory. |
| */ |
| public static boolean isDirectory(FileStatus fileStatus) { |
| return fileStatus.isDirectory() || isFilePretendingToBeDirectory(fileStatus); |
| } |
| |
| /** |
| * Test for the entry being a file that is treated as if it is a |
| * directory |
| * |
| * @param fileStatus status |
| * @return true if it meets the rules for being a directory |
| */ |
| public static boolean isFilePretendingToBeDirectory(FileStatus fileStatus) { |
| return fileStatus.getLen() == 0; |
| } |
| |
| /** |
| * Predicate: Is a swift object referring to the root direcory? |
| * @param swiftObject object to probe |
| * @return true iff the object refers to the root |
| */ |
| public static boolean isRootDir(SwiftObjectPath swiftObject) { |
| return swiftObject.objectMatches("") || swiftObject.objectMatches("/"); |
| } |
| |
| /** |
| * Sprintf() to the log iff the log is at debug level. If the log |
| * is not at debug level, the printf operation is skipped, so |
| * no time is spent generating the string. |
| * @param log log to use |
| * @param text text message |
| * @param args args arguments to the print statement |
| */ |
| public static void debug(Log log, String text, Object... args) { |
| if (log.isDebugEnabled()) { |
| log.debug(String.format(text, args)); |
| } |
| } |
| |
| /** |
| * Log an exception (in text and trace) iff the log is at debug |
| * @param log Log to use |
| * @param text text message |
| * @param ex exception |
| */ |
| public static void debugEx(Log log, String text, Exception ex) { |
| if (log.isDebugEnabled()) { |
| log.debug(text + ex, ex); |
| } |
| } |
| |
| /** |
| * Sprintf() to the log iff the log is at trace level. If the log |
| * is not at trace level, the printf operation is skipped, so |
| * no time is spent generating the string. |
| * @param log log to use |
| * @param text text message |
| * @param args args arguments to the print statement |
| */ |
| public static void trace(Log log, String text, Object... args) { |
| if (log.isTraceEnabled()) { |
| log.trace(String.format(text, args)); |
| } |
| } |
| |
| /** |
| * Given a partition number, calculate the partition value. |
| * This is used in the SwiftNativeOutputStream, and is placed |
| * here for tests to be able to calculate the filename of |
| * a partition. |
| * @param partNumber part number |
| * @return a string to use as the filename |
| */ |
| public static String partitionFilenameFromNumber(int partNumber) { |
| return String.format("%06d", partNumber); |
| } |
| |
| /** |
| * List a a path to string |
| * @param fileSystem filesystem |
| * @param path directory |
| * @return a listing of the filestatuses of elements in the directory, one |
| * to a line, precedeed by the full path of the directory |
| * @throws IOException connectivity problems |
| */ |
| public static String ls(FileSystem fileSystem, Path path) throws |
| IOException { |
| if (path == null) { |
| //surfaces when someone calls getParent() on something at the top of the path |
| return "/"; |
| } |
| FileStatus[] stats; |
| String pathtext = "ls " + path; |
| try { |
| stats = fileSystem.listStatus(path); |
| } catch (FileNotFoundException e) { |
| return pathtext + " -file not found"; |
| } catch (IOException e) { |
| return pathtext + " -failed: " + e; |
| } |
| return pathtext + fileStatsToString(stats, "\n"); |
| } |
| |
| /** |
| * Take an array of filestats and convert to a string (prefixed w/ a [01] counter |
| * @param stats array of stats |
| * @param separator separator after every entry |
| * @return a stringified set |
| */ |
| public static String fileStatsToString(FileStatus[] stats, String separator) { |
| StringBuilder buf = new StringBuilder(stats.length * 128); |
| for (int i = 0; i < stats.length; i++) { |
| buf.append(String.format("[%02d] %s", i, stats[i])).append(separator); |
| } |
| return buf.toString(); |
| } |
| |
| /** |
| * Verify that the basic args to a read operation are valid; |
| * throws an exception if not -with meaningful text includeing |
| * @param buffer destination buffer |
| * @param off offset |
| * @param len number of bytes to read |
| * @throws NullPointerException null buffer |
| * @throws IndexOutOfBoundsException on any invalid range. |
| */ |
| public static void validateReadArgs(byte[] buffer, int off, int len) { |
| if (buffer == null) { |
| throw new NullPointerException("Null byte array in"+ READ); |
| } |
| if (off < 0 ) { |
| throw new IndexOutOfBoundsException("Negative buffer offset " |
| + off |
| + " in " + READ); |
| } |
| if (len < 0 ) { |
| throw new IndexOutOfBoundsException("Negative read length " |
| + len |
| + " in " + READ); |
| } |
| if (off > buffer.length) { |
| throw new IndexOutOfBoundsException("Buffer offset of " |
| + off |
| + "beyond buffer size of " |
| + buffer.length |
| + " in " + READ); |
| } |
| } |
| } |