Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Strategies added to FreePort for how to pick ports #3

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions ops4j-base-net/src/main/java/org/ops4j/net/DefaultPortTester.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.ops4j.net;

import java.net.ServerSocket;

public final class DefaultPortTester implements PortTester {

@Override
public boolean isFree(int port) {
try
{
ServerSocket sock = new ServerSocket( port );
sock.close();
// is free:
return true;
// We rely on an exception thrown to determine availability or not availability.
} catch( Exception e )
{
// not free.
return false;
}
}
}
57 changes: 15 additions & 42 deletions ops4j-base-net/src/main/java/org/ops4j/net/FreePort.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
*/
package org.ops4j.net;

import java.net.ServerSocket;

/**
* Find a not-taken port on localhost.
* First call to getPort will try to find a free port in range given by contructor.
Expand All @@ -32,20 +30,32 @@
public class FreePort
{

private int m_from = 0;
private int m_to = Integer.MAX_VALUE;
private final int m_from;
private final int m_to;
private final FreePortStrategy m_strategy;
private int m_found = -1;

/**
* @param from Begin of range to search for free port (including)
* @param to End of range to search for free port (including)
*/
public FreePort( int from, int to )
{
this(from, to, new LinearFreePortStrategy(new DefaultPortTester()));
}

/**
* @param from Begin of range to search for free port (including)
* @param to End of range to search for free port (including)
*/
public FreePort( int from, int to, FreePortStrategy strategy)
{
m_from = from;
m_to = to;
m_strategy = strategy;
}


/**
* Finds a free socket upon first calll and returns the same for every next call.
*
Expand All @@ -57,48 +67,11 @@ public int getPort()
{
if( m_found == -1 )
{
m_found = findFree();
m_found = m_strategy.findFree(m_from, m_to);
}
return m_found;
}

private int findFree()
{
for( int i = m_from; i <= m_to; i++ )
{
if( isFree( i ) )
{
return i;
}
}
throw new RuntimeException( "No free port in range " + m_from + ":" + m_to );
}

/**
* Checks a given port for availability (by creating a temporary socket)
*
* Package visibility to enable overwriting in test.
*
* @param port Port to check
*
* @return true if its free, otherwise false.
*/
boolean isFree( int port )
{
try
{
ServerSocket sock = new ServerSocket( port );
sock.close();
// is free:
return true;
// We rely on an exception thrown to determine availability or not availability.
} catch( Exception e )
{
// not free.
return false;
}
}

/**
* @return Human readably String representation
*/
Expand Down
13 changes: 13 additions & 0 deletions ops4j-base-net/src/main/java/org/ops4j/net/FreePortStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.ops4j.net;

public interface FreePortStrategy {

/**
* Finds a free port in the range [from, to]
* @param from
* @param to
* @return
*/
int findFree(int from, int to);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.ops4j.net;

public final class LinearFreePortStrategy implements FreePortStrategy {

private final PortTester m_portTester;

public LinearFreePortStrategy(PortTester portTester)
{
this.m_portTester = portTester;
}

public int findFree(int from, int to)
{
for(int i = from; i <= to; i++ )
{
if( m_portTester.isFree( i ) )
{
return i;
}
}
throw new RuntimeException( "No free port in range " + from + ":" + to);
}

}
14 changes: 14 additions & 0 deletions ops4j-base-net/src/main/java/org/ops4j/net/PortTester.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.ops4j.net;

public interface PortTester {

/**
* Checks a given port for availability
*
* @param port Port to check
*
* @return true if its free, otherwise false.
*/
boolean isFree( int port );

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package org.ops4j.net;

import java.util.Objects;
import java.util.Random;

public final class RandomizedFreePortStrategy implements FreePortStrategy {

private final PortTester m_portTester;
private final int m_attempts;
private final Random m_random;

public RandomizedFreePortStrategy(PortTester portTester)
{
this(portTester, 50);
}

public RandomizedFreePortStrategy( PortTester portTester, int attempts)
{
this(portTester, attempts, new Random());
}

public RandomizedFreePortStrategy( PortTester portTester, int attempts, Random random)
{
Objects.requireNonNull(portTester);
if (attempts <= 0) {
throw new IllegalArgumentException("attempts must be greater than 0");
}
this.m_portTester = portTester;
this.m_attempts = attempts;
this.m_random = random;
}

public int findFree(int from, int to)
{
if (from > to) {
throw new IllegalArgumentException();
}
int range = to - from;
if (range == 0)
{
if (m_portTester.isFree(from))
{
return from;
}
} else
{
for (int attempt = 0; attempt < m_attempts; ++attempt)
{
int candidate = from + (m_random.nextInt(range + 1));
if (m_portTester.isFree(candidate))
{
return candidate;
}
}
}
throw new RuntimeException( "No free port found in range " + from + ":" + to + " after " + m_attempts + " attempts");
}
}
27 changes: 15 additions & 12 deletions ops4j-base-net/src/test/java/org/ops4j/net/FreePortTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ public class FreePortTest
@Test
public void testFreePortFirstTimeWithOneRange()
{
FreePort p = new FreePort( 1, 1 )
PortTester portTester = new PortTester()
{
public boolean alreadyCalled = false;

boolean isFree( int port )
public boolean isFree( int port )
{
assertEquals( 1, port );
if( alreadyCalled )
Expand All @@ -49,18 +49,19 @@ boolean isFree( int port )
}

};
assertEquals( 1, p.getPort() );
assertEquals( 1, p.getPort() );
FreePort freePort = new FreePort( 1, 1, new LinearFreePortStrategy(portTester));
assertEquals( 1, freePort.getPort() );
assertEquals( 1, freePort.getPort() );
}

@Test
public void testFreePortFirstTimeWithOneRangeNonFree()
{
FreePort p = new FreePort( 1, 1 )
PortTester portTester = new PortTester()
{
public boolean alreadyCalled = false;

boolean isFree( int port )
public boolean isFree( int port )
{
assertEquals( 1, port );
if( alreadyCalled )
Expand All @@ -78,26 +79,27 @@ boolean isFree( int port )
}

};
FreePort freePort = new FreePort( 1, 1, new LinearFreePortStrategy(portTester));
try
{
assertEquals( 1, p.getPort() );
assertEquals( 1, freePort.getPort() );
fail( "Exception expected." );
} catch( RuntimeException e )
{

}
// second check should be made
assertEquals( 1, p.getPort() );
assertEquals( 1, freePort.getPort() );
}

@Test
public void testRangeTraverse()
{
FreePort p = new FreePort( 1, 3 )
PortTester portTester = new PortTester()
{
public int alreadyCalled = 0;

boolean isFree( int port )
public boolean isFree( int port )
{
// this is the amount of required checks

Expand Down Expand Up @@ -130,9 +132,10 @@ boolean isFree( int port )

};

assertEquals( 3, p.getPort() );
FreePort freePort = new FreePort( 1, 3, new LinearFreePortStrategy(portTester));
assertEquals( 3, freePort.getPort() );
// subsequent call:
assertEquals( 3, p.getPort() );
assertEquals( 3, freePort.getPort() );

}
}
Loading