/* dCache - http://www.dcache.org/ * * Copyright (C) 2015 Deutsches Elektronen-Synchrotron * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.dcache.util; import com.google.common.base.Throwables; import com.google.common.util.concurrent.Uninterruptibles; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import java.io.IOException; import java.net.BindException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.concurrent.ExecutionException; /** * Immutable class representing a port range. */ public class NettyPortRange extends PortRange { /** * Creates a port range with the given bounds (both inclusive). * Zero is excluded from non-empty port ranges. * * @throws IllegalArgumentException is either bound is not between * 0 and 65535, or if <code>high</code> is lower than * <code>low</code>. */ public NettyPortRange(int low, int high) { super(low, high); } /** * Creates a port range containing a single port. */ public NettyPortRange(int port) { this(port, port); } public NettyPortRange(PortRange range) { this(range.getLower(), range.getUpper()); } /** * Parse a port range. A port range consists of either a single * integer, or two integers separated by either a comma or a * colon. * * The bounds must be between 0 and 65535, both inclusive. * * @return The port range represented by <code>s</code>. Returns * the range [0,0] if <code>s</code> is null or empty. */ public static NettyPortRange valueOf(String s) throws IllegalArgumentException { return new NettyPortRange(PortRange.valueOf(s)); } /** * Binds <code>server</socket> to <code>address</code>. A port is * chosen from this port range. If the port range is [0,0], then a * free port is chosen by the OS. * * @throws IOException if the bind operation fails. */ public Channel bind(ServerBootstrap server, InetAddress address) throws IOException { int start = random(); int port = start; do { try { ChannelFuture future = server.bind(new InetSocketAddress(address, port)); Uninterruptibles.getUninterruptibly(future); return future.channel(); } catch (ExecutionException e) { if (!(e.getCause() instanceof BindException)) { Throwables.throwIfInstanceOf(e.getCause(), IOException.class); Throwables.throwIfUnchecked(e.getCause()); throw new RuntimeException(e.getCause()); } } port = succ(port); } while (port != start); throw new BindException("No free port within range"); } /** * Binds <code>server</socket> to the wildcard * <code>address</code>. A port is chosen from this port range. If * the port range is [0,0], then a free port is chosen by the OS. * * @throws IOException if the bind operation fails, or if the * socket is already bound. */ public Channel bind(ServerBootstrap socket) throws IOException { return bind(socket, null); } }