/**
* @copyright 2013 Computer Science Department, Recursive InterNetworking Architecture (RINA) laboratory, Boston University.
* All rights reserved. Permission to use, copy, modify, and distribute this software and its documentation
* for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all
* copies and that both the copyright notice and this permission notice appear in supporting documentation.
* The RINA laboratory of the Computer Science Department at Boston University makes no
* representations about the suitability of this software for any purpose.
* @author Yuefeng Wang. Computer Science Department, Boston University
*/
/**
* This is a component of the IPC Process that responds to allocation Requests from Application Processes
* @author Yuefeng Wang. Computer Science Department, Boston University
* @version 1.0
*/
package rina.tcp;
import java.util.LinkedHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import rina.config.RINAConfig;
import rina.dns.DNSMessage;
import rina.irm.util.HandleEntry;
import rina.message.CDAP;
import rina.message.DTP;
import rina.object.gpb.DNS;
import rina.object.gpb.IDDMessage_t.iddMessage_t;
import rina.object.internal.IDDRecord;
import rina.rib.impl.RIBImpl;
import rina.tcp.util.IncomingCom;
import rina.tcp.util.IncomingComControl;
import rina.tcp.util.IncomingComData;
import rina.tcp.util.OutgoingComHandler;
import rina.tcp.util.TCPFlowAllocated;
import rina.tcp.util.WireListenerTCP;
import rina.tcp.util.WellKnownRINAAddr.DirectoryEntry;
import com.google.protobuf.InvalidProtocolBufferException;
public class TCPFlowManager {
private Log log = LogFactory.getLog(this.getClass());
private RIBImpl rib = null;
private RINAConfig config = null;
private String DIFName = null;
//note for BU case, IPCName = IPCName + IPCInstance from configuration file
private String IPCName = null;
// // this is only used when talking to RINA community
// // when BU case which is using TCP as wire, IPCInstance is not used,
// //only IPC name is used(where IPCName = IPCName + IPCInstance from configuration file e.g BostonU1, BostonU2 )
// private String IPCInstance = null;
// private IncomingComControl incomingTCPConnectionControl = null;
// private IncomingComData incomingTCPConnectionData = null;
// private TCPFlow listeningTCPFlowControl = null;
// private TCPFlow listeningTCPFlowData = null;
// private int controlTCPPortID = -1;
// private int dataTCPPortID = -1;
private TCPFlowAllocated flowAllocated;
private IncomingCom incomingTCPConnection = null; // this is used when we use only one TCP flow as the wire.
private TCPFlow listeningTCPFlow = null;// this is used when we use only one TCP flow as the wire.
private int tcpPortID = -1;// this is used when we use only one TCP flow as the wire.
/**
* The following four attributes are used for DNS process provided in the rina.dns
*/
private String DNSName;
private int DNSPort;
private TCPFlow dnsFlow = null;
private LinkedHashMap<String, DNS.DNSRecord> dataBase = null;
private TCPFlow iddFlow = null;
private String IDDName = null;
private int IDDPort = -1;
/**
* Case 1:
* this is used when we use only one TCP flow as the wire.
* BU case
*/
public TCPFlowManager(RIBImpl rib)
{
this.rib = rib;
this.flowAllocated = new TCPFlowAllocated(this.rib);
this.config = (RINAConfig)this.rib.getAttribute("config");
//here IPCName is the name of one side of the wire
//it is the concatenation of IPCName + IPCInstance
this.IPCName = this.rib.getAttribute("ipcName").toString() + this.rib.getAttribute("ipcInstance").toString();
// this.registerApplicationOnWire(this.rib.getAttribute("ipcName").toString(), this.rib.getAttribute("ipcInstance").toString());
if( this.rib.getAttribute("difName") != null)
{
this.DIFName = this.rib.getAttribute("difName").toString();
}
this.DNSName = this.config.getDNSName();
this.DNSPort = this.config.getDNSPort();
//create TCP listening thread
this.tcpPortID = this.config.getTCPPort();
this.dataBase = new LinkedHashMap<String, DNS.DNSRecord>();
// if the registration to DNS failed, process stopped
if( this.registerToDNS() == false ) {System.exit(-1);}
//init IDD flow
this.IDDName = this.config.getIDDName();
this.IDDPort = this.config.getIDDPort();
try {
this.iddFlow = new TCPFlow(this.IDDName, this.IDDPort);
this.log.debug("Flow to IDD created");
} catch (Exception e1) {
this.log.error(e1.getMessage());
e1.printStackTrace();
}
try {
this.listeningTCPFlow = new TCPFlow(this.tcpPortID);
this.incomingTCPConnection = new IncomingCom(this.listeningTCPFlow, this.flowAllocated);
} catch (Exception e) {
// TODO Auto-generated catch block
//TESTME
this.log.error(e.getMessage());
System.exit(-1);
}
this.initWire();
}
/**
* This is to set up the wire connectivity by initializing tcp connections.
*/
private void initWire() {
// // wait some time for others to bootstrap
// try {
// Thread.sleep(5000);
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
boolean stop = true;
int i =1;
while(stop)
{
String neihbour = this.config.getNeighbour(i);
if(neihbour == null)
{
stop = false;
}else
{
this.allocateWire(neihbour.trim());
i++;
}
}
this.log.info("IPC Process "+this.IPCName + " : init Connection done");
}
// /**
// * This is used to debug with RINA community (Shim layer)
// * @param controlTCPPortID
// * @param dataTCPPortID
// */
// public TCPFlowManager(String apName, String apInstance,int controlTCPPortID, int dataTCPPortID)
//
// {
// this.IPCName = apName;
// this.IPCInstance = apInstance;
//
// this.controlTCPPortID = controlTCPPortID;
// this.dataTCPPortID = dataTCPPortID;
//
// this.log.debug("apName:" + apName + ",apInstance:" +apInstance
// + ",controlTCPPortID:" + controlTCPPortID + ",dataTCPPortID:" +dataTCPPortID);
//
//
// this.flowAllocated = new TCPFlowAllocated(this.IPCName,this.IPCInstance);
//
// try {
// this.listeningTCPFlowControl = new TCPFlow(this.controlTCPPortID);
// this.listeningTCPFlowData = new TCPFlow(this.dataTCPPortID);
//
// this.incomingTCPConnectionControl = new IncomingComControl(this.listeningTCPFlowControl, this.flowAllocated);
// this.incomingTCPConnectionData = new IncomingComData(this.listeningTCPFlowData, this.flowAllocated);
// } catch (Exception e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
//
// }
// /**
// * this one is used to debug with RINA community
// * and this function will be called from outside
// * @param srcApName
// * @param srcApInstance
// * @param srcAeName
// * @param srcAeInstance
// * @param dstApName
// * @param dstApInstance
// * @param dstAeName
// * @param dstAeInstance
// * @return
// */
// public synchronized int allocateTCPFlow(String srcApName, String srcApInstance, String srcAeName, String srcAeInstance,
// String dstApName, String dstApInstance, String dstAeName, String dstAeInstance)
// {
// int flowID = -1;
//
// if(srcAeInstance == null)
// {
// srcAeInstance = "1";
// }
//
// if(dstAeInstance == null)
// {
// dstAeInstance = "1";
// }
//
// DirectoryEntry directoryEntry = null;
//
// if(dstAeName.equals("Management")||dstAeName.equals("Control"))
// {
// directoryEntry = this.flowAllocated.getWellKnownRINAAddr().getManagementEntry(dstApName, dstApInstance);
// }else if (dstAeName.equals("Data Transfer") || dstAeName.equals("Data"))
// {
// directoryEntry = this.flowAllocated.getWellKnownRINAAddr().getDataEntry(dstApName, dstApInstance);
// }
//
// directoryEntry.print();
//
// String srcName = srcApName+srcApInstance+srcAeName+srcAeInstance;
// String dstName = dstApName+dstApInstance+dstAeName+dstAeInstance;
//
// flowID = this.allocateTCPFlow(srcName,dstName, directoryEntry.getHostIP(), directoryEntry.getPort());
//
// if(flowID ==-1)
// {
// return -1;
// }
//
// if(dstAeName.equals("Management") || dstAeName.equals("Control")) //this is a control flow
// {
// CDAP.CDAPMessage M_CONNECT = rina.message.CDAPMessageGenerator.generateM_CONNECT
// (
// dstAeInstance,//destAEInst
// dstAeName,//destAEName
// dstApInstance,//destApInst
// dstApName,//destApName
// 15,
// srcAeInstance,//srcAEInst
// srcAeName,//srcAEName
// srcApInstance,//srcApInst
// srcApName//srcApName
// );
//
//
//
// DTP dtp_first = new DTP(M_CONNECT);
//
// try {
// this.send(flowID, dtp_first.toBytes());
// this.log.info("first DTP message containing a M_CONNECT sent out over a management flow");
//
// } catch (Exception e) {
// this.log.error(e.getMessage());
// }
//
// //wait a M_CONNECT R then it is all set
//
// byte[] msg = this.receive(flowID);
//
// DTP dtp = new DTP(msg);
//
// CDAP.CDAPMessage cdapMessage = null;
//
// try {
// cdapMessage = CDAP.CDAPMessage.parseFrom( dtp.getPayload());
//
// } catch (InvalidProtocolBufferException e) {
//
// this.log.error(e.getMessage());
//
// if(flowID != -1)
// {
// this.flowAllocated.receive(flowID);
// return -1;
// }
// }
//
//
// if(cdapMessage.getOpCode().toString().equals("M_CONNECT_R") && cdapMessage.getResult() == 0)
// {
// this.log.info("M_CONNECT_R recived over flow " + flowID);
// this.log.info("TCP flow allocated successful between " + srcName + " and " + dstName + " with flow ID " + flowID);
// }else
// {
// this.log.info("TCP flow allocated failed between " + srcName + " and " + dstName + " with flow ID " + flowID);
//
// if(flowID != -1)
// {
// this.flowAllocated.receive(flowID);
// }
// return -1;
// }
//
// }else if(dstAeName.equals("Data Transfer") ||dstAeName.equals("Data") )//this.is a data flow
// {
// //send a data DTP message to the other side
//
// int srcRINAAddr = this.flowAllocated.getWellKnownRINAAddr().getRINAAddr();
// int dstRINAAddr = this.flowAllocated.getWellKnownRINAAddr().getRINAAddr(dstApName, dstApInstance);
//
// this.log.debug("srcRINAAddr " + srcRINAAddr);
// this.log.debug("dstRINAAddr " + dstRINAAddr);
//
//
// DTP first_dtp = new DTP ( (short)srcRINAAddr, (short)srcRINAAddr, (short)0, (short)0, (byte)0xC1);
//
//
// try {
// this.send(flowID, first_dtp.toBytes());
// } catch (Exception e) {
// // TODO Auto-generated catch block
// this.log.error(e.getMessage());
// }
// }
//
// return flowID;
// }
/**
* this one is only called by method inside this class, not from outside
* @param srcName
* @param dstName
* @param ipAddr
* @param portID
* @return
*/
private synchronized int allocateTCPFlow(String srcName, String dstName, String ipAddr, int portID)
{
TCPFlow tcpFlow = null;
int flowID = -1;
String ip = ipAddr;
int tcpPort = portID;
//attach a handler thread for this flow to receive msg and put it in the msgQueue
try{
tcpFlow = new TCPFlow(ip, tcpPort);
tcpFlow.setSrcName(srcName);
tcpFlow.setDstName(dstName);
flowID = this.flowAllocated.addTCPFlow(srcName+dstName, tcpFlow);
new OutgoingComHandler(tcpFlow).start();
this.log.info("TCP flow allocated between " + srcName + " and " + dstName + " with flow ID " + flowID
+ ", ipAddr is " + ipAddr + " and portID is " + portID + ". Next send M_CONNECT");
}catch(Exception e)
{
this.log.error(e.getMessage());
this.log.error("TCP flow allocated failed between " + srcName + " and " + dstName );
if(flowID != -1)
{
this.flowAllocated.removeTCPFlow(flowID);
}
return -1;
}
return flowID;
}
public synchronized void deallocateTCPFlow(int flowID){}
/**
* For BU case, flowID is the wireID
* @param flowID
* @param msg
* @throws Exception
*/
public synchronized void send (int flowID, byte[]msg) throws Exception
{
this.flowAllocated.send(flowID, msg);
}
public byte[] receive(int flowID)
{
return this.flowAllocated.receive(flowID);
}
/**
* This is used for BU case where multiple flows are mapping on a wire
* @param flowID
* @return
*/
public byte[] receive(int wireID, int portID)
{
return this.flowAllocated.receive(wireID, portID);
}
/**
* Allocate a wire
* Here IPCName = apName + apInstance
* @param IPCName
* @return
*/
private synchronized TCPFlow allocateWire(String IPCName)
{
TCPFlow tcpFlow = null;
if(this.flowAllocated.hasTCPFlow(IPCName))
{
tcpFlow = this.flowAllocated.getTCPFlow(IPCName);
this.log.info("Wire to " + IPCName + " allocated before.");
return tcpFlow;
}
if(!this.dataBase.containsKey(IPCName))
{
this.queryDNS(IPCName);
}
DNS.DNSRecord dr = this.dataBase.get(IPCName);
String ip = dr.getIp();
int tcpPort = dr.getPort();
if(ip.equals(" "))
{
this.log.error(this.IPCName + ": " + IPCName + " is not found on DNS Server" );
return null;
}
try {
tcpFlow = new TCPFlow(ip, tcpPort);
} catch (Exception e1) {
this.log.error(e1.getMessage());
return null;
}
tcpFlow.setSrcName(this.IPCName);//this.IPCName is the name of host IPC which contains this TCP Manager
tcpFlow.setDstName(IPCName); // IPCName is a local variable only in this method
//here flowID is the wireID
int flowID = -1;
flowID = this.flowAllocated.addTCPFlow(IPCName, tcpFlow);
//generate flow ID
//should get a message back from the other side about his IPC Name
// but for now we omit this part FIXME
//send a message to the other side tells which IPC it is
// here we just make it a byte[]
byte [] first_msg = this.IPCName.getBytes();
try {
tcpFlow.send(first_msg);
} catch (Exception e) {
// TODO Auto-generated catch block
//remove this flow from flowAllocated
if(flowID != -1)
{
this.flowAllocated.removeTCPFlow(flowID);
}
this.log.error(e.getMessage());
this.log.error(this.IPCName + ": New wire to " + this.IPCName + " failed");
return null;
}
this.log.info(this.IPCName + ": New wire is added to " + IPCName + ", flowID is " + flowID);
//attach a wireListener thread for this flow to receive msg and put it in the msgQueue
try
{
//new OutgoingComHandler(tcpFlow).start();
this.flowAllocated.addWireListener(flowID, new WireListenerTCP(tcpFlow, this.flowAllocated));
}catch(Exception e)
{
this.log.error(e.getMessage());
return null;
}
return tcpFlow;
}
/**
* used for a wire in BU case
* @return
*/
private boolean registerToDNS()
{
try {
this.dnsFlow = new TCPFlow(this.DNSName, this.DNSPort);
DNS.DNSRecord register = DNSMessage.generateDNS_REG(this.IPCName,this.tcpPortID);
this.dnsFlow.send(register.toByteArray());
} catch (Exception e1) {
this.log.error(e1.getMessage());
this.log.error( this.IPCName + ":Registration to DNS failed. Process stopped");
return false;
}
this.log.info( this.IPCName + ":Registration to DNS successed");
return true;
}
/**
* used for allocating a wire in BU case
* @param IPCName
*/
private void queryDNS(String IPCName)
{
DNS.DNSRecord query = DNSMessage.generateDNS_QUERY(IPCName);
try {
this.dnsFlow.send(query.toByteArray());
} catch (Exception e2) {
// TODO Auto-generated catch block
//e2.printStackTrace();
this.log.error(e2.getMessage());
}
byte[] reply = null;
try {
reply = dnsFlow.receive();
} catch (Exception e1) {
// TODO Auto-generated catch block
//e1.printStackTrace();
this.log.error(e1.getMessage());
}
DNS.DNSRecord dnsMessage = null;
try {
dnsMessage = DNS.DNSRecord.parseFrom(reply);
} catch (InvalidProtocolBufferException e) {
e.printStackTrace();
}
this.dataBase.put(IPCName, dnsMessage);
this.log.info( this.IPCName + ":DNS Query of " + IPCName + " finished");
}
public synchronized int getWireID(String name)
{
return this.flowAllocated.getTCPFlowID(name);
}
public synchronized int addDIF0FlowOnWire(int wireID, HandleEntry he)
{
return this.flowAllocated.addDIF0FlowOnWire(wireID, he);
}
public synchronized void removeDIF0FlowOnWire(int wireID, HandleEntry he) {
this.flowAllocated.removeDIF0FlowOnWire(wireID, he);
}
public IDDRecord queryIDD(iddMessage_t iddRequestMsg)
{
IDDRecord iddRecord = null;
try {
this.iddFlow.send(iddRequestMsg.toByteArray());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
byte[] reply = null;
try {
reply = this.iddFlow.receive();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
iddMessage_t iddResponseMsg = null;
try {
iddResponseMsg = iddMessage_t.parseFrom(reply);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
this.log.debug("IDD Repsonse message recevied with result " + iddResponseMsg.getResult());
if(iddResponseMsg.getResult() == 0) //true
{
iddRecord = new IDDRecord(iddResponseMsg);
}else
{
return null;
}
return iddRecord;
}
public void registerToIDD(iddMessage_t iddRegMsg) {
try {
this.iddFlow.send(iddRegMsg.toByteArray());
this.log.debug("IDD REG message sent");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
// /**
// * This is used to register application on the wire in BU case
// * one for Control AE, and one for Data AE
// * @param ApName
// * @param ApInstance
// */
// private void registerApplicationOnWire(String ApName, String ApInstance) {
//
// this.flowAllocated.registerApplicationOnWire(ApName,ApInstance);
//
// }
}