2

I reworked the Java code to find a free port that I found here => https://gist.github.com/vorburger/3429822#file-gistfile1-java to use in Scala

def findFreePort(): Int = {
    var ss: ServerSocket = null

    try {
      ss = new ServerSocket(0)
      ss.getLocalPort
    } finally {
      ss.close()
    }
  }

However, it looks very ugly with var assigned to null. Is there a better way to do it?

  • You could wrap it in an [option](https://www.scala-lang.org/api/current/scala/Option.html) and use `Some(ss)` and `None`. – k0pernikus Dec 17 '19 at 11:01
  • 1
    Avoid questions that allow for opinionated fuzzy answers. "What's an elegant / better way to do X?" is off topic, but "How to avoid `null`?" is on-topic. – k0pernikus Dec 17 '19 at 11:03
  • @k0pernikus Language of the question aside, such cases are really tricky. And almost everyone in Scala community has faced these at least few times. – sarveshseri Dec 17 '19 at 11:14
  • 1
    `Try(new ServerSocket(0)).map(_.getLocalPort).toOption` The finally is a bit more tricky, I would recommend you to use something like a [**Resource**](https://typelevel.org/cats-effect/api/cats/effect/Resource.html) – Luis Miguel Mejía Suárez Dec 17 '19 at 11:47
  • @LuisMiguelMejíaSuárez as I understand, when you call ```new ServerSocket(0)```, it can open a socket connection and then throw an exception, so you need to close it handling the exception... Wrapping in ```Try```does not offer it. COrrect me if I am wrong – ClassNotFoundException Dec 17 '19 at 14:31
  • @ClassNotFoundException you are probably correct, that is why I said the `close` was _tricky_. That is why I suggested `Resource`. However, if you aren't using **cats** and you have Scala `2.13` then `Using` sounds like the best option. – Luis Miguel Mejía Suárez Dec 17 '19 at 14:37
  • The solution is quite simple, use val ss = new ServerSocket(); val port = ss.getLocalPort; ss.close(); return port. If new ServerSocket() throws, there's nothing to clean up, because it won't have returned anything, so there's nothing you can close, you actually have a bug in your code where you're not checking if ss is null. and getLocalPort doesn't throw, so it doesn't need to be done in a try/finally. – James Roper Oct 14 '21 at 01:53

2 Answers2

5

How about Using:

scala> import scala.util.{Using, Try}

scala> def findFreePort(): Try[Int] = Using(new ServerSocket(0)) (_.getLocalPort)
def findFreePort(): util.Try[Int]

scala> findFreePort()
val res1: util.Try[Int] = Success(51865)
Eastsun
  • 18,526
  • 6
  • 57
  • 81
0

The original implementation seems wrong to me. If the ServerSocket constructor fails, ss will still be null, and the close call will fail with a NullPointerException. Just run this Scastie snippet to be convinced.

Unless I'm missing something, it's then best to write your snippet as

val port: Option[Int] = Try(new ServerSocket(0).close()).map(_.getLocalPort).toOption
francoisr
  • 4,407
  • 1
  • 28
  • 48