forked from fabric8io/jube
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fabric8io#210: Jube uses port finder to allocate ports in a range sta…
…rting from 48000
- Loading branch information
Showing
4 changed files
with
186 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
165 changes: 165 additions & 0 deletions
165
process-manager/src/main/java/io/fabric8/jube/process/service/AvailablePortFinder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,165 @@ | ||
/** | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You 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 io.fabric8.jube.process.service; | ||
|
||
import java.io.IOException; | ||
import java.net.DatagramSocket; | ||
import java.net.ServerSocket; | ||
import java.util.NoSuchElementException; | ||
import java.util.concurrent.atomic.AtomicInteger; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
/** | ||
* Finds currently available server ports. | ||
*/ | ||
public class AvailablePortFinder { | ||
|
||
/** | ||
* The minimum server currentMinPort number for IPv4. | ||
* We use port ranges starting from 48000 | ||
*/ | ||
public static final int MIN_PORT_NUMBER = 48000; | ||
|
||
/** | ||
* The maximum server currentMinPort number for IPv4. | ||
*/ | ||
public static final int MAX_PORT_NUMBER = 65535; | ||
|
||
private static final Logger LOG = LoggerFactory.getLogger(AvailablePortFinder.class); | ||
|
||
/** | ||
* We'll hold open the lowest port in this process | ||
* so parallel processes won't use the same block | ||
* of ports. They'll go up to the next block. | ||
*/ | ||
private static ServerSocket LOCK; | ||
|
||
/** | ||
* Incremented to the next lowest available port when getNextAvailable() is called. | ||
*/ | ||
private AtomicInteger currentMinPort; | ||
|
||
/** | ||
* Creates a new instance. | ||
*/ | ||
public AvailablePortFinder(int minPortNumber) { | ||
currentMinPort = new AtomicInteger(minPortNumber); | ||
|
||
int port = minPortNumber; | ||
ServerSocket ss = null; | ||
|
||
while (ss == null) { | ||
try { | ||
ss = new ServerSocket(port); | ||
} catch (Exception e) { | ||
ss = null; | ||
port += 200; | ||
} | ||
} | ||
LOCK = ss; | ||
Runtime.getRuntime().addShutdownHook(new Thread() { | ||
public void run() { | ||
try { | ||
LOCK.close(); | ||
} catch (Exception ex) { | ||
//ignore | ||
} | ||
} | ||
}); | ||
currentMinPort.set(port + 1); | ||
} | ||
|
||
/** | ||
* Gets the next available port starting at the lowest number. This is the preferred | ||
* method to use. The port return is immediately marked in use and doesn't rely on the caller actually opening | ||
* the port. | ||
* | ||
* @throws IllegalArgumentException is thrown if the port number is out of range | ||
* @throws java.util.NoSuchElementException if there are no ports available | ||
* @return the available port | ||
*/ | ||
public synchronized int getNextAvailable() { | ||
int next = getNextAvailable(currentMinPort.get()); | ||
currentMinPort.set(next + 1); | ||
return next; | ||
} | ||
|
||
/** | ||
* Gets the next available port starting at a given from port. | ||
* | ||
* @param fromPort the from port to scan for availability | ||
* @throws IllegalArgumentException is thrown if the port number is out of range | ||
* @throws java.util.NoSuchElementException if there are no ports available | ||
* @return the available port | ||
*/ | ||
public synchronized int getNextAvailable(int fromPort) { | ||
if (fromPort < currentMinPort.get() || fromPort > MAX_PORT_NUMBER) { | ||
throw new IllegalArgumentException("From port number not in valid range: " + fromPort); | ||
} | ||
|
||
for (int i = fromPort; i <= MAX_PORT_NUMBER; i++) { | ||
if (available(i)) { | ||
LOG.info("getNextAvailable({}) -> {}", fromPort, i); | ||
return i; | ||
} | ||
} | ||
|
||
throw new NoSuchElementException("Could not find an available port above " + fromPort); | ||
} | ||
|
||
/** | ||
* Checks to see if a specific port is available. | ||
* | ||
* @param port the port number to check for availability | ||
* @return <tt>true</tt> if the port is available, or <tt>false</tt> if not | ||
* @throws IllegalArgumentException is thrown if the port number is out of range | ||
*/ | ||
public boolean available(int port) throws IllegalArgumentException { | ||
if (port < currentMinPort.get() || port > MAX_PORT_NUMBER) { | ||
throw new IllegalArgumentException("Invalid start currentMinPort: " + port); | ||
} | ||
|
||
ServerSocket ss = null; | ||
DatagramSocket ds = null; | ||
try { | ||
ss = new ServerSocket(port); | ||
ss.setReuseAddress(true); | ||
ds = new DatagramSocket(port); | ||
ds.setReuseAddress(true); | ||
return true; | ||
} catch (IOException e) { | ||
// Do nothing | ||
} finally { | ||
if (ds != null) { | ||
ds.close(); | ||
} | ||
|
||
if (ss != null) { | ||
try { | ||
ss.close(); | ||
} catch (IOException e) { | ||
/* should not be thrown */ | ||
} | ||
} | ||
} | ||
|
||
return false; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters