View Javadoc
1   package ca.uhn.fhir.util;
2   
3   /*
4    * #%L
5    * HAPI FHIR - Core Library
6    * %%
7    * Copyright (C) 2014 - 2019 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
24  import org.slf4j.Logger;
25  import org.slf4j.LoggerFactory;
26  
27  import java.io.IOException;
28  import java.net.InetSocketAddress;
29  import java.net.ServerSocket;
30  import java.net.Socket;
31  
32  /**
33   * Provides server ports
34   */
35  @CoverageIgnore
36  public class PortUtil {
37  	private static final Logger ourLog = LoggerFactory.getLogger(PortUtil.class);
38  
39  	/*
40  	 * Non instantiable
41  	 */
42  	private PortUtil() {
43  		// nothing
44  	}
45  
46  	/**
47  	 * The entire purpose here is to find an available port that can then be
48  	 * bound for by server in a unit test without conflicting with other tests.
49  	 * <p>
50  	 * This is really only used for unit tests but is included in the library
51  	 * so it can be reused across modules. Use with caution.
52  	 */
53  	public static int findFreePort() {
54  		ServerSocket server;
55  		try {
56  			server = new ServerSocket(0);
57  			server.setReuseAddress(true);
58  			int port = server.getLocalPort();
59  
60  			/*
61  			 * Try to connect to the newly allocated port to make sure
62  			 * it's free
63  			 */
64  			for (int i = 0; i < 10; i++) {
65  				try {
66  					Socket client = new Socket();
67  					client.connect(new InetSocketAddress(port), 1000);
68  					break;
69  				} catch (Exception e) {
70  					if (i == 9) {
71  						throw new InternalErrorException("Can not connect to port: " + port);
72  					}
73  					Thread.sleep(250);
74  				}
75  			}
76  
77  			server.close();
78  
79  			/*
80  			 * This is an attempt to make sure the port is actually
81  			 * free before releasing it. For whatever reason on Linux
82  			 * it seems like even after we close the ServerSocket there
83  			 * is a short while where it is not possible to bind the
84  			 * port, even though it should be released by then.
85  			 *
86  			 * I don't have any solid evidence that this is a good
87  			 * way to do this, but it seems to help...
88  			 */
89  			for (int i = 0; i < 10; i++) {
90  				try {
91  					Socket client = new Socket();
92  					client.connect(new InetSocketAddress(port), 1000);
93  					ourLog.info("Socket still seems open");
94  					Thread.sleep(250);
95  				} catch (Exception e) {
96  					break;
97  				}
98  			}
99  
100 			// ....annnd sleep a bit for the same reason.
101 			Thread.sleep(500);
102 
103 			// Log who asked for the port, just in case that's useful
104 			StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace();
105 			StackTraceElement previousElement = stackTraceElements[2];
106 			ourLog.info("Returned available port {} for: {}", port, previousElement.toString());
107 
108 			return port;
109 		} catch (IOException | InterruptedException e) {
110 			throw new Error(e);
111 		}
112 	}
113 
114 }
115