package com.robonobo.mina.network; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import com.robonobo.common.exceptions.SeekInnerCalmException; import com.robonobo.common.util.TimeUtil; import com.robonobo.core.api.proto.CoreApi.EndPoint; import com.robonobo.mina.external.buffer.PageInfo; import com.robonobo.mina.instance.MinaInstance; import com.robonobo.mina.instance.StreamMgr; import com.robonobo.mina.message.MessageHolder; import com.robonobo.mina.message.proto.MinaProtocol.ReqPage; import com.robonobo.mina.message.proto.MinaProtocol.SourceStopping; import com.robonobo.mina.util.MinaConnectionException; /** * @syncpriority 120 */ public class BCPair extends ConnectionPair { static final char GAMMA = 0x03b3; private BroadcastConnection bc; private boolean isClosed; private Lock reqPageLock = new ReentrantLock(true); /** * @param pages * @syncpriority 160 */ public BCPair(MinaInstance mina, String sid, ControlConnection cc, EndPoint listenEp, List<Long> pages) { super(mina, sid, cc); try { bc = cc.getSCF().getBroadcastConnection(cc, listenEp); bc.setBCPair(this); } catch (MinaConnectionException e) { log.error("Error getting broadcast connection to talk to " + listenEp, e); die(); } // This will set our gamma cc.addBCPair(this); log.info("Starting broadcast of " + sid + " to node " + cc.getNodeId()); requestPages(pages); } public boolean isClosed() { return isClosed; } @Override public int getFlowRate() { return bc.getFlowRate(); } /** * @syncpriority 120 */ public void die() { die(true); } /** * @syncpriority 120 */ public void die(boolean sendSourceStopping) { log.info("Stopping broadcast of " + sid + " to node " + cc.getNodeId()); synchronized (this) { if (isClosed) return; isClosed = true; if (bc != null) bc.close(); } if (sendSourceStopping) cc.sendMessage("SourceStopping", SourceStopping.newBuilder().setStreamId(sid).build()); cc.removeBCPair(this); super.die(); } /** * @syncpriority 120 */ public synchronized void abort() { if (bc != null) bc.close(); } /** * @syncpriority 160 */ public void requestPages(List<Long> pages) { if (mina.getCCM().isShuttingDown()) { log.debug(this + " not requesting pages - closing down"); return; } // Bug huntin if (bc == null) throw new SeekInnerCalmException(); List<Long> failedPages = null; // Use a fair reentrant lock to make sure we don't handle reqpage requests out of order reqPageLock.lock(); try { // Get the total amount of data we want to send long totalPageLen = 0; if (mina.getConfig().isAgoric()) { for (Long pn : pages) { PageInfo pi = mina.getPageBufProvider().getPageBuf(sid).getPageInfo(pn); if (pi != null) totalPageLen += pi.getLength(); else { // We will only get here very rarely (should be never, they should never ask), so no point in // pointlessly instantiating the list 99.9% of the time if (failedPages == null) failedPages = new ArrayList<Long>(); failedPages.add(pn); if (log.isDebugEnabled()) log.debug(this + " requested page " + pn + " which I do not have"); } } } // Make sure they have enough ends to get these (if their // balance is too low, this call will cause them to be told to // pay up) int auctStatIdx = (mina.getConfig().isAgoric()) ? mina.getSellMgr().requestAndCharge(cc.getNodeId(), totalPageLen) : 0; if (auctStatIdx < 0) { // Ask again when they've paid up ReqPage rp = ReqPage.newBuilder().setStreamId(sid).addAllRequestedPage(pages).build(); MessageHolder mh = new MessageHolder("ReqPage", rp, cc, TimeUtil.now()); if (!mina.getSellMgr().haveActiveAccount(cc.getNodeId())) { mina.getSellMgr().msgPendingActiveAccount(mh); return; } if (!mina.getSellMgr().haveAgreedBid(cc.getNodeId())) { mina.getSellMgr().msgPendingAgreedBid(mh); return; } // wtf? we have an account and an agreed bid, something's not right log.error(this+" failed to charge sellmgr, but i'm not sure why!"); } else { for (Long pn : pages) { if (failedPages == null || !failedPages.contains(pn)) bc.addPageToQ(pn, auctStatIdx); } } } finally { reqPageLock.unlock(); } // If they asked us for a page we didn't have, tell them where we are in the stream if (failedPages != null) cc.sendMessage("StreamStatus", mina.getStreamMgr().buildStreamStatus(sid, cc.getNodeId())); } public void setGamma(float gamma) { if (log.isDebugEnabled()) log.debug(this + ": setting " + GAMMA + " to " + gamma); bc.setGamma(gamma); } public boolean equals(Object obj) { if (obj instanceof BCPair) return (hashCode() == obj.hashCode()); else return false; } public int hashCode() { return getClass().getName().hashCode() ^ cc.getNodeId().hashCode() ^ sid.hashCode(); } public String toString() { return "BCP[node=" + cc.getNodeId() + ",stream=" + sid + "]"; } }