HDFS-3574. Fix small race and do some cleanup in GetImageServlet. Contributed by Todd Lipcon.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1356939 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Todd Lipcon 2012-07-03 20:55:29 +00:00
parent b8389e4c73
commit 3728d16160
4 changed files with 58 additions and 36 deletions

View File

@ -61,6 +61,20 @@ public class ServletUtil {
return s.length() == 0? null: s; return s.length() == 0? null: s;
} }
/**
* @return a long value as passed in the given parameter, throwing
* an exception if it is not present or if it is not a valid number.
*/
public static long parseLongParam(ServletRequest request, String param)
throws IOException {
String paramStr = request.getParameter(param);
if (paramStr == null) {
throw new IOException("Invalid request has no " + param + " parameter");
}
return Long.valueOf(paramStr);
}
public static final String HTML_TAIL = "<hr />\n" public static final String HTML_TAIL = "<hr />\n"
+ "<a href='http://hadoop.apache.org/core'>Hadoop</a>, " + "<a href='http://hadoop.apache.org/core'>Hadoop</a>, "
+ Calendar.getInstance().get(Calendar.YEAR) + ".\n" + Calendar.getInstance().get(Calendar.YEAR) + ".\n"

View File

@ -397,6 +397,8 @@ Branch-2 ( Unreleased changes )
HDFS-3575. HttpFS does not log Exception Stacktraces (brocknoland via tucu) HDFS-3575. HttpFS does not log Exception Stacktraces (brocknoland via tucu)
HDFS-3574. Fix small race and do some cleanup in GetImageServlet (todd)
BREAKDOWN OF HDFS-3042 SUBTASKS BREAKDOWN OF HDFS-3042 SUBTASKS
HDFS-2185. HDFS portion of ZK-based FailoverController (todd) HDFS-2185. HDFS portion of ZK-based FailoverController (todd)

View File

@ -46,8 +46,10 @@ import org.apache.hadoop.hdfs.server.protocol.RemoteEditLog;
import org.apache.hadoop.hdfs.util.DataTransferThrottler; import org.apache.hadoop.hdfs.util.DataTransferThrottler;
import org.apache.hadoop.hdfs.util.MD5FileUtils; import org.apache.hadoop.hdfs.util.MD5FileUtils;
import org.apache.hadoop.http.HttpServer; import org.apache.hadoop.http.HttpServer;
import org.apache.hadoop.io.IOUtils;
import org.apache.hadoop.io.MD5Hash; import org.apache.hadoop.io.MD5Hash;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.ServletUtil;
import org.apache.hadoop.util.StringUtils; import org.apache.hadoop.util.StringUtils;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
@ -126,23 +128,14 @@ public class GetImageServlet extends HttpServlet {
throw new IOException(errorMessage); throw new IOException(errorMessage);
} }
CheckpointFaultInjector.getInstance().beforeGetImageSetsHeaders(); CheckpointFaultInjector.getInstance().beforeGetImageSetsHeaders();
setFileNameHeaders(response, imageFile); serveFile(imageFile);
setVerificationHeaders(response, imageFile);
// send fsImage
TransferFsImage.getFileServer(response.getOutputStream(), imageFile,
getThrottler(conf));
} else if (parsedParams.isGetEdit()) { } else if (parsedParams.isGetEdit()) {
long startTxId = parsedParams.getStartTxId(); long startTxId = parsedParams.getStartTxId();
long endTxId = parsedParams.getEndTxId(); long endTxId = parsedParams.getEndTxId();
File editFile = nnImage.getStorage() File editFile = nnImage.getStorage()
.findFinalizedEditsFile(startTxId, endTxId); .findFinalizedEditsFile(startTxId, endTxId);
setVerificationHeaders(response, editFile); serveFile(editFile);
setFileNameHeaders(response, editFile);
// send edits
TransferFsImage.getFileServer(response.getOutputStream(), editFile,
getThrottler(conf));
} else if (parsedParams.isPutImage()) { } else if (parsedParams.isPutImage()) {
final long txid = parsedParams.getTxId(); final long txid = parsedParams.getTxId();
@ -182,6 +175,28 @@ public class GetImageServlet extends HttpServlet {
} }
return null; return null;
} }
private void serveFile(File file) throws IOException {
FileInputStream fis = new FileInputStream(file);
try {
setVerificationHeaders(response, file);
setFileNameHeaders(response, file);
if (!file.exists()) {
// Potential race where the file was deleted while we were in the
// process of setting headers!
throw new FileNotFoundException(file.toString());
// It's possible the file could be deleted after this point, but
// we've already opened the 'fis' stream.
// It's also possible length could change, but this would be
// detected by the client side as an inaccurate length header.
}
// send file
TransferFsImage.getFileServer(response, file, fis,
getThrottler(conf));
} finally {
IOUtils.closeStream(fis);
}
}
}); });
} catch (Throwable t) { } catch (Throwable t) {
@ -193,7 +208,7 @@ public class GetImageServlet extends HttpServlet {
} }
} }
private static void setFileNameHeaders(HttpServletResponse response, public static void setFileNameHeaders(HttpServletResponse response,
File file) { File file) {
response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" + response.setHeader(CONTENT_DISPOSITION, "attachment; filename=" +
file.getName()); file.getName());
@ -205,7 +220,7 @@ public class GetImageServlet extends HttpServlet {
* @param conf configuration * @param conf configuration
* @return a data transfer throttler * @return a data transfer throttler
*/ */
private final DataTransferThrottler getThrottler(Configuration conf) { public final static DataTransferThrottler getThrottler(Configuration conf) {
long transferBandwidth = long transferBandwidth =
conf.getLong(DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_KEY, conf.getLong(DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_KEY,
DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_DEFAULT); DFSConfigKeys.DFS_IMAGE_TRANSFER_RATE_DEFAULT);
@ -263,7 +278,7 @@ public class GetImageServlet extends HttpServlet {
* Set headers for content length, and, if available, md5. * Set headers for content length, and, if available, md5.
* @throws IOException * @throws IOException
*/ */
private void setVerificationHeaders(HttpServletResponse response, File file) public static void setVerificationHeaders(HttpServletResponse response, File file)
throws IOException { throws IOException {
response.setHeader(TransferFsImage.CONTENT_LENGTH, response.setHeader(TransferFsImage.CONTENT_LENGTH,
String.valueOf(file.length())); String.valueOf(file.length()));
@ -336,7 +351,7 @@ public class GetImageServlet extends HttpServlet {
if (key.equals("getimage")) { if (key.equals("getimage")) {
isGetImage = true; isGetImage = true;
try { try {
txId = parseLongParam(request, TXID_PARAM); txId = ServletUtil.parseLongParam(request, TXID_PARAM);
} catch (NumberFormatException nfe) { } catch (NumberFormatException nfe) {
if (request.getParameter(TXID_PARAM).equals(LATEST_FSIMAGE_VALUE)) { if (request.getParameter(TXID_PARAM).equals(LATEST_FSIMAGE_VALUE)) {
fetchLatest = true; fetchLatest = true;
@ -346,11 +361,11 @@ public class GetImageServlet extends HttpServlet {
} }
} else if (key.equals("getedit")) { } else if (key.equals("getedit")) {
isGetEdit = true; isGetEdit = true;
startTxId = parseLongParam(request, START_TXID_PARAM); startTxId = ServletUtil.parseLongParam(request, START_TXID_PARAM);
endTxId = parseLongParam(request, END_TXID_PARAM); endTxId = ServletUtil.parseLongParam(request, END_TXID_PARAM);
} else if (key.equals("putimage")) { } else if (key.equals("putimage")) {
isPutImage = true; isPutImage = true;
txId = parseLongParam(request, TXID_PARAM); txId = ServletUtil.parseLongParam(request, TXID_PARAM);
} else if (key.equals("port")) { } else if (key.equals("port")) {
remoteport = new Integer(val[0]).intValue(); remoteport = new Integer(val[0]).intValue();
} else if (key.equals(STORAGEINFO_PARAM)) { } else if (key.equals(STORAGEINFO_PARAM)) {
@ -406,16 +421,5 @@ public class GetImageServlet extends HttpServlet {
return fetchLatest; return fetchLatest;
} }
private static long parseLongParam(HttpServletRequest request, String param)
throws IOException {
// Parse the 'txid' parameter which indicates which image is to be
// fetched.
String paramStr = request.getParameter(param);
if (paramStr == null) {
throw new IOException("Invalid request has no " + param + " parameter");
}
return Long.valueOf(paramStr);
}
} }
} }

View File

@ -25,6 +25,8 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.lang.Math; import java.lang.Math;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
@ -147,16 +149,16 @@ public class TransferFsImage {
* A server-side method to respond to a getfile http request * A server-side method to respond to a getfile http request
* Copies the contents of the local file into the output stream. * Copies the contents of the local file into the output stream.
*/ */
static void getFileServer(OutputStream outstream, File localfile, public static void getFileServer(ServletResponse response, File localfile,
FileInputStream infile,
DataTransferThrottler throttler) DataTransferThrottler throttler)
throws IOException { throws IOException {
byte buf[] = new byte[HdfsConstants.IO_FILE_BUFFER_SIZE]; byte buf[] = new byte[HdfsConstants.IO_FILE_BUFFER_SIZE];
FileInputStream infile = null; ServletOutputStream out = null;
try { try {
infile = new FileInputStream(localfile);
CheckpointFaultInjector.getInstance() CheckpointFaultInjector.getInstance()
.aboutToSendFile(localfile); .aboutToSendFile(localfile);
out = response.getOutputStream();
if (CheckpointFaultInjector.getInstance(). if (CheckpointFaultInjector.getInstance().
shouldSendShortFile(localfile)) { shouldSendShortFile(localfile)) {
@ -180,14 +182,14 @@ public class TransferFsImage {
buf[0]++; buf[0]++;
} }
outstream.write(buf, 0, num); out.write(buf, 0, num);
if (throttler != null) { if (throttler != null) {
throttler.throttle(num); throttler.throttle(num);
} }
} }
} finally { } finally {
if (infile != null) { if (out != null) {
infile.close(); out.close();
} }
} }
} }