HADOOP-11012. hadoop fs -text of zero-length file causes EOFException. Contributed by Eric Payne
(cherry picked from commit 01e8f056d9
)
This commit is contained in:
parent
a96696bdc3
commit
0a582c77ef
|
@ -397,6 +397,9 @@ Release 2.6.0 - UNRELEASED
|
||||||
|
|
||||||
HADOOP-11036. Add build directory to .gitignore (Tsuyoshi OZAWA via aw)
|
HADOOP-11036. Add build directory to .gitignore (Tsuyoshi OZAWA via aw)
|
||||||
|
|
||||||
|
HADOOP-11012. hadoop fs -text of zero-length file causes EOFException
|
||||||
|
(Eric Payne via jlowe)
|
||||||
|
|
||||||
Release 2.5.1 - UNRELEASED
|
Release 2.5.1 - UNRELEASED
|
||||||
|
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package org.apache.hadoop.fs.shell;
|
package org.apache.hadoop.fs.shell;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.EOFException;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
@ -126,8 +127,17 @@ class Display extends FsCommand {
|
||||||
protected InputStream getInputStream(PathData item) throws IOException {
|
protected InputStream getInputStream(PathData item) throws IOException {
|
||||||
FSDataInputStream i = (FSDataInputStream)super.getInputStream(item);
|
FSDataInputStream i = (FSDataInputStream)super.getInputStream(item);
|
||||||
|
|
||||||
|
// Handle 0 and 1-byte files
|
||||||
|
short leadBytes;
|
||||||
|
try {
|
||||||
|
leadBytes = i.readShort();
|
||||||
|
} catch (EOFException e) {
|
||||||
|
i.seek(0);
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
// Check type of stream first
|
// Check type of stream first
|
||||||
switch(i.readShort()) {
|
switch(leadBytes) {
|
||||||
case 0x1f8b: { // RFC 1952
|
case 0x1f8b: { // RFC 1952
|
||||||
// Must be gzip
|
// Must be gzip
|
||||||
i.seek(0);
|
i.seek(0);
|
||||||
|
|
|
@ -42,29 +42,14 @@ public class TestTextCommand {
|
||||||
System.getProperty("test.build.data", "build/test/data/") + "/testText";
|
System.getProperty("test.build.data", "build/test/data/") + "/testText";
|
||||||
private static final String AVRO_FILENAME =
|
private static final String AVRO_FILENAME =
|
||||||
new Path(TEST_ROOT_DIR, "weather.avro").toUri().getPath();
|
new Path(TEST_ROOT_DIR, "weather.avro").toUri().getPath();
|
||||||
|
private static final String TEXT_FILENAME =
|
||||||
|
new Path(TEST_ROOT_DIR, "testtextfile.txt").toUri().getPath();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tests whether binary Avro data files are displayed correctly.
|
* Tests whether binary Avro data files are displayed correctly.
|
||||||
*/
|
*/
|
||||||
@Test (timeout = 30000)
|
@Test (timeout = 30000)
|
||||||
public void testDisplayForAvroFiles() throws Exception {
|
public void testDisplayForAvroFiles() throws Exception {
|
||||||
// Create a small Avro data file on the local file system.
|
|
||||||
createAvroFile(generateWeatherAvroBinaryData());
|
|
||||||
|
|
||||||
// Prepare and call the Text command's protected getInputStream method
|
|
||||||
// using reflection.
|
|
||||||
Configuration conf = new Configuration();
|
|
||||||
URI localPath = new URI(AVRO_FILENAME);
|
|
||||||
PathData pathData = new PathData(localPath, conf);
|
|
||||||
Display.Text text = new Display.Text();
|
|
||||||
text.setConf(conf);
|
|
||||||
Method method = text.getClass().getDeclaredMethod(
|
|
||||||
"getInputStream", PathData.class);
|
|
||||||
method.setAccessible(true);
|
|
||||||
InputStream stream = (InputStream) method.invoke(text, pathData);
|
|
||||||
String output = inputStreamToString(stream);
|
|
||||||
|
|
||||||
// Check the output.
|
|
||||||
String expectedOutput =
|
String expectedOutput =
|
||||||
"{\"station\":\"011990-99999\",\"time\":-619524000000,\"temp\":0}" +
|
"{\"station\":\"011990-99999\",\"time\":-619524000000,\"temp\":0}" +
|
||||||
System.getProperty("line.separator") +
|
System.getProperty("line.separator") +
|
||||||
|
@ -77,18 +62,72 @@ public class TestTextCommand {
|
||||||
"{\"station\":\"012650-99999\",\"time\":-655509600000,\"temp\":78}" +
|
"{\"station\":\"012650-99999\",\"time\":-655509600000,\"temp\":78}" +
|
||||||
System.getProperty("line.separator");
|
System.getProperty("line.separator");
|
||||||
|
|
||||||
|
String output = readUsingTextCommand(AVRO_FILENAME,
|
||||||
|
generateWeatherAvroBinaryData());
|
||||||
assertEquals(expectedOutput, output);
|
assertEquals(expectedOutput, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a zero-length file is displayed correctly.
|
||||||
|
*/
|
||||||
|
@Test (timeout = 30000)
|
||||||
|
public void testEmptyTextFil() throws Exception {
|
||||||
|
byte[] emptyContents = { };
|
||||||
|
String output = readUsingTextCommand(TEXT_FILENAME, emptyContents);
|
||||||
|
assertTrue("".equals(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a one-byte file is displayed correctly.
|
||||||
|
*/
|
||||||
|
@Test (timeout = 30000)
|
||||||
|
public void testOneByteTextFil() throws Exception {
|
||||||
|
byte[] oneByteContents = { 'x' };
|
||||||
|
String output = readUsingTextCommand(TEXT_FILENAME, oneByteContents);
|
||||||
|
assertTrue(new String(oneByteContents).equals(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests that a one-byte file is displayed correctly.
|
||||||
|
*/
|
||||||
|
@Test (timeout = 30000)
|
||||||
|
public void testTwoByteTextFil() throws Exception {
|
||||||
|
byte[] twoByteContents = { 'x', 'y' };
|
||||||
|
String output = readUsingTextCommand(TEXT_FILENAME, twoByteContents);
|
||||||
|
assertTrue(new String(twoByteContents).equals(output));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a file on the local file system and read it using
|
||||||
|
// the Display.Text class.
|
||||||
|
private String readUsingTextCommand(String fileName, byte[] fileContents)
|
||||||
|
throws Exception {
|
||||||
|
createFile(fileName, fileContents);
|
||||||
|
|
||||||
|
// Prepare and call the Text command's protected getInputStream method
|
||||||
|
// using reflection.
|
||||||
|
Configuration conf = new Configuration();
|
||||||
|
URI localPath = new URI(fileName);
|
||||||
|
PathData pathData = new PathData(localPath, conf);
|
||||||
|
Display.Text text = new Display.Text() {
|
||||||
|
@Override
|
||||||
|
public InputStream getInputStream(PathData item) throws IOException {
|
||||||
|
return super.getInputStream(item);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
text.setConf(conf);
|
||||||
|
InputStream stream = (InputStream) text.getInputStream(pathData);
|
||||||
|
return inputStreamToString(stream);
|
||||||
|
}
|
||||||
|
|
||||||
private String inputStreamToString(InputStream stream) throws IOException {
|
private String inputStreamToString(InputStream stream) throws IOException {
|
||||||
StringWriter writer = new StringWriter();
|
StringWriter writer = new StringWriter();
|
||||||
IOUtils.copy(stream, writer);
|
IOUtils.copy(stream, writer);
|
||||||
return writer.toString();
|
return writer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createAvroFile(byte[] contents) throws IOException {
|
private void createFile(String fileName, byte[] contents) throws IOException {
|
||||||
(new File(TEST_ROOT_DIR)).mkdir();
|
(new File(TEST_ROOT_DIR)).mkdir();
|
||||||
File file = new File(AVRO_FILENAME);
|
File file = new File(fileName);
|
||||||
file.createNewFile();
|
file.createNewFile();
|
||||||
FileOutputStream stream = new FileOutputStream(file);
|
FileOutputStream stream = new FileOutputStream(file);
|
||||||
stream.write(contents);
|
stream.write(contents);
|
||||||
|
|
Loading…
Reference in New Issue