blob: 412634462a3dff16ffc04d402d84e02821dba458 [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.io.nativeio;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.FileDescriptor;
import org.apache.commons.lang.SystemUtils;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A factory for creating shared file descriptors inside a given directory.
* Typically, the directory will be /dev/shm or /tmp.
*
* We will hand out file descriptors that correspond to unlinked files residing
* in that directory. These file descriptors are suitable for sharing across
* multiple processes and are both readable and writable.
*
* Because we unlink the temporary files right after creating them, a JVM crash
* usually does not leave behind any temporary files in the directory. However,
* it may happen that we crash right after creating the file and before
* unlinking it. In the constructor, we attempt to clean up after any such
* remnants by trying to unlink any temporary files created by previous
* SharedFileDescriptorFactory instances that also used our prefix.
*/
@InterfaceAudience.Private
@InterfaceStability.Unstable
public class SharedFileDescriptorFactory {
public static final Logger LOG =
LoggerFactory.getLogger(SharedFileDescriptorFactory.class);
private final String prefix;
private final String path;
public static String getLoadingFailureReason() {
if (!NativeIO.isAvailable()) {
return "NativeIO is not available.";
}
if (!SystemUtils.IS_OS_UNIX) {
return "The OS is not UNIX.";
}
return null;
}
/**
* Create a new SharedFileDescriptorFactory.
*
* @param prefix The prefix to prepend to all the file names created
* by this factory.
* @param paths An array of paths to use. We will try each path in
* succession, and return a factory using the first
* usable path.
* @return The factory.
* @throws IOException If a factory could not be created for any reason.
*/
public static SharedFileDescriptorFactory create(String prefix,
String paths[]) throws IOException {
String loadingFailureReason = getLoadingFailureReason();
if (loadingFailureReason != null) {
throw new IOException(loadingFailureReason);
}
if (paths.length == 0) {
throw new IOException("no SharedFileDescriptorFactory paths were " +
"configured.");
}
StringBuilder errors = new StringBuilder();
String strPrefix = "";
for (String path : paths) {
try {
FileInputStream fis =
new FileInputStream(createDescriptor0(prefix + "test", path, 1));
fis.close();
deleteStaleTemporaryFiles0(prefix, path);
return new SharedFileDescriptorFactory(prefix, path);
} catch (IOException e) {
errors.append(strPrefix).append("Error creating file descriptor in ").
append(path).append(": ").append(e.getMessage());
strPrefix = ", ";
}
}
throw new IOException(errors.toString());
}
/**
* Create a SharedFileDescriptorFactory.
*
* @param prefix Prefix to add to all file names we use.
* @param path Path to use.
*/
private SharedFileDescriptorFactory(String prefix, String path) {
this.prefix = prefix;
this.path = path;
}
public String getPath() {
return path;
}
/**
* Create a shared file descriptor which will be both readable and writable.
*
* @param info Information to include in the path of the
* generated descriptor.
* @param length The starting file length.
*
* @return The file descriptor, wrapped in a FileInputStream.
* @throws IOException If there was an I/O or configuration error creating
* the descriptor.
*/
public FileInputStream createDescriptor(String info, int length)
throws IOException {
return new FileInputStream(
createDescriptor0(prefix + info, path, length));
}
/**
* Delete temporary files in the directory, NOT following symlinks.
*/
private static native void deleteStaleTemporaryFiles0(String prefix,
String path) throws IOException;
/**
* Create a file with O_EXCL, and then resize it to the desired size.
*/
private static native FileDescriptor createDescriptor0(String prefix,
String path, int length) throws IOException;
}