/**
* @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.
*/
package rina.flowAllocator.impl;
import java.util.LinkedHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import rina.flowAllocator.api.FlowAllocatorInstance;
import rina.irm.impl.IRMImpl;
import rina.message.CDAP;
import rina.message.CDAP.CDAPMessage;
import rina.object.gpb.ApplicationProcessNamingInfoMessage_t.applicationProcessNamingInfo_t;
import rina.object.gpb.Flow_t.connectionId_t;
import rina.object.gpb.Flow_t.flow_t;
import rina.object.internal.ApplicationProcessNamingInfo;
import rina.object.internal.DirectoryForwardingTable;
import rina.object.internal.Flow;
import rina.object.internal.ForwardingTable;
import rina.object.internal.Neighbor;
import rina.object.internal.Neighbors;
import rina.rib.impl.RIBImpl;
import rina.util.MessageQueue;
import com.google.protobuf.ByteString;
/**
* Flow Allocator Instance Impl
* @author Yuefeng Wang. Computer Science Department, Boston University
*
*/
public class FlowAllocatorInstanceImpl extends Thread implements FlowAllocatorInstance {
private Log log = LogFactory.getLog(this.getClass());
private String IPCName = null;
private String IPCInstance = null;
private Flow flow = null;
private RIBImpl rib = null;
private IRMImpl irm = null;
private boolean listen = true;
private int portID = -1;
private MessageQueue msgQueue = null;
private DirectoryForwardingTable directoryForwardingTable = null;
private Neighbors neighbors = null;
private ForwardingTable forwardingTable = null;
//this one is used to notify back to the call in Flow Allocator
// about whether the flow allocation is successful not
private MessageQueue notifyToRequst = null;
public FlowAllocatorInstanceImpl(){}
public FlowAllocatorInstanceImpl(RIBImpl rib, IRMImpl irm, MessageQueue notify)
{
this.rib = rib;
this.irm = irm;
this.notifyToRequst = notify;
this.IPCName = (String) this.rib.getAttribute("ipcName");
this.IPCInstance = (String)this.rib.getAttribute("ipcInstance");
this.directoryForwardingTable = (DirectoryForwardingTable)this.rib.getAttribute("directoryForwardingTable");
this.neighbors = (Neighbors)this.rib.getAttribute("neighbors");
this.forwardingTable = (ForwardingTable)this.rib.getAttribute("forwardingTable");
}
public void run()
{
while(this.listen)
{
byte[] msg = this.msgQueue.getReceive();
CDAP.CDAPMessage cdapMessage = null;
try {
cdapMessage = CDAP.CDAPMessage.parseFrom(msg);
} catch (Exception e) {
this.log.error(e.getMessage());
}
this.processCDAPMessage(cdapMessage);
}
}
private void processCDAPMessage(CDAPMessage cdapMessage) {
// TODO Auto-generated method stub
}
///this is to handle the flow request, and M_CREATE_R is also handled here
/**
* Flow allocator got the request from application
*/
public void submitAllocationRequest(Flow flowRequest) {
this.flow = flowRequest;
this.portID = (int) this.flow.getSrcPortID();
this.msgQueue = ( ( LinkedHashMap<Integer, MessageQueue> ) this.rib.getAttribute("flowQueues") ).get(this.portID);
ApplicationProcessNamingInfo apInfo = flow.getDstApInfo();
String apName = apInfo.getApName();
String apInstance = apInfo.getApInstance();
String name = null;
if(apInstance == null) //sometimes only apName is used, for example: applications users write
{
name = apName;
}else //sometimes both apName and aeName are used, for example: IPC process
{
name = apName + apInstance;
}
//this address needs to be "translated" to the IPC name (IPC apName, IPC apInstance )
long dstAddr = this.directoryForwardingTable.getAddress(name);
if(dstAddr == -1)
{
this.log.info( "No IPC address found for " + name + ", flow allocation failed");
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
return;
//or it can query DIF manager
}else
{
this.flow.setDstAddr(dstAddr);
// System.out.println("ddddddddddddddstAddr is " + dstAddr);
// System.out.println("this.forwardingTable is " + this.forwardingTable);
//find next hop of dst ipc
//note this dstAddr must be cast to int, otherwise there will be error since forwarding table is long type
long nextHop = this.forwardingTable.getNextHop((int)dstAddr);
// this.log.debug("next hop of " + dstAddr + " is " + nextHop);
Neighbor neighbor = this.neighbors.getBeighbor(nextHop);
if(neighbor == null)
{
this.log.info("Next hop does not exist for dstAddr " + dstAddr );
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
return;
}
String dstIPCName = neighbor.getApName();
String dstIPCInstance = neighbor.getApInstance();
this.log.info( " IPC address found for " + name + ":" + dstAddr +". IPC info:" + dstIPCName + "/" + dstIPCInstance );
//Talk to dst(or next hop's) IPC's Management AE
int handleID = this.irm.allocateFlow(this.IPCName, this.IPCInstance, "Management", "1",
dstIPCName, dstIPCInstance, "Management", "1");
this.log.debug("hanlde for CREATE(flow) is " + handleID);
ApplicationProcessNamingInfo srcInfo = flow.getSrcApInfo();
ApplicationProcessNamingInfo dstInfo = flow.getDstApInfo();
String srcApName = srcInfo.getApName();
String srcApInstance = srcInfo.getApInstance();
String srcAeName = srcInfo.getAeName();
String srcAeInstance = srcInfo.getAeInstance();
String dstApName = dstInfo.getApName();
String dstApInstance = dstInfo.getApInstance();
String dstAeName = dstInfo.getAeName();
String dstAeInstance = dstInfo.getAeInstance();
applicationProcessNamingInfo_t.Builder src = applicationProcessNamingInfo_t.newBuilder();
src.setApplicationProcessName(srcApName);
//The following three need to be checked,
//If the application calling is IPC process, then all are used
//But if it is just a regular application, then they might not be used, as only apName is required
if(srcApInstance != null)
{
src.setApplicationProcessInstance(srcApInstance);
}
if(srcAeName != null)
{
src.setApplicationEntityName(srcAeName);
}
if(srcAeInstance != null)
{
src.setApplicationEntityInstance(srcAeInstance);
}
applicationProcessNamingInfo_t.Builder dst = applicationProcessNamingInfo_t.newBuilder();
dst.setApplicationProcessName(dstApName);
//The following three need to be checked,
//If the application calling is IPC process, then all are used
//But if it is just a regular application, then they might not be used, as only apName is required
if(dstApInstance != null)
{
dst.setApplicationProcessInstance(dstApInstance);
}
if(dstAeName != null)
{
dst.setApplicationEntityName(dstAeName);
}
if(dstAeInstance !=null)
{
dst.setApplicationEntityInstance(dstAeInstance);
}
connectionId_t.Builder cid = connectionId_t.newBuilder();
cid.setQosId(1); // ignore this for now
cid.setSourceCEPId((int)flow.getSrcPortID()); // Note: src cid is the portID for simplicity
cid.setDestinationCEPId(-1); // For now the other side is unkonw so make it -1
flow_t.Builder flow_obj = flow_t.newBuilder();
flow_obj.setDestinationNamingInfo(dst.buildPartial());
flow_obj.setSourceNamingInfo(src.buildPartial());
flow_obj.setSourcePortId((int)flow.getSrcPortID());
flow_obj.setDestinationPortId(-1); // first time all dst port is -1
flow_obj.setSourceAddress(flow.getSrcAddr()); //note this is a required field
flow_obj.setDestinationAddress(flow.getDstAddr());
flow_obj.addConnectionIds(cid.buildPartial());
CDAP.objVal_t.Builder obj = CDAP.objVal_t.newBuilder();
obj.setByteval(ByteString.copyFrom(flow_obj.buildPartial().toByteArray()));
CDAP.CDAPMessage M_CREATE = rina.message.CDAPMessageGenerator.generateM_CREATE
( "flow",
"/dif/resourceallocation/flowallocator/flow/",
obj.buildPartial(),
99
);
try {
this.irm.send(handleID, M_CREATE.toByteArray());
this.log.info( " M_CREATE(flow) sent out over handleID " + handleID);
} catch (Exception e) {
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
this.log.error(e.getMessage());
this.log.info( "M_CREATE(flow) sent error");
return;
}
byte [] msg_reply = this.msgQueue.getReceive();
CDAP.CDAPMessage cdapMessage = null;
try {
cdapMessage = CDAP.CDAPMessage.parseFrom(msg_reply);
} catch (Exception e) {
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
this.log.error(e.getMessage());
this.log.error( "Error when receiving M_CREATE_R ");
return;
}
if(cdapMessage.getOpCode().toString().equals("M_CREATE_R") && cdapMessage.getResult() == 0)
{
this.log.debug("we got the M_CREATE_R(flow) success");
}else
{
this.log.debug("we got the M_CREATE_R(flow) fail");
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
return;
}
CDAP.objVal_t objValue = null;
flow_t flow_reply = null;
try {
objValue = cdapMessage.getObjValue();
flow_reply = flow_t.parseFrom(objValue.getByteval().toByteArray());
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
int dstPortID = (int)flow_reply.getDestinationPortId();
this.log.debug("dstPortID in the M_CREATE_R received is " + dstPortID);
int srcPortID = (int)flow_reply.getSourcePortId();
this.flow.setDstPortID(srcPortID);
this.notifyToRequst.addReceive("true".getBytes());
}
//flow allocation done, start maintaining this flow
this.start();
}
/**
* Flow allocator received request from another flow allocator
*/
public void receiveAllocationRequest(Flow flowRequest) {
this.flow = flowRequest;
this.portID = (int) this.flow.getSrcPortID();
this.msgQueue = ( ( LinkedHashMap<Integer, MessageQueue> ) this.rib.getAttribute("flowQueues") ).get(this.portID);
ApplicationProcessNamingInfo srcApInfo = this.flow.getSrcApInfo();
ApplicationProcessNamingInfo dstApInfo = this.flow.getDstApInfo();
//send M_CREATE_R back
applicationProcessNamingInfo_t.Builder src = applicationProcessNamingInfo_t.newBuilder();
applicationProcessNamingInfo_t.Builder dst = applicationProcessNamingInfo_t.newBuilder();
src.setApplicationProcessName(srcApInfo.getApName());
src.setApplicationProcessInstance(srcApInfo.getApInstance());
src.setApplicationEntityName(srcApInfo.getAeName());
src.setApplicationEntityInstance(srcApInfo.getAeInstance());
dst.setApplicationProcessName(dstApInfo.getApName());
dst.setApplicationProcessInstance(dstApInfo.getApInstance());
dst.setApplicationEntityName(dstApInfo.getAeName());
dst.setApplicationEntityInstance(dstApInfo.getAeInstance());
flow_t.Builder flow_reply = flow_t.newBuilder();
connectionId_t.Builder cid = connectionId_t.newBuilder();
cid.setQosId(1); // ignore this for now
cid.setSourceCEPId((int)this.flow.getSrcPortID()); //portID is CEPID for simplicity
cid.setDestinationCEPId((int)this.flow.getDstPortID());
flow_reply.setDestinationNamingInfo(dst.buildPartial());
flow_reply.setSourceNamingInfo(src.buildPartial());
flow_reply.setSourcePortId(this.flow.getSrcPortID());
flow_reply.setDestinationPortId(this.flow.getDstPortID()); // this is the one the other side needs
flow_reply.addConnectionIds(cid.buildPartial());
flow_reply.setSourceAddress(this.flow.getSrcAddr());
flow_reply.setDestinationAddress(this.flow.getDstAddr());
CDAP.objVal_t.Builder flowObj = CDAP.objVal_t.newBuilder();
flowObj.setByteval(ByteString.copyFrom(flow_reply.buildPartial().toByteArray()));
int result = 0; //true all the time
CDAP.CDAPMessage M_CREATE_R = rina.message.CDAPMessageGenerator.generateM_CREATE_R
( result,
"flow",
"/dif/resourceallocation/flowallocator/flow/",
flowObj.buildPartial(),
99
);
//sent M_CREATE_R to the other side's mamagement AE which will forwards result to its flow allocator
//find next hop of dst ipc
//note this dstAddr must be cast to int, otherwise there will be error since forwarding table is long type
long nextHop = this.forwardingTable.getNextHop((int)this.flow.getDstAddr());
this.log.debug("next hop of " + this.flow.getDstAddr() + " is " + nextHop);
Neighbor neighbor = this.neighbors.getBeighbor(nextHop);
if(neighbor == null)
{
this.log.info("Next hop does not exist for dstAddr " + this.flow.getDstAddr() );
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
return;
}
String dstIPCName = neighbor.getApName();
String dstIPCInstance = neighbor.getApInstance();
this.log.info( " IPC address found for dest Addr " + this.flow.getDstAddr() +". IPC info:" + dstIPCName + "/" + dstIPCInstance );
//Talk to dst IPC's Management AE
int handleID = this.irm.allocateFlow(this.IPCName, this.IPCInstance, "Management", "1",
dstIPCName, dstIPCInstance, "Management", "1");
try {
this.irm.send(handleID, M_CREATE_R.toByteArray());
this.log.info( " M_CREATE_R (flow) sent out over handleID " + handleID);
} catch (Exception e) {
this.notifyToRequst.addReceive("false".getBytes());
this.stopFAI();
this.log.error(e.getMessage());
this.log.info( "M_CREATE_R(flow) sent error");
return;
}
//just agree the flow request every time
this.notifyToRequst.addReceive("true".getBytes());
//flow allocation done, start maintaining this flow
this.start();
}
public void stopFAI()
{
this.listen = false;
}
}