/** * 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.ChannelFactory; import org.jboss.netty.channel.ChannelFuture; import org.jboss.netty.channel.ChannelPipeline; import org.jboss.netty.channel.ChannelPipelineException; import org.jboss.netty.channel.socket.nio.NioDatagramChannelFactory; import org.jboss.netty.channel.socket.nio.NioDatagramWorkerPool; import org.jboss.netty.handler.timeout.IdleStateHandler; import org.jboss.netty.util.ExternalResourceReleasable; import org.jboss.netty.util.HashedWheelTimer; import org.jboss.netty.util.Timer; import org.kaazing.k3po.driver.internal.executor.ExecutorServiceFactory; import org.kaazing.k3po.driver.internal.netty.bootstrap.BootstrapFactorySpi; import org.kaazing.k3po.driver.internal.netty.bootstrap.ClientBootstrap; import org.kaazing.k3po.driver.internal.netty.bootstrap.ServerBootstrap; import org.kaazing.k3po.driver.internal.netty.channel.ChannelAddress; import org.kaazing.k3po.driver.internal.netty.channel.udp.UdpChannelAddress; import javax.annotation.Resource; import java.net.InetSocketAddress; import java.net.SocketAddress; import java.net.URI; import java.util.Collection; import java.util.concurrent.ConcurrentLinkedDeque; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public final class UdpBootstrapFactorySpi extends BootstrapFactorySpi implements ExternalResourceReleasable { private final Collection<ChannelFactory> channelFactories; private ExecutorServiceFactory executorServiceFactory; private NioDatagramChannelFactory clientChannelFactory; private UdpServerChannelFactory serverChannelFactory; private final Timer timer; public UdpBootstrapFactorySpi() { channelFactories = new ConcurrentLinkedDeque<>(); timer = new HashedWheelTimer(); } @Resource public void setExecutorServiceFactory(ExecutorServiceFactory executorServiceFactory) { this.executorServiceFactory = executorServiceFactory; } /** * Returns the name of the transport provided by factories using this service provider. */ @Override public String getTransportName() { return "udp"; } @Override public void shutdown() { for (ChannelFactory channelFactory : channelFactories) { channelFactory.shutdown(); } } @Override public void releaseExternalResources() { for (ChannelFactory channelFactory : channelFactories) { channelFactory.releaseExternalResources(); } timer.stop(); } /** * Returns a {@link ClientBootstrap} instance for the named transport. */ @Override public synchronized ClientBootstrap newClientBootstrap() throws Exception { if (clientChannelFactory == null) { Executor workerExecutor = executorServiceFactory.newExecutorService("worker.client"); NioDatagramWorkerPool workerPool = new NioDatagramWorkerPool(workerExecutor, 1); clientChannelFactory = new NioDatagramChannelFactory(workerPool); // unshared channelFactories.add(clientChannelFactory); } return new UdpClientBootstrap(clientChannelFactory, timer); } /** * Returns a {@link ServerBootstrap} instance for the named transport. */ @Override public synchronized ServerBootstrap newServerBootstrap() throws Exception { if (serverChannelFactory == null) { Executor workerExecutor = executorServiceFactory.newExecutorService("worker.server"); NioDatagramWorkerPool workerPool = new NioDatagramWorkerPool(workerExecutor, 1); serverChannelFactory = new UdpServerChannelFactory(new UdpServerChannelSink(workerPool, timer)); // unshared channelFactories.add(serverChannelFactory); } return new ServerBootstrap(serverChannelFactory); } private static InetSocketAddress toInetSocketAddress(ChannelAddress channelAddress) { if (channelAddress == null) { return null; } URI location = channelAddress.getLocation(); String hostname = location.getHost(); int port = location.getPort(); return new InetSocketAddress(hostname, port); } // Subclassing for two reasons: // 1) uses InetSocketAddress for local and remote addresses // 2) Adds IdleStateHandler filter to track UDP idle client connections private static class UdpClientBootstrap extends ClientBootstrap { private final Timer timer; UdpClientBootstrap(ChannelFactory channelFactory, Timer timer) { super(channelFactory); this.timer = timer; } @Override public ChannelFuture connect(final SocketAddress remoteChannelAddress, final SocketAddress localChannelAddress) { InetSocketAddress localAddress = toInetSocketAddress((ChannelAddress) localChannelAddress); InetSocketAddress remoteAddress = toInetSocketAddress((ChannelAddress) remoteChannelAddress); if (remoteAddress == null) { throw new NullPointerException("remoteAddress"); } ChannelPipeline pipeline; try { pipeline = getPipelineFactory().getPipeline(); } catch (Exception e) { throw new ChannelPipelineException("Failed to initialize a pipeline.", e); } long timeout = ((UdpChannelAddress) remoteChannelAddress).timeout(); if (timeout != 0) { IdleStateHandler idleStateHandler = new IdleStateHandler(timer, 0, 0, timeout, TimeUnit.MILLISECONDS); pipeline.addFirst("idleHandler", new UdpIdleHandler()); pipeline.addFirst("idleStateHandler", idleStateHandler); } setPipeline(pipeline); // Set the options. Channel ch = getFactory().newChannel(pipeline); boolean success = false; try { ch.getConfig().setOptions(getOptions()); success = true; } finally { if (!success) { ch.close(); } } // Bind. if (localAddress != null) { ch.bind(localAddress); } // Connect. return ch.connect(remoteAddress); } } }