/** * Remove all references to the channel. 1) the map of a channel (socket) to its interests. 2) the * map of interests to its channels. * * <p>As each interest is removed inform all channels having a similar interest. * * @param sourceCtx */ public void unregister(final ChannelHandlerContext sourceCtx) { final SocketAddress key = sourceCtx.channel().remoteAddress(); majorLogger.info("unregister channel {}", key); final Interest.ChannelInterestSet channelInterestSet = this.channelInterestMap.remove(key); if (channelInterestSet == null) { majorLogger.warn( "nothing to unregister in interest map: {}", this.channelInterestMap.keySet()); return; } final String trackingGuid = UUID.randomUUID().toString(); majorLogger.warn("no interested channel set"); for (final Interest interest : channelInterestSet.getInterestList()) { if (!this.interestedChannelMap.containsKey(interest)) { continue; } final Interest.InterestedChannelSet interestedChannelSet = this.interestedChannelMap.get(interest); interestedChannelSet.removeItem(sourceCtx, interest); majorLogger.info("unregistered interest {}", interest); // this.interestedChannelMap.put(interest, interestedChannelSet); // an implicit DISINTEREST message will be sent to all interested channels final MetaLinkMsg.Edit.Builder editBuilder = MetaLinkMsg.Edit.newBuilder() .addMode(MetaLinkMsg.Edit.EditMode.DISINTEREST) .setEditMode(MetaLinkMsg.Edit.EditMode.DISINTEREST) .setGuid(trackingGuid) .setSequence(sequenceNumber.incrementAndGet()) .addAllTopic(interest.getTopic()) .addOrigin(sourceCtx.channel().remoteAddress().toString()) .addOrigin(METALINK_BRIDGE_NAME); final MetaLinkMsg.Edit disinterestMsg = editBuilder.build(); majorLogger.trace("expressing disinterest to all channels except source"); for (final Map.Entry<SocketAddress, Interest.ChannelInterestSet> entry : this.channelInterestMap.entrySet()) { final ChannelHandlerContext ctx = entry.getValue().context; if (isSameChannel(ctx, sourceCtx)) { continue; } majorLogger.trace("disinterest {} to {}", trackingGuid, ctx.channel().remoteAddress()); ctx.write(disinterestMsg); ctx.flush(); detailLogger.info("message sent:\n{}", disinterestMsg); } } }
/** * Send the data to all channels except the incoming channel. * * <p>Write the message to the recorder file for later study and playback. This involves * converting the binary payload to a textual representation. * * <p>If the inbound message is an INTEREST message then it is not sent. Instead it is responded * to in a way indicated by the ?. * * @param sourceCtx * @param originalMsg */ public void distribute(ChannelHandlerContext sourceCtx, final MetaLinkMsg.Edit originalMsg) { majorLogger.trace( "incoming message to distribute(): {}\n {}", sourceCtx.channel().remoteAddress(), originalMsg); if (originalMsg == null) { majorLogger.warn("attempt to distribute null message"); return; } try { final SyntheticData.Recorder recorder = this.recorder.get(); final MetaLinkMsg.Edit.Builder editBuilder = originalMsg.toBuilder(); final MetaLinkMsg.Edit msg; if (originalMsg.getModeCount() < 1) { if (originalMsg.hasEditMode()) { editBuilder.addMode(originalMsg.getEditMode()); msg = editBuilder.build(); } else { majorLogger.warn("no edit mode provided, assuming POST"); editBuilder.addMode(MetaLinkMsg.Edit.EditMode.POST); editBuilder.setEditMode(MetaLinkMsg.Edit.EditMode.POST); msg = editBuilder.build(); } } else { if (originalMsg.hasEditMode()) { if (originalMsg.getEditMode() == originalMsg.getMode(originalMsg.getModeCount() - 1)) { msg = editBuilder.build(); } else { majorLogger.info("edit mode mismatch, set the last mode"); editBuilder.addMode(originalMsg.getEditMode()); msg = editBuilder.build(); } } else { majorLogger.info("edit mode provided, using the last mode"); editBuilder.setEditMode(originalMsg.getMode(originalMsg.getModeCount() - 1)); msg = editBuilder.build(); } } if (null != recorder) { recorder.record(sourceCtx, msg); } final String trackingGuid = msg.hasGuid() ? msg.getGuid().toString() : UUID.randomUUID().toString(); final String origin = (msg.getOriginCount() > 0) ? msg.getOrigin(0) : ""; switch (msg.getEditMode()) { case NOTICE: { final Interest topic = new Interest(msg.getTopicList()); final Interest.InterestedChannelSet channelSet = this.interestedChannelMap.get(topic); if (channelSet == null) { majorLogger.warn("no interested channel set"); editBuilder .addMode(MetaLinkMsg.Edit.EditMode.NOTICE) .setEditMode(MetaLinkMsg.Edit.EditMode.NOTICE) .addOrigin(METALINK_BRIDGE_NAME) .setSequence(sequenceNumber.incrementAndGet()); final MetaLinkMsg.Notice.Builder noticeBuilder = MetaLinkMsg.Notice.newBuilder() .setNoticeMode(MetaLinkMsg.Notice.NoticeMode.ACK) .setCode(NO_INTEREST_PRESENT) .setMsg("no interest in topic") .setId(msg.getGuid()); editBuilder.addNotices(noticeBuilder); final MetaLinkMsg.Edit updatedEdit = editBuilder.build(); majorLogger.trace( "notice no-interest notice {} to {}", trackingGuid, sourceCtx.channel().remoteAddress()); sourceCtx.write(updatedEdit); sourceCtx.flush(); detailLogger.info("update edit\n{}", updatedEdit); if (null != recorder) { recorder.record(sourceCtx, updatedEdit); } break; } majorLogger.trace("posting notice to all interested channels except source"); boolean anyInterest = false; for (final ChannelHandlerContext ctx : channelSet.getContextSet()) { if (isSameChannel(ctx, sourceCtx)) { continue; } anyInterest = true; majorLogger.trace( "notice forward {} to {}", trackingGuid, ctx.channel().remoteAddress()); ctx.write(msg); ctx.flush(); detailLogger.info("message sent:\n{}", msg); } if (!anyInterest) { editBuilder .addMode(MetaLinkMsg.Edit.EditMode.NOTICE) .setEditMode(MetaLinkMsg.Edit.EditMode.NOTICE) .addOrigin(METALINK_BRIDGE_NAME) .setSequence(sequenceNumber.incrementAndGet()); final MetaLinkMsg.Notice.Builder noticeBuilder = MetaLinkMsg.Notice.newBuilder() .setNoticeMode(MetaLinkMsg.Notice.NoticeMode.ACK) .setCode(NO_INTEREST_PRESENT) .setMsg("no pending interest for topic") .setId(msg.getGuid()); editBuilder.addNotices(noticeBuilder); final MetaLinkMsg.Edit updatedEdit = editBuilder.build(); majorLogger.trace( "notice no interest {} to {}", trackingGuid, sourceCtx.channel().remoteAddress()); sourceCtx.write(updatedEdit); sourceCtx.flush(); detailLogger.info("message sent:\n{}", updatedEdit); majorLogger.trace("cannot post, no other interest \n{}", updatedEdit); if (null != recorder) { recorder.record(sourceCtx, updatedEdit); } } break; } case POST: { final Interest topic = new Interest(msg.getTopicList()); final Interest.InterestedChannelSet channelSet = this.interestedChannelMap.get(topic); if (channelSet == null || channelSet.size() == 0) { majorLogger.warn("no interested channel set"); editBuilder .addMode(MetaLinkMsg.Edit.EditMode.NOTICE) .setEditMode(MetaLinkMsg.Edit.EditMode.NOTICE) .addOrigin(METALINK_BRIDGE_NAME) .setSequence(sequenceNumber.incrementAndGet()); final MetaLinkMsg.Notice.Builder noticeBuilder = MetaLinkMsg.Notice.newBuilder() .setNoticeMode(MetaLinkMsg.Notice.NoticeMode.ACK) .setCode(NO_INTEREST_PRESENT) .setMsg("no interest in the posted topic") .setId(msg.getGuid()); editBuilder.addNotices(noticeBuilder); final MetaLinkMsg.Edit updatedEdit = editBuilder.build(); majorLogger.trace( "post no-interest {} to {}", trackingGuid, sourceCtx.channel().remoteAddress()); sourceCtx.write(updatedEdit); sourceCtx.flush(); detailLogger.info("message sent:\n{}", updatedEdit); if (null != recorder) { recorder.record(sourceCtx, updatedEdit); } break; } else { majorLogger.trace("posting edit to all interested channels except source"); for (final ChannelHandlerContext ctx : channelSet.getContextSet()) { if (isSameChannel(ctx, sourceCtx)) { continue; } majorLogger.trace("post all {} to {}", trackingGuid, ctx.channel().remoteAddress()); ctx.write(msg); ctx.flush(); detailLogger.info("message sent:\n{}", msg); } } /* if (! anyInterest) { editBuilder .addMode(MetaLinkMsg.Edit.EditMode.NOTICE) .setEditMode(MetaLinkMsg.Edit.EditMode.NOTICE) .addOrigin(METALINK_BRIDGE_NAME) .setSequence(sequenceNumber.incrementAndGet()); final MetaLinkMsg.Notice.Builder noticeBuilder = MetaLinkMsg.Notice.newBuilder() .setNoticeMode(MetaLinkMsg.Notice.NoticeMode.ACK) .setCode(NO_INTEREST_PRESENT) .setMsg("no pending interest for topic") .setId(msg.getGuid()); editBuilder.addNotices(noticeBuilder); final MetaLinkMsg.Edit updatedEdit = editBuilder.build(); majorLogger.trace("post no interest {} to {}", trackingGuid, sourceCtx.channel().remoteAddress()); sourceCtx.write(updatedEdit); sourceCtx.flush(); detailLogger.info("message sent:\n{}", updatedEdit); majorLogger.trace("cannot post, no other interest \n{}", updatedEdit); if (null != recorder) { recorder.record(sourceCtx, updatedEdit); } } */ break; } case INTEREST: { final Interest interest = new Interest(msg.getTopicList()); playback(sourceCtx, interest); majorLogger.trace("determine if there is a client which already is interested?"); final Interest.InterestedChannelSet ics = this.interestedChannelMap.get(interest); boolean anyInterest = false; if (ics != null) { for (final ChannelHandlerContext ctx : ics.getContextSet()) { if (isSameChannel(ctx, sourceCtx)) { continue; } anyInterest = true; majorLogger.trace( "interest in {} from {} to {}", trackingGuid, ctx.channel().remoteAddress(), sourceCtx.channel().remoteAddress()); sourceCtx.write(msg); sourceCtx.flush(); detailLogger.info("message sent:\n{}", msg); } } if (!anyInterest) { editBuilder .addMode(MetaLinkMsg.Edit.EditMode.NOTICE) .setEditMode(MetaLinkMsg.Edit.EditMode.NOTICE) .addOrigin(METALINK_BRIDGE_NAME) .setSequence(sequenceNumber.incrementAndGet()); final MetaLinkMsg.Notice.Builder noticeBuilder = MetaLinkMsg.Notice.newBuilder() .setNoticeMode(MetaLinkMsg.Notice.NoticeMode.ACK) .setCode(NO_INTEREST_PRESENT) .setMsg("no pending interest for topic") .setId(msg.getGuid()); editBuilder.addNotices(noticeBuilder); final MetaLinkMsg.Edit updatedEdit = editBuilder.build(); majorLogger.trace( "interest notice {} to {}", trackingGuid, sourceCtx.channel().remoteAddress()); sourceCtx.write(updatedEdit); sourceCtx.flush(); detailLogger.info("message sent:\n{}", updatedEdit); majorLogger.trace("no other interest"); if (null != recorder) { recorder.record(sourceCtx, updatedEdit); } } setInterest(sourceCtx, interest); majorLogger.trace("interest recorded"); majorLogger.trace("expressing interest to all channels except source"); for (final Map.Entry<SocketAddress, Interest.ChannelInterestSet> entry : this.channelInterestMap.entrySet()) { final ChannelHandlerContext ctx = entry.getValue().context; if (isSameChannel(ctx, sourceCtx)) { continue; } majorLogger.trace("interest {} to {}", trackingGuid, ctx.channel().remoteAddress()); ctx.write(msg); ctx.flush(); detailLogger.info("message sent:\n{}", msg); } break; } case DISINTEREST: { final Interest interest = new Interest(msg.getTopicList()); majorLogger.trace("expressing disinterest to all channels except source"); for (final Map.Entry<SocketAddress, Interest.ChannelInterestSet> entry : this.channelInterestMap.entrySet()) { final ChannelHandlerContext ctx = entry.getValue().context; if (isSameChannel(ctx, sourceCtx)) { continue; } majorLogger.trace( "disinterest {} to {}", trackingGuid, ctx.channel().remoteAddress()); ctx.write(msg); ctx.flush(); detailLogger.info("message sent:\n{}", msg); } setDisinterest(sourceCtx, interest); } } } catch (Exception ex) { majorLogger.warn("problem distributing message", ex); } majorLogger.info("MESSAGE PROCESSED"); }