/* * Copyright (c) 2015 Huawei, Inc and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.usc.plugin; import java.net.SocketAddress; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.local.LocalChannel; import org.opendaylight.usc.manager.monitor.evt.UscChannelErrorEvent; import org.opendaylight.usc.manager.monitor.evt.UscSessionErrorEvent; import org.opendaylight.usc.manager.monitor.evt.UscSessionTransactionEvent; import org.opendaylight.usc.plugin.exception.UscChannelException; import org.opendaylight.usc.plugin.exception.UscConnectionException; import org.opendaylight.usc.plugin.exception.UscSessionException; import org.opendaylight.usc.plugin.model.UscChannelImpl; import org.opendaylight.usc.plugin.model.UscSessionImpl; import org.opendaylight.usc.protocol.UscControl; import org.opendaylight.usc.protocol.UscData; import org.opendaylight.usc.protocol.UscError; import org.opendaylight.usc.protocol.UscFrame; import org.opendaylight.usc.protocol.UscHeader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.SettableFuture; /** * This class handles the demultiplexing of the raw USC traffic into the * respective client sessions. */ @Sharable public class UscDemultiplexer extends SimpleChannelInboundHandler<UscFrame> { private static final Logger LOG = LoggerFactory.getLogger(UscDemultiplexer.class); public final ConcurrentMap<SocketAddress, SettableFuture<Throwable>> promiseMap = new ConcurrentHashMap<>(); private final UscPlugin plugin; /** * Constructs a new UscDemultiplexer * * @param plugin * The instance of UscPlugin on whose behalf this demultiplexer * is managing session state. */ public UscDemultiplexer(UscPlugin plugin) { this.plugin = plugin; } @Override protected void channelRead0(ChannelHandlerContext ctx, UscFrame frame) throws Exception { LOG.trace("UscDemultiplexer.channelRead: " + frame); if(frame instanceof UscControl) { UscControl controlMsg = (UscControl)frame; if(controlMsg.getControlCode() == UscControl.ControlCode.ECHO) { SocketAddress remoteAddress = ctx.channel().remoteAddress(); promiseMap.get(remoteAddress).set(new Throwable("Success")); LOG.trace("channelRead0: promiseMap = " + promiseMap); return; } } final UscHeader header = frame.getHeader(); final int sessionId = header.getSessionId(); final UscChannelImpl connection = ctx.channel().attr(UscPlugin.CHANNEL).get(); final UscSessionImpl session = connection.getSession(sessionId); final LocalChannel serverChannel = session.getServerChannel(); if (frame instanceof UscError) { // propagate exception to the client channel UscSessionException ex = new UscSessionException(((UscError) frame).getErrorCode()); serverChannel.writeAndFlush(ex); plugin.sendEvent(new UscSessionErrorEvent(session, ex)); } else if (frame instanceof UscData) { if (serverChannel != null) { LOG.trace("write session " + sessionId + " to " + serverChannel + ": " + frame.getPayload()); ByteBuf payload = frame.getPayload(); plugin.sendEvent(new UscSessionTransactionEvent(session, payload.readableBytes(), 0)); serverChannel.writeAndFlush(payload); } else { UscChannelException ex = new UscChannelException("write unknown session " + sessionId + "; discard"); plugin.sendEvent(new UscChannelErrorEvent(session.getChannel(), ex)); throw ex; } } else if(frame instanceof UscControl) { UscControl controlMsg = (UscControl)frame; Channel clientChannel = serverChannel.attr(UscPlugin.CLIENT_CHANNEL).get(); if(controlMsg.getControlCode() == UscControl.ControlCode.TERMINATION_REQUEST) { LOG.trace("UscDemultiplexer received control message TERMINATION_REQUEST"); clientChannel.close(); // send back TERMINATION_RESPONSE UscControl data = new UscControl(session.getPort(), session.getSessionId(), 2); ctx.channel().writeAndFlush(data); } else if(controlMsg.getControlCode() == UscControl.ControlCode.TERMINATION_RESPONSE) { LOG.trace("UscDemultiplexer received control message TERMINATION_RESPONSE"); if(clientChannel != null) { SettableFuture<Boolean> status = plugin.getCloseFuture().get(clientChannel); status.set(true); LOG.trace("UscDemultiplexer: termination status is " + status.get()); } } } else { LOG.trace("UscDemultiplexer.channelRead: unexpected UscFrame object " + frame); UscChannelException ex = new UscChannelException("unexpected UscFrame object " + frame); plugin.sendEvent(new UscChannelErrorEvent(session.getChannel(), ex)); throw ex; } } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { LOG.trace("UscDemultiplexer channelInactive()"); UscConnectionException ex = new UscConnectionException("The channel is closed."); throw ex; } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { SocketAddress remoteAddress = ctx.channel().remoteAddress(); promiseMap.get(remoteAddress).set(cause); LOG.trace("exceptionCaught: promiseMap = " + promiseMap); if (cause instanceof UscChannelException) { LOG.trace("UscDemultiplexer exceptionCaught()"); final UscChannelImpl connection = ctx.channel().attr(UscPlugin.CHANNEL).get(); for (UscSessionImpl session : connection.getAllSessions()) { final LocalChannel serverChannel = session.getServerChannel(); if (serverChannel != null) { LOG.trace("UscDemultiplexer exceptionCaught() and write to the server channel."); serverChannel.writeAndFlush(cause); } } } } }