/** * Copyright 2007-2015, Kaazing Corporation. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.kaazing.k3po.driver.internal.netty.bootstrap.udp; import org.jboss.netty.channel.Channel; import org.jboss.netty.channel.ChannelConfig; import org.jboss.netty.channel.ChannelHandlerContext; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineFactory; import org.jboss.netty.channel.ChannelSink; import org.jboss.netty.channel.ChannelStateEvent; import org.jboss.netty.channel.DefaultChannelConfig; import org.jboss.netty.channel.ExceptionEvent; import org.jboss.netty.channel.MessageEvent; import org.jboss.netty.channel.socket.nio.NioDatagramChannel; import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.util.Timer; import org.kaazing.k3po.driver.internal.netty.channel.ChannelAddress; import org.kaazing.k3po.driver.internal.netty.channel.SimpleChannelHandler; import org.kaazing.k3po.driver.internal.netty.channel.udp.UdpChannelAddress; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; import static org.jboss.netty.channel.Channels.fireChannelBound; import static org.jboss.netty.channel.Channels.fireChannelClosed; import static org.jboss.netty.channel.Channels.fireChannelConnected; import static org.jboss.netty.channel.Channels.fireChannelOpen; import static org.jboss.netty.channel.Channels.fireExceptionCaught; import static org.jboss.netty.channel.Channels.fireMessageReceived; import static org.kaazing.k3po.driver.internal.channel.Channels.channelAddress; import static org.kaazing.k3po.driver.internal.channel.Channels.toInetSocketAddress; /* * UdpServerChannelSink sets up NioDatagramChannel with this handler as the pipeline * * This handler creates a channel (UdpChildChannel) for each remote address * Netty doesn't have this feature yet (https://github.com/netty/netty/issues/344) */ class UdpChildChannelSource extends SimpleChannelHandler { // remote address --> child channel private final Map<SocketAddress, UdpChildChannel> childChannels = new ConcurrentHashMap<>(); final UdpServerChannel serverChannel; private final Timer timer; UdpChildChannelSource(UdpServerChannel serverChannel, Timer timer) { this.serverChannel = serverChannel; this.timer = timer; } void closeChildChannel(UdpChildChannel childChannel) { InetSocketAddress socketAddress = toInetSocketAddress(childChannel.getRemoteAddress()); childChannels.remove(socketAddress); } @Override public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { childChannels.forEach(((socketAddress, udpChildChannel) -> { fireExceptionCaught(udpChildChannel, e.getCause()); fireChannelClosed(udpChildChannel); })); childChannels.clear(); Channel channel = ctx.getChannel(); channel.close(); } @Override public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { childChannels.forEach(((socketAddress, udpChildChannel) -> udpChildChannel.close())); e.getFuture().setSuccess(); childChannels.clear(); } @Override public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { SocketAddress remoteAddress = e.getRemoteAddress(); NioDatagramChannel datagramChannel = (NioDatagramChannel) e.getChannel(); UdpChannelAddress localAddress = serverChannel.getLocalAddress(); long timeout = localAddress.timeout(); UdpChildChannel udpChildChannel = childChannels.computeIfAbsent(remoteAddress, x -> { ChannelPipelineFactory pipelineFactory = serverChannel.getConfig().getPipelineFactory(); ChannelPipeline pipeline; try { pipeline = pipelineFactory.getPipeline(); } catch (Exception ex) { throw new RuntimeException(ex); } if (timeout != 0) { IdleStateHandler idleStateHandler = new IdleStateHandler(timer, 0, 0, timeout, TimeUnit.MILLISECONDS); pipeline.addFirst("idleHandler", new UdpIdleHandler()); pipeline.addFirst("idleStateHandler", idleStateHandler); } ChannelConfig config = new DefaultChannelConfig(); ChannelSink sink = new UdpChildChannelSink(this); UdpChildChannel childChannel = new UdpChildChannel(serverChannel, null, pipeline, sink, config); fireChannelOpen(childChannel); childChannel.setLocalAddress(serverChannel.getLocalAddress()); childChannel.setBound(); fireChannelBound(childChannel, e.getRemoteAddress()); ChannelAddress address = channelAddress(datagramChannel, e.getRemoteAddress()); childChannel.setRemoteAddress(address); childChannel.setConnected(); fireChannelConnected(childChannel, e.getRemoteAddress()); return childChannel; }); fireMessageReceived(udpChildChannel, e.getMessage()); } }