1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one 3 * or more contributor license agreements. See the NOTICE file 4 * distributed with this work for additional information 5 * regarding copyright ownership. The ASF licenses this file 6 * to you under the Apache License, Version 2.0 (the 7 * "License"); you may not use this file except in compliance 8 * with the License. You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 package org.apache.hadoop.io.nativeio; 19 20 import java.io.FileInputStream; 21 import java.io.IOException; 22 import java.io.FileDescriptor; 23 24 import org.apache.commons.lang.SystemUtils; 25 import org.apache.commons.logging.Log; 26 import org.apache.commons.logging.LogFactory; 27 import org.apache.hadoop.classification.InterfaceAudience; 28 import org.apache.hadoop.classification.InterfaceStability; 29 30 /** 31 * A factory for creating shared file descriptors inside a given directory. 32 * Typically, the directory will be /dev/shm or /tmp. 33 * 34 * We will hand out file descriptors that correspond to unlinked files residing 35 * in that directory. These file descriptors are suitable for sharing across 36 * multiple processes and are both readable and writable. 37 * 38 * Because we unlink the temporary files right after creating them, a JVM crash 39 * usually does not leave behind any temporary files in the directory. However, 40 * it may happen that we crash right after creating the file and before 41 * unlinking it. In the constructor, we attempt to clean up after any such 42 * remnants by trying to unlink any temporary files created by previous 43 * SharedFileDescriptorFactory instances that also used our prefix. 44 */ 45 @InterfaceAudience.Private 46 @InterfaceStability.Unstable 47 public class SharedFileDescriptorFactory { 48 public static final Log LOG = LogFactory.getLog(SharedFileDescriptorFactory.class); 49 private final String prefix; 50 private final String path; 51 getLoadingFailureReason()52 public static String getLoadingFailureReason() { 53 if (!NativeIO.isAvailable()) { 54 return "NativeIO is not available."; 55 } 56 if (false) { 57 return "The OS is not UNIX."; 58 } 59 return null; 60 } 61 62 /** 63 * Create a new SharedFileDescriptorFactory. 64 * 65 * @param prefix The prefix to prepend to all the file names created 66 * by this factory. 67 * @param paths An array of paths to use. We will try each path in 68 * succession, and return a factory using the first 69 * usable path. 70 * @return The factory. 71 * @throws IOException If a factory could not be created for any reason. 72 */ create(String prefix, String paths[])73 public static SharedFileDescriptorFactory create(String prefix, 74 String paths[]) throws IOException { 75 String loadingFailureReason = getLoadingFailureReason(); 76 if (loadingFailureReason != null) { 77 throw new IOException(loadingFailureReason); 78 } 79 if (paths.length == 0) { 80 throw new IOException("no SharedFileDescriptorFactory paths were " + 81 "configured."); 82 } 83 StringBuilder errors = new StringBuilder(); 84 String strPrefix = ""; 85 for (String path : paths) { 86 try { 87 FileInputStream fis = 88 new FileInputStream(createDescriptor0(prefix + "test", path, 1)); 89 fis.close(); 90 deleteStaleTemporaryFiles0(prefix, path); 91 return new SharedFileDescriptorFactory(prefix, path); 92 } catch (IOException e) { 93 errors.append(strPrefix).append("Error creating file descriptor in "). 94 append(path).append(": ").append(e.getMessage()); 95 strPrefix = ", "; 96 } 97 } 98 throw new IOException(errors.toString()); 99 } 100 101 /** 102 * Create a SharedFileDescriptorFactory. 103 * 104 * @param prefix Prefix to add to all file names we use. 105 * @param path Path to use. 106 */ SharedFileDescriptorFactory(String prefix, String path)107 private SharedFileDescriptorFactory(String prefix, String path) { 108 this.prefix = prefix; 109 this.path = path; 110 } 111 getPath()112 public String getPath() { 113 return path; 114 } 115 116 /** 117 * Create a shared file descriptor which will be both readable and writable. 118 * 119 * @param info Information to include in the path of the 120 * generated descriptor. 121 * @param length The starting file length. 122 * 123 * @return The file descriptor, wrapped in a FileInputStream. 124 * @throws IOException If there was an I/O or configuration error creating 125 * the descriptor. 126 */ createDescriptor(String info, int length)127 public FileInputStream createDescriptor(String info, int length) 128 throws IOException { 129 return new FileInputStream( 130 createDescriptor0(prefix + info, path, length)); 131 } 132 133 /** 134 * Delete temporary files in the directory, NOT following symlinks. 135 */ deleteStaleTemporaryFiles0(String prefix, String path)136 private static native void deleteStaleTemporaryFiles0(String prefix, 137 String path) throws IOException; 138 139 /** 140 * Create a file with O_EXCL, and then resize it to the desired size. 141 */ createDescriptor0(String prefix, String path, int length)142 private static native FileDescriptor createDescriptor0(String prefix, 143 String path, int length) throws IOException; 144 } 145