View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.hbase;
20  
21  import java.io.IOException;
22  import java.net.BindException;
23  import java.net.InetAddress;
24  import java.net.InetSocketAddress;
25  import java.net.ServerSocket;
26  import java.nio.channels.ServerSocketChannel;
27  
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.apache.hadoop.hbase.testclassification.SmallTests;
31  import org.junit.Assert;
32  import org.junit.Test;
33  import org.junit.experimental.categories.Category;
34  
35  /**
36   * This tests whether ServerSocketChannel works over ipv6, which Zookeeper
37   * depends on. On Windows Oracle JDK 6, creating a ServerSocketChannel throws
38   * java.net.SocketException: Address family not supported by protocol family
39   * exception. It is a known JVM bug, seems to be only resolved for JDK7:
40   * http://bugs.sun.com/view_bug.do?bug_id=6230761
41   *
42   * For this test, we check that whether we are effected by this bug, and if so
43   * the test ensures that we are running with java.net.preferIPv4Stack=true, so
44   * that ZK will not fail to bind to ipv6 address using ClientCnxnSocketNIO.
45   */
46  @Category(SmallTests.class)
47  public class TestIPv6NIOServerSocketChannel {
48  
49    private static final Log LOG = LogFactory.getLog(TestIPv6NIOServerSocketChannel.class);
50  
51    /**
52     * Creates and binds a regular ServerSocket.
53     */
54    private void bindServerSocket(InetAddress inetAddr) throws IOException {
55      while(true) {
56        int port = HBaseTestingUtility.randomFreePort();
57        InetSocketAddress addr = new InetSocketAddress(inetAddr, port);
58        ServerSocket serverSocket = null;
59        try {
60          serverSocket = new ServerSocket();
61          serverSocket.bind(addr);
62          break;
63        } catch (BindException ex) {
64          //continue
65        } finally {
66          if (serverSocket != null) {
67            serverSocket.close();
68          }
69        }
70      }
71    }
72  
73    /**
74     * Creates a NIO ServerSocketChannel, and gets the ServerSocket from
75     * there. Then binds the obtained socket.
76     * This fails on Windows with Oracle JDK1.6.0u33, if the passed InetAddress is a
77     * IPv6 address. Works on Oracle JDK 1.7.
78     */
79    private void bindNIOServerSocket(InetAddress inetAddr) throws IOException {
80      while (true) {
81        int port = HBaseTestingUtility.randomFreePort();
82        InetSocketAddress addr = new InetSocketAddress(inetAddr, port);
83        ServerSocketChannel channel = null;
84        ServerSocket serverSocket = null;
85        try {
86          channel = ServerSocketChannel.open();
87          serverSocket = channel.socket();
88          serverSocket.bind(addr); // This does not work
89          break;
90        } catch (BindException ex) {
91          //continue
92        } finally {
93          if (serverSocket != null) {
94            serverSocket.close();
95          }
96          if (channel != null) {
97            channel.close();
98          }
99        }  
100     }
101   }
102 
103   /**
104    * Checks whether we are effected by the JDK issue on windows, and if so
105    * ensures that we are running with preferIPv4Stack=true.
106    */
107   @Test
108   public void testServerSocket() throws IOException {
109     byte[] addr = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 };
110     InetAddress inetAddr = InetAddress.getByAddress(addr);
111 
112     try {
113       bindServerSocket(inetAddr);
114       bindNIOServerSocket(inetAddr);
115       //if on *nix or windows JDK7, both will pass
116     } catch(java.net.SocketException ex) {
117       //On Windows JDK6, we will get expected exception:
118       //java.net.SocketException: Address family not supported by protocol family
119       //or java.net.SocketException: Protocol family not supported
120       Assert.assertFalse(ex.getClass().isInstance(BindException.class));
121       Assert.assertTrue(ex.getMessage().toLowerCase().contains("protocol family"));
122       LOG.info("Received expected exception:");
123       LOG.info(ex);
124 
125       //if this is the case, ensure that we are running on preferIPv4=true
126       ensurePreferIPv4();
127     }
128   }
129 
130   /**
131    * Checks whether we are running with java.net.preferIPv4Stack=true
132    */
133   public void ensurePreferIPv4() throws IOException {
134     InetAddress[] addrs = InetAddress.getAllByName("localhost");
135     for (InetAddress addr : addrs) {
136       LOG.info("resolved localhost as:" + addr);
137       Assert.assertEquals(4, addr.getAddress().length); //ensure 4 byte ipv4 address
138     }
139   }
140 
141   /**
142    * Tests whether every InetAddress we obtain by resolving can open a
143    * ServerSocketChannel.
144    */
145   @Test
146   public void testServerSocketFromLocalhostResolution() throws IOException {
147     InetAddress[] addrs = InetAddress.getAllByName("localhost");
148     for (InetAddress addr : addrs) {
149       LOG.info("resolved localhost as:" + addr);
150       bindServerSocket(addr);
151       bindNIOServerSocket(addr);
152     }
153   }
154 
155   public static void main(String[] args) throws Exception {
156     TestIPv6NIOServerSocketChannel test = new TestIPv6NIOServerSocketChannel();
157     test.testServerSocket();
158     test.testServerSocketFromLocalhostResolution();
159   }
160 }