ZIO/ZLayer FAQ: How do I use a Java Properties file with ZIO 2 and Scala?
Solution
You can use the zio-config library for things like this, but at the moment my preferred approach is to hand-code this ZLayer
solution. Maybe that’s because I know how to work with a Java Properties
file — i.e., how to read and load it — so I like to see those details.
Therefore, given this Java properties file named application.properties
:
username=alvin
email=coyote@acme.org
The following ZIO 2 and Scala 3 code shows how to read that properties file when it’s located in the same directory in which you start this application. I have added a lot of comments to the code, so please see the comments and Scala code for more information:
import zio.*
import java.io.FileInputStream
import java.util.Properties
// these fields correspond to the properties file fields
case class AppConfig(username: String, email: String)
// read the properties file as usual, wrapping that with ZIO.attempt
def readProperties(filePath: String) = ZIO.attempt {
val properties = new Properties()
properties.load(new FileInputStream(filePath))
properties
}
// access the individual properties to create an AppConfig instance
def propertiesToConfig(properties: Properties): Task[AppConfig] = ZIO.attempt {
val username = properties.getProperty("username")
val email = properties.getProperty("email")
AppConfig(username, email)
}
/**
* Here I create a ZLayer named `live` by actually doing the work of
* reading the properties file, populating an AppConfig instance, and
* then returning that instance.
*/
object PropertiesConfig:
def live(filePath: String): ZLayer[Any, Throwable, AppConfig] = ZLayer {
for
properties <- readProperties(filePath)
config <- propertiesToConfig(properties)
yield
config
}
/**
* This is the "main" ZIO 2 application, including an `app` value
* and the required `run` value. The `run` value *provides* the
* configuration information to the `app`.
*
* For ZLayer info, see:
* - https://zio.dev/reference/contextual/zlayer
* Can also use zio-config for app configuration.
*/
object ZioZLayerJavaPropertiesFile extends ZIOAppDefault:
val app: ZIO[AppConfig, Throwable, Unit] = for
appConfig <- ZIO.service[AppConfig]
_ <- Console.printLine(s"USERNAME: ${appConfig.username}")
_ <- Console.printLine(s"EMAIL: ${appConfig.email}")
yield
()
val run =
app.provideLayer(PropertiesConfig.live("./application.properties"))
.exitCode
An interesting/unique part of this solution is that it shows how to pass a parameter into a ZLayer
from inside the run
value. By that, I mean that the ZLayer
needs to know the name and location of the configuration file, and I pass that parameter into the live
function from inside the run
value. At the moment you can’t find solutions like this on the internet, so I hope this is helpful.
Because I added many comments to that code, again, please see those comments for more details.
this post is sponsored by my books: | |||
#1 New Release |
FP Best Seller |
Learn Scala 3 |
Learn FP Fast |