/*
* Copyright (c) 2008-2017, Hazelcast, Inc. 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 com.hazelcast.instance;
import com.hazelcast.config.Config;
import com.hazelcast.logging.ILogger;
import com.hazelcast.logging.Logger;
import com.hazelcast.nio.Address;
import com.hazelcast.nio.IOUtil;
import com.hazelcast.spi.properties.HazelcastProperties;
import com.hazelcast.test.HazelcastSerialClassRunner;
import com.hazelcast.test.annotation.QuickTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeNotNull;
@RunWith(HazelcastSerialClassRunner.class)
@Category({QuickTest.class})
public class DefaultAddressPickerTest {
private static final String PUBLIC_HOST = "www.hazelcast.org";
private static final String HAZELCAST_LOCAL_ADDRESS_PROP = "hazelcast.local.localAddress";
private ILogger logger = Logger.getLogger(AddressPicker.class);
private Config config = new Config();
private HazelcastProperties properties;
private AddressPicker addressPicker;
private InetAddress loopback;
private String localAddressValue;
@Before
public void setup() throws UnknownHostException {
properties = new HazelcastProperties(config);
InetAddress publicAddress = null;
try {
loopback = InetAddress.getByName("127.0.0.1");
publicAddress = InetAddress.getByName(PUBLIC_HOST);
} catch (UnknownHostException e) {
e.printStackTrace();
}
assumeNotNull(loopback, publicAddress);
localAddressValue = System.getProperty(HAZELCAST_LOCAL_ADDRESS_PROP);
System.clearProperty(HAZELCAST_LOCAL_ADDRESS_PROP);
}
@After
public void tearDown() {
if (addressPicker != null) {
IOUtil.closeResource(addressPicker.getServerSocketChannel());
}
if (localAddressValue != null) {
System.setProperty(HAZELCAST_LOCAL_ADDRESS_PROP, localAddressValue);
}
}
@Test
public void testBindAddress_withDefaultPortAndLoopbackAddress() throws Exception {
config.setProperty(HAZELCAST_LOCAL_ADDRESS_PROP, loopback.getHostAddress());
testBindAddress(loopback);
}
@Test
public void testBindAddress_withCustomPortAndLoopbackAddress() throws Exception {
config.setProperty(HAZELCAST_LOCAL_ADDRESS_PROP, loopback.getHostAddress());
int port = 6789;
config.getNetworkConfig().setPort(port);
testBindAddress(loopback);
}
@Test
public void testBindAddress_withNonLoopbackAddressViaInterfaces() throws Exception {
InetAddress address = findIPv4NonLoopbackInterface();
assumeNotNull(address);
config.getNetworkConfig().getInterfaces().setEnabled(true)
.clear().addInterface(address.getHostAddress());
testBindAddress(address);
}
@Test
public void testBindAddress_withNonLoopbackAddressViaTCPMembers() throws Exception {
InetAddress address = findIPv4NonLoopbackInterface();
assumeNotNull(address);
config.getNetworkConfig().getJoin().getTcpIpConfig().setEnabled(true)
.clear().addMember(address.getHostAddress());
testBindAddress(address);
}
@Test
public void testBindAddress_withNonLoopbackAddressViaSystemProperty() throws Exception {
InetAddress address = findIPv4NonLoopbackInterface();
assumeNotNull(address);
config.setProperty(HAZELCAST_LOCAL_ADDRESS_PROP, address.getHostAddress());
testBindAddress(address);
}
private void testBindAddress(InetAddress address) throws Exception {
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
int port = config.getNetworkConfig().getPort();
assertEquals(new Address(address, port), addressPicker.getBindAddress());
assertEquals(addressPicker.getBindAddress(), addressPicker.getPublicAddress());
}
@Test
public void testBindAddress_withEphemeralPort() throws Exception {
config.setProperty(HAZELCAST_LOCAL_ADDRESS_PROP, loopback.getHostAddress());
config.getNetworkConfig().setPort(0);
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
int port = addressPicker.getServerSocketChannel().socket().getLocalPort();
assertEquals(new Address(loopback, port), addressPicker.getBindAddress());
assertEquals(addressPicker.getBindAddress(), addressPicker.getPublicAddress());
}
@Test
public void testBindAddress_whenAddressAlreadyInUse() throws Exception {
int port = 6789;
config.getNetworkConfig().setPort(port);
config.getNetworkConfig().setPortAutoIncrement(false);
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
try {
new DefaultAddressPicker(config, properties, logger).pickAddress();
fail("Should fail with 'java.net.BindException: Address already in use'");
} catch (Exception expected) {
}
}
@Test
public void testBindAddress_whenAddressAlreadyInUse_WithPortAutoIncrement() throws Exception {
int port = 6789;
config.getNetworkConfig().setPort(port);
config.getNetworkConfig().setPortAutoIncrement(true);
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
new DefaultAddressPicker(config, properties, logger).pickAddress();
}
@Test
public void testPublicAddress_withDefaultPortAndLoopbackAddress() throws Exception {
testPublicAddress("127.0.0.1", -1);
}
@Test
public void testPublicAddress_withDefaultPortAndLocalhost() throws Exception {
testPublicAddress("localhost", -1);
}
@Test
public void testPublicAddress_withSpecifiedHost() throws Exception {
testPublicAddress(PUBLIC_HOST, -1);
}
@Test
public void testPublicAddress_withSpecifiedHostAndPort() throws Exception {
testPublicAddress(PUBLIC_HOST, 6789);
}
private void testPublicAddress(String host, int port) throws Exception {
config.getNetworkConfig().setPublicAddress(port < 0 ? host : (host + ":" + port));
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
if (port < 0) {
port = config.getNetworkConfig().getPort();
}
assertEquals(new Address(host, port), addressPicker.getPublicAddress());
}
@Test
public void testPublicAddress_withSpecifiedHostAndPortViaProperty() throws Exception {
String host = PUBLIC_HOST;
int port = 6789;
config.setProperty("hazelcast.local.publicAddress", host + ":" + port);
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
assertEquals(new Address(host, port), addressPicker.getPublicAddress());
}
@Test(expected = UnknownHostException.class)
public void testPublicAddress_withInvalidAddress() throws Exception {
config.getNetworkConfig().setPublicAddress("invalid");
addressPicker = new DefaultAddressPicker(config, properties, logger);
addressPicker.pickAddress();
}
private static InetAddress findIPv4NonLoopbackInterface() {
try {
Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
while (interfaces.hasMoreElements()) {
NetworkInterface ni = interfaces.nextElement();
Enumeration<InetAddress> addresses = ni.getInetAddresses();
while (addresses.hasMoreElements()) {
InetAddress address = addresses.nextElement();
if (address.isLoopbackAddress()) {
continue;
}
if (address instanceof Inet6Address) {
continue;
}
return address;
}
}
} catch (SocketException e) {
e.printStackTrace();
}
return null;
}
}