Skip to content

Commit

Permalink
Attempt to fix Mac OSX compatibility by listing all network interface…
Browse files Browse the repository at this point in the history
…s and binding on each one separately
  • Loading branch information
mryan43 committed Oct 12, 2015
1 parent 5507c86 commit 7c57c85
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 19 deletions.
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
package com.github.mryan43.arquillian.available.port.extension.impl;

import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Map;
import java.net.SocketException;
import java.util.*;

import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.core.api.event.ManagerStarted;
Expand All @@ -12,37 +14,62 @@ public class AvailablePortFinder {

private static final String SYSTEM_PROPERTY_NAME = "available.port";

private static final Integer MAX_PORT_NUMBER = 65535;
private static final Integer MAX_PORT_NUMBER = 0xFFFF; //65535
private static final Integer MIN_PORT_NUMBER = 8080;

private final Map<ManagerStarted, String> previousValues = new HashMap<ManagerStarted, String>();

// it's important that this method is invoked before the arquillian.xml file is read, which is typically triggered by the
// ConfigurationRegistar observer, which is a ManagerStarted event observer.
// Since the ManagerStarted event is the very first one fired by Arquillian, we need to use the same, but with a higher precedence.
public void beforeConfigurationIsRead(@Observes(precedence = 50) ManagerStarted event) {

for (int port = MIN_PORT_NUMBER; port <= MAX_PORT_NUMBER; port++) {
try {
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.close();
previousValues.put(event, System.getProperty(SYSTEM_PROPERTY_NAME));
System.setProperty(SYSTEM_PROPERTY_NAME, Integer.toString(port));
return;
public void findAvailableNetworkPort(@Observes(precedence = 50) ManagerStarted event) {

try{
Set<InetAddress> addresses = getAllNetworkAddresses();

for (int port = MIN_PORT_NUMBER; port <= MAX_PORT_NUMBER; port++) {
boolean failed = false;
for (InetAddress address : addresses){
try{
ServerSocket serverSocket = new ServerSocket(port,5,address);
serverSocket.close();
}
catch (IOException ioe) {
// Port binding failed on one of the interfaces, let's try next port
failed = true;
break;
}
}
// managed to bind on all interfaces ?
if (!failed){
previousValues.put(event, System.getProperty(SYSTEM_PROPERTY_NAME));
System.setProperty(SYSTEM_PROPERTY_NAME, Integer.toString(port));
return;
}
}
catch (IOException ioe) {
// Tried binding port without success. Trying next port
} catch (SocketException e){
// Failed to list all network addresses, we fallback and try to bind on the default port
for (int port = MIN_PORT_NUMBER; port <= MAX_PORT_NUMBER; port++) {
try {
ServerSocket serverSocket = new ServerSocket(port);
serverSocket.close();
previousValues.put(event, System.getProperty(SYSTEM_PROPERTY_NAME));
System.setProperty(SYSTEM_PROPERTY_NAME, Integer.toString(port));
return;
}
catch (IOException ioe) {
// Port binding failed let's try next port
}
}
}

System.clearProperty("available.port");
throw new IllegalStateException("Unable to find an available port between " + MIN_PORT_NUMBER
+ " and " + MAX_PORT_NUMBER);

}

// Restore any previous value of the system property after the configuration has been read
public void afterConfigurationIsRead(@Observes(precedence = -50) ManagerStarted event) {
public void cleanup(@Observes(precedence = -50) ManagerStarted event) {

String previousValue = previousValues.get(event);
if (previousValue == null){
Expand All @@ -52,4 +79,16 @@ public void afterConfigurationIsRead(@Observes(precedence = -50) ManagerStarted
}
}

private static Set<InetAddress> getAllNetworkAddresses() throws SocketException {
Set<InetAddress> result = new HashSet<InetAddress>();
Enumeration<NetworkInterface> networkInterfaces = NetworkInterface.getNetworkInterfaces();
for (NetworkInterface networkInterface : Collections.list(networkInterfaces)) {
Enumeration<InetAddress> inetAddresses = networkInterface.getInetAddresses();
for (InetAddress inetAddress : Collections.list(inetAddresses)) {
result.add(inetAddress);
}
}
return result;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,34 @@

import com.github.mryan43.arquillian.available.port.extension.impl.AvailablePortFinder;

import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;

public class AvailablePortFinderTest {

@Test
public void test_find_port(){
public void test_find_port() throws IOException{

// Given
AvailablePortFinder portFinder = new AvailablePortFinder();
ManagerStarted event = new ManagerStarted();
System.setProperty("available.port", "previous value foo");

// When
portFinder.beforeConfigurationIsRead(event);
portFinder.findAvailableNetworkPort(event);

// Then
String[] possibleValues = new String[65537];
for (int i = 8080; i <= 65535; i++){
possibleValues[i] = Integer.toString(i);
}
assertThat(System.getProperty("available.port"), is(oneOf(possibleValues)));
System.out.println("Found available port : "+System.getProperty("available.port"));

// When
portFinder.afterConfigurationIsRead(event);
portFinder.cleanup(event);

// Then
assertThat(System.getProperty("available.port"), is("previous value foo"));
Expand Down

0 comments on commit 7c57c85

Please sign in to comment.