package com.datasalt.utils.commons.io;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.io.SequenceFile;
import org.apache.hadoop.io.SequenceFile.CompressionType;
import org.apache.hadoop.io.SequenceFile.Metadata;
import org.apache.hadoop.io.compress.CompressionCodec;
import org.apache.hadoop.util.ReflectionUtils;
import org.apache.log4j.Logger;
public class SequenceFileCat {
private static final Logger log = Logger.getLogger(SequenceFileCat.class);
private FileSystem fs;
private Configuration conf;
private List<Path> inputs;
private Path output;
private Metadata metadata;
public Metadata getMetadata() {
return metadata;
}
public void setMetadata(Metadata metadata) {
this.metadata = metadata;
}
public FileSystem getFs() {
return fs;
}
public CompressionCodec getCodec() {
return codec;
}
public void setCodec(CompressionCodec codec) {
this.codec = codec;
}
public CompressionType getCompressionType() {
return compressionType;
}
private CompressionType compressionType=CompressionType.BLOCK;
private CompressionCodec codec=new org.apache.hadoop.io.compress.DefaultCodec();
public SequenceFileCat(Configuration conf,FileSystem fs){
this.conf = conf;
this.fs = fs;
}
public void setCompressionType(CompressionType compressionType){
this.compressionType = compressionType;
}
public List<Path> getInputs() {
return inputs;
}
public void setInputs(List<Path> inputs) {
this.inputs = inputs;
}
public Path getOutput() {
return output;
}
public void setOutput(Path output) {
this.output = output;
}
public Configuration getConf() {
return conf;
}
private static void raiseException(String e) throws IOException {
throw new IOException(e);
}
private static void checkKeyValue(FileSystem fs,Configuration conf,List<Path> inputs)
throws IOException {
Path firstInput = inputs.get(0);
SequenceFile.Reader reader = new SequenceFile.Reader(fs,firstInput, conf);
Class keyClass = reader.getKeyClass();
Class valueClass = reader.getValueClass();
reader.close();
for (int i=1 ; i < inputs.size() ; i++){
Path input = inputs.get(i);
reader = new SequenceFile.Reader(fs,input, conf);
Metadata metadata = reader.getMetadata();
CompressionCodec codec = reader.getCompressionCodec();
log.info("Input:" + input);
log.info("Metadata:"+metadata);
log.info("Compression:" + codec);
if (codec != null){
log.info("Compression:"+codec.getCompressorType() + "," + codec.getDefaultExtension());
}
Class currentKey = reader.getKeyClass();
Class currentValue = reader.getValueClass();
if (currentKey != keyClass){
raiseException("Key class must be the same in all files.File '" + "' keyClass="+currentKey);
}
if (currentValue != valueClass){
raiseException("Key class must be the same in all files.File '" + "' keyClass="+currentValue);
}
reader.close();
}
}
public void concat() throws IOException {
checkKeyValue(fs,conf,inputs);
Path firstInput = inputs.get(0);
SequenceFile.Reader reader = new SequenceFile.Reader(fs,firstInput, conf);
Class keyClass = reader.getKeyClass();
Class valueClass = reader.getValueClass();
log.info("Key:"+keyClass +" Value:"+valueClass);
reader.close();
SequenceFile.Writer writer;
if (metadata != null){
writer = SequenceFile.createWriter(fs,conf, output, keyClass, valueClass, compressionType, codec,null,metadata);
} else {
writer = SequenceFile.createWriter(fs,conf, output, keyClass, valueClass, compressionType, codec);
}
CompressionCodec codec = writer.getCompressionCodec();
log.info("Output compression : " + codec);
if (codec != null){
log.info("Output compression:" + codec.getDefaultExtension() + "," + codec.getCompressorType());
}
Object key = ReflectionUtils.newInstance(keyClass,null);
Object value = ReflectionUtils.newInstance(valueClass,null);
int totalCount=0;
int partialCount=0;
long totalStart=System.currentTimeMillis();
for (Path input : inputs){
reader = new SequenceFile.Reader(fs,input, conf);
log.info("Copying input:"+input);
long partialStart=System.currentTimeMillis();
partialCount=0;
while (reader.next(key) != null){
value = reader.getCurrentValue(value);
writer.append(key, value);
partialCount++;
}
long partialEnd = System.currentTimeMillis();
double numSecs = (partialEnd-partialStart)/1000.0;
double avg = (numSecs == 0) ? Double.NaN : partialCount / numSecs;
log.info(partialCount +" pairs copied in " + numSecs + " secondss => (" + avg + " pairs/s)");
reader.close();
totalCount+=partialCount;
}
writer.close();
long totalEnd = System.currentTimeMillis();
double numSecs = ((totalEnd-totalStart))/1000.0;
double avg = (numSecs == 0) ? Double.NaN : totalCount / numSecs;
log.info("TOTAL:"+totalCount +" pairs copied in " + (int)((numSecs)/60) + " min " + ((int)numSecs%60) + " sec => (" + avg + " pairs/sec)");
}
private static final String HELP="[OPTIONAL --local ] <input_1> <input_2> ... <output>";
public static void main(String[] args) throws IOException{
Configuration conf = new Configuration();
main(args,conf);
}
public static void main(String[] args,Configuration conf) throws IOException{
if (args.length < 2){
System.err.println("At least one input and output");
System.err.println(HELP);
System.exit(-1);
}
int offset=0;
FileSystem fs;
if ("--local".equals(args[0])){
fs = FileSystem.getLocal(conf);
offset=1;
} else {
fs = FileSystem.get(conf);
}
List<Path> inputs = new ArrayList<Path>();
for (int i=offset ; i < args.length -1 ; i++){
Path p = new Path(args[i]);
FileStatus[] statuses = fs.globStatus(p);
for (FileStatus s : statuses){
Path sP = s.getPath();
System.out.println("Input:" + sP);
inputs.add(sP);
}
}
if (inputs.isEmpty()){
System.err.println("No existing inputs ");
System.exit(-1);
}
Path output = new Path(args[args.length-1]);
System.out.println("Output:" + output);
SequenceFileCat s = new SequenceFileCat(conf,fs);
s.setInputs(inputs);
s.setOutput(output);
//TODO update this to enable different compressionCodec and types;
//s.setCodec(codec);
//s.setCompressionType(type);
//s.setMetadata(..)
s.concat();
}
public static void printInfo(Path input,Configuration conf) throws IOException{
FileSystem fs = FileSystem.get(conf);
SequenceFile.Reader reader = new SequenceFile.Reader(fs,input, conf);
CompressionCodec codec = reader.getCompressionCodec();
Metadata metadata = reader.getMetadata();
Class keyClass = reader.getKeyClass();
Class valueClass = reader.getValueClass();
System.out.println("Compression codec:" + codec.getClass());
System.out.println("Compressor type:" + codec.getCompressorType());
System.out.println("Decompressor type:" + codec.getDecompressorType());
System.out.println("key:"+ keyClass + " value:" + valueClass);
System.out.println("Metadata:"+metadata);
reader.close();
}
}