System language: german
The test Jetty10ServletWebServerFactoryTests#portClashOfPrimaryConnectorResultsInPortInUseException only works if system language is set to english (export LANG=en). But this should be a problem with production code, too.
The code in org.springframework.boot.web.server.PortInUseException#ifPortBindingException checks if the error message contains the words "in use", which is not the case on non-english systems. On a german system the message is "java.net.BindException: Die Adresse wird bereits verwendet" for example.
One can workaround by setting LANG=en java -jar ... when starting the application, then the error message will be english and PortInUseException is thrown.
Comment From: viktorardelean
@mhalbritter I have checkout out the 2.5.x branch and I cannot see the portClashOfPrimaryConnectorResultsInPortInUseException test method in the org.springframework.boot.web.embedded.jetty.Jetty10ServletWebServerFactoryTests class.
Comment From: wilkinsona
It is inherited from a super-class: https://github.com/spring-projects/spring-boot/blob/1872af056ec059db2316db40ef19e2c1801af208/spring-boot-project/spring-boot/src/test/java/org/springframework/boot/web/servlet/server/AbstractServletWebServerFactoryTests.java#L916
Comment From: viktorardelean
@wilkinsona Thanks for pointing it out! The problem here is that BindException contains just the error message - which is different for every language. Do you have any thoughts on what would be a good fix for this?
Should we just remove the if condition: if (bindException.getMessage().toLowerCase().contains("in use")) and accept all BindExceptionsin the ifPortBindingExceptionmethod?
Comment From: mhalbritter
The error message comes from the operating system, and depends on the LANG environment variable. We will have to find a better way to determine if there's a port clash going on than parsing the (localized) message. How we do that, I don't know at the moment.
Comment From: somayaj
Can we try based on the OS type -- lsof, netstat use for this? using ProcessBuilder
Comment From: mhalbritter
I don't think that's worth it.
Comment From: somayaj
Well then reading the OS error codes, 98 (EADDRINUSE) or 10048 (WSAEADDRINUSE) ..somewhere in the error stack would do it?
Comment From: wilkinsona
As far as I know, those error codes do not appear anywhere in the stack trace of the BindException. If you have evidence to the contrary, please do share it.
Comment From: somayaj
try {
serverSocket.setReuseAddress(false);
serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), 8080), 1);
serverSocket.bind(new InetSocketAddress(InetAddress.getByName("localhost"), 8080), 1);
} catch (SocketException be) {
if(serverSocket.isBound()) { // this check here should do it.
throw new BindException(be.getMessage());
}
}
Comment From: wilkinsona
The code that's trying to bind the socket isn't ours, it's part of the web server that we embed and, therefore, we cannot change it. All that we have available for analysis is the BindException that's thrown by the JDK.
Comment From: somayaj
Well then we can translate the message/exception based on the lang/locale to en-US and then check for the port in use str?
Comment From: somayaj
Well then we can translate the message/exception based on the lang/locale to en-US and then check for the port in use str?
Any word here?
Comment From: philwebb
@somayaj I'm not sure that translating the message into every possible language is going to be feasible.
Comment From: mhalbritter
I don't think this one is fixable.