package org.apache.hadoop.thriftdatanode;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.util.Daemon;
import org.apache.hadoop.util.StringUtils;
import org.apache.hadoop.hdfs.protocol.Block;
import org.apache.hadoop.hdfs.protocol.BlockPathInfo;
import org.apache.hadoop.hdfs.protocol.DatanodeID;
import org.apache.hadoop.hdfs.protocol.DatanodeInfo;
import org.apache.hadoop.hdfs.protocol.ClientDatanodeProtocol;
import org.apache.hadoop.hdfs.DFSClient;
import org.apache.hadoop.ipc.ProtocolProxy;
import org.apache.thrift.TProcessor;
import org.apache.thrift.TProcessorFactory;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TBinaryProtocol;
import org.apache.thrift.server.TServer;
import org.apache.thrift.server.TThreadPoolServer;
import org.apache.thrift.transport.TServerSocket;
import org.apache.thrift.transport.TServerTransport;
import org.apache.thrift.transport.TTransportFactory;
import org.apache.hadoop.thriftdatanode.api.*;
/**
* ThriftHadoopDatanode
* A thrift wrapper around the Hadoop Datanode
*/
public class HadoopThriftDatanodeServer extends ThriftHadoopDatanode {
static int serverPort = 0; // default port
TServer server = null;
public static class HadoopThriftHandler implements ThriftHadoopDatanode.Iface
{
public static final Log LOG = LogFactory.getLog("org.apache.hadoop.thriftdatanode");
public static final int timeout = 10000;
// HDFS glue
Configuration conf;
FileSystem fs;
// stucture that maps each host:port object into an RPC object
private HashMap<String, ClientDatanodeProtocol> hadoopHash =
new HashMap<String, ClientDatanodeProtocol>();
// Detect inactive session
private static volatile boolean fsRunning = true;
private static long now;
// allow outsider to change the hadoopthrift path
public void setOption(String key, String val) {
}
/**
* Current system time.
* @return current time in msec.
*/
static long now() {
return System.currentTimeMillis();
}
/**
* getVersion
*
* @return current version of the interface.
*/
public String getVersion() {
return "0.1";
}
/**
* shutdown
*
* cleanly closes everything and exit.
*/
public void shutdown(int status) {
LOG.info("HadoopThriftDatanodeServer shutting down.");
try {
fs.close();
} catch (IOException e) {
LOG.warn("Unable to close file system");
}
Runtime.getRuntime().exit(status);
}
/**
* HadoopThriftDatanodeServer
*
* Constructor for the HadoopThriftDatanodeServer glue with Thrift Class.
*
* @param name - the name of this handler
*/
public HadoopThriftHandler(String name) {
conf = new Configuration();
now = now();
try {
fs = FileSystem.get(conf);
} catch (IOException e) {
LOG.warn("Unable to open hadoop file system...");
Runtime.getRuntime().exit(-1);
}
}
/**
* printStackTrace
*
* Helper function to print an exception stack trace to the log and not stderr
*
* @param e the exception
*
*/
static private void printStackTrace(Exception e) {
for(StackTraceElement s: e.getStackTrace()) {
LOG.error(s);
}
}
/**
* Parse a host:port pair and return the port name
*/
static private int getPort(String name) {
int colon = name.indexOf(":");
if (colon < 0) {
return 50010; // default port.
}
return Integer.parseInt(name.substring(colon+1));
}
/**
* Creates one rpc object if necessary
*/
private synchronized ClientDatanodeProtocol getOrCreate(String name)
throws IOException {
ClientDatanodeProtocol obj = hadoopHash.get(name);
if (obj != null) {
return obj;
}
// connection does not exist, create a new one.
DatanodeID dn = new DatanodeID(name, "", -1, getPort(name));
ClientDatanodeProtocol instance =
DFSClient.createClientDatanodeProtocolProxy(dn, conf, timeout);
// cache connection
hadoopHash.put(name, instance);
return instance;
}
/**
* Implement the API exported by this thrift server
*/
public ThdfsBlock recoverBlock(TDatanodeID datanode,
ThdfsNamespaceId namespaceId,
ThdfsBlock block,
boolean keepLength,
List<TDatanodeID> targets,
long deadline)
throws ThriftIOException, TException {
Block blk = new Block(block.blockId, block.numBytes,
block.generationStamp);
DatanodeInfo[] targs = new DatanodeInfo[targets.size()];
for (int i = 0; i < targs.length; i++) {
targs[i] = new DatanodeInfo(
new DatanodeID(targets.get(i).name, "", -1,
getPort(targets.get(i).name)));
}
// make RPC to datanode
try {
ClientDatanodeProtocol remote = getOrCreate(datanode.name);
Block nblk = remote.recoverBlock(namespaceId.id, blk,
keepLength, targs, deadline).getBlock();
return new ThdfsBlock(nblk.getBlockId(), nblk.getNumBytes(),
nblk.getGenerationStamp());
} catch (IOException e) {
String msg = "Error recoverBlock datanode " + datanode.name +
" namespaceid " + namespaceId.id +
" block " + blk;
LOG.warn(msg);
throw new ThriftIOException(msg);
}
}
// get block info from datanode
public ThdfsBlock getBlockInfo(TDatanodeID datanode,
ThdfsNamespaceId namespaceid,
ThdfsBlock block)
throws ThriftIOException, TException {
Block blk = new Block(block.blockId, block.numBytes,
block.generationStamp);
// make RPC to datanode
try {
ClientDatanodeProtocol remote = getOrCreate(datanode.name);
Block nblk = remote.getBlockInfo(namespaceid.id, blk);
return new ThdfsBlock(nblk.getBlockId(), nblk.getNumBytes(),
nblk.getGenerationStamp());
} catch (IOException e) {
String msg = "Error getBlockInfo datanode " + datanode.name +
" namespaceid " + namespaceid.id +
" block " + blk;
LOG.warn(msg);
throw new ThriftIOException(msg);
}
}
// Instruct the datanode to copy a block to specified target.
public void copyBlock(TDatanodeID datanode,
ThdfsNamespaceId srcNamespaceId, ThdfsBlock srcblock,
ThdfsNamespaceId dstNamespaceId, ThdfsBlock destblock,
TDatanodeID target, boolean asynchronous)
throws ThriftIOException, TException {
Block sblk = new Block(srcblock.blockId, srcblock.numBytes,
srcblock.generationStamp);
Block dblk = new Block(destblock.blockId, destblock.numBytes,
destblock.generationStamp);
DatanodeInfo targs = new DatanodeInfo(
new DatanodeID(target.name, "", -1, getPort(target.name)));
// make RPC to datanode
try {
ClientDatanodeProtocol remote = getOrCreate(datanode.name);
remote.copyBlock(srcNamespaceId.id, sblk,
dstNamespaceId.id, dblk,
targs, asynchronous);
} catch (IOException e) {
String msg = "Error copyBlock datanode " + datanode.name +
" srcnamespaceid " + srcNamespaceId.id +
" destnamespaceid " + dstNamespaceId.id +
" srcblock " + sblk +
" destblock " + dblk;
LOG.warn(msg);
throw new ThriftIOException(msg);
}
}
// Retrives filename of blockfile and metafile from datanode
public ThdfsBlockPath getBlockPathInfo(TDatanodeID datanode,
ThdfsNamespaceId namespaceId,
ThdfsBlock block)
throws ThriftIOException, TException {
Block blk = new Block(block.blockId, block.numBytes,
block.generationStamp);
// make RPC to datanode to find local pathnames of blocks
try {
ClientDatanodeProtocol remote = getOrCreate(datanode.name);
BlockPathInfo pathinfo = remote.getBlockPathInfo(namespaceId.id, blk);
return new ThdfsBlockPath(pathinfo.getBlockPath(),
pathinfo.getMetaPath());
} catch (IOException e) {
String msg = "Error getBlockPathInfo datanode " + datanode.name +
" namespaceid " + namespaceId.id +
" block " + blk;
LOG.warn(msg);
throw new ThriftIOException(msg);
}
}
} // end of ThriftHadoopDatanode.Iface
// Bind to port. If the specified port is 0, then bind to random port.
private ServerSocket createServerSocket(int port) throws IOException {
try {
ServerSocket sock = new ServerSocket();
// Prevent 2MSL delay problem on server restarts
sock.setReuseAddress(true);
// Bind to listening port
if (port == 0) {
sock.bind(null);
serverPort = sock.getLocalPort();
} else {
sock.bind(new InetSocketAddress(port));
}
return sock;
} catch (IOException ioe) {
throw new IOException("Could not create ServerSocket on port " + port + "." +
ioe);
}
}
/**
* Constructs a server object
*/
public HadoopThriftDatanodeServer(String [] args) throws Exception {
if (args.length > 0) {
serverPort = new Integer(args[0]);
}
try {
ServerSocket ssock = createServerSocket(serverPort);
TServerTransport serverTransport = new TServerSocket(ssock);
Iface handler = new HadoopThriftHandler("hdfs-thriftdatanode-dhruba");
ThriftHadoopDatanode.Processor processor = new ThriftHadoopDatanode.Processor(handler);
TThreadPoolServer.Args serverArgs = new TThreadPoolServer.Args(serverTransport);
serverArgs.minWorkerThreads = 10;
serverArgs.processor(processor);
serverArgs.transportFactory(new TTransportFactory());
serverArgs.protocolFactory(new TBinaryProtocol.Factory());
server = new TThreadPoolServer(serverArgs);
System.out.println("Starting the hadoop datanode thrift server on port [" + serverPort + "]...");
HadoopThriftHandler.LOG.info("Starting the hadoop datanode thrift server on port [" +serverPort + "]...");
System.out.flush();
} catch (Exception x) {
HadoopThriftHandler.LOG.warn("XXX Exception in HadoopThriftDatanodeServer");
x.printStackTrace();
throw x;
}
}
public static void main(String [] args) throws Exception {
HadoopThriftDatanodeServer me = new HadoopThriftDatanodeServer(args);
me.server.serve();
}
};