Skip to content

Instantly share code, notes, and snippets.

@EdgeCaseBerg
Last active March 31, 2020 13:54
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save EdgeCaseBerg/c25ec3d57a8622d08cafed2a1b7d320b to your computer and use it in GitHub Desktop.
Save EdgeCaseBerg/c25ec3d57a8622d08cafed2a1b7d320b to your computer and use it in GitHub Desktop.
Example of using the ThrowingProviders and CheckedProviders in Guice. See full context: http://www.ethanjoachimeldridge.info/tech-blog/guice-scala-checked-providers
import com.google.inject.{AbstractModule, Provides, Guice}
import com.google.inject.throwingproviders.{ CheckedProvides, CheckedProvider, ThrowingProviderBinder }
import com.typesafe.config.{ ConfigException, ConfigFactory }
import java.net.{URL, MalformedURLException}
import javax.inject.Inject
import scala.collection.JavaConversions._
import scala.util.{Try, Success, Failure}
object Example extends App {
case class DataSourceParams(val url: URL, @transient val accessCode: String)
case class RemotePizzaOrder(
val numberOfPizzas: Long,
val pizzaToppings: Seq[String],
val dietRestrictions: Seq[String]
)
class UnsafePizzaModule extends AbstractModule {
def configure {}
@Provides
def provideDataSourceParams() = {
val conf = ConfigFactory.load() // Might throw an exception!
val url = new URL(conf.getString("dsp.url")) // Might throw an exception!
val accessCode = conf.getString("dsp.accessCode") // Might throw an expcetion!
DataSourceParams(url, accessCode)
}
@Provides
def provideRemotePizzaOrder(dataSourceParams: DataSourceParams) = {
val conf = ConfigFactory.parseURL(dataSourceParams.url) // Might throw an exception
RemotePizzaOrder(
conf.getLong("rpo.numberOfPizzas"),
conf.getStringList("rpo.pizzaToppings"),
conf.getStringList("rpo.dietRestrictions")
) // Any of the conf.get* might throw an exception
}
}
class PizzaHandler @Inject() (order: RemotePizzaOrder) {
println(order)
}
// Uncomment these to get explosions
//val injector = Guice.createInjector(new UnsafePizzaModule)
//val unsafeHandler = injector.getInstance(classOf[PizzaHandler])
//println(unsafeHandler) // You won't get here because of exceptions
trait DataSourceProviderLike extends CheckedProvider[DataSourceParams] {
@throws(classOf[ConfigException])
@throws(classOf[MalformedURLException])
def get(): DataSourceParams
}
class DataSourceProvider extends DataSourceProviderLike {
@throws(classOf[ConfigException])
@throws(classOf[MalformedURLException])
def get() = {
val conf = ConfigFactory.load() // Might throw an exception!
val url = new URL(conf.getString("dsp.url")) // Might throw an exception!
val accessCode = conf.getString("dsp.accessCode") // Might throw an expcetion!
DataSourceParams(url, accessCode)
}
}
trait RemotePizzaOrderProviderLike extends CheckedProvider[RemotePizzaOrder] {
@throws(classOf[ConfigException])
def get(): RemotePizzaOrder
}
class RemotePizzaOrderProvider @Inject() (dataSourceParamsProvider: DataSourceProviderLike) extends RemotePizzaOrderProviderLike {
@throws(classOf[ConfigException])
def get() = {
Try(dataSourceParamsProvider.get()) match {
case Success(dataSourceParams) => {
val conf = ConfigFactory.parseURL(dataSourceParams.url)
RemotePizzaOrder(
conf.getLong("rpo.numberOfPizzas"),
conf.getStringList("rpo.pizzaToppings"),
conf.getStringList("rpo.dietRestrictions")
)
}
case Failure(exception) => {
println("Can't get pizza order right now, try again later...")
RemotePizzaOrder(0, Nil, Nil)
}
}
}
}
class SaferPizzaModuleBound extends AbstractModule {
def configure() {
ThrowingProviderBinder.create(binder())
.bind(classOf[DataSourceProviderLike], classOf[DataSourceParams])
.to(classOf[DataSourceProvider])
ThrowingProviderBinder.create(binder())
.bind(classOf[RemotePizzaOrderProviderLike], classOf[RemotePizzaOrder])
.to(classOf[RemotePizzaOrderProvider])
}
}
class SaferPizzaModuleInstalled extends AbstractModule {
def configure() {
install(ThrowingProviderBinder.forModule(this))
}
@CheckedProvides(classOf[DataSourceProviderLike])
def provideDataSourceParams() = {
val conf = ConfigFactory.load()
val url = new URL(conf.getString("dsp.url"))
val accessCode = conf.getString("dsp.accessCode")
DataSourceParams(url, accessCode)
}
@CheckedProvides(classOf[RemotePizzaOrderProviderLike])
def provideRemotePizzaOrder(dataSourceParamsProvider: DataSourceProviderLike) = {
Try(dataSourceParamsProvider.get()) match {
case Success(dataSourceParams) => {
val conf = ConfigFactory.parseURL(dataSourceParams.url)
RemotePizzaOrder(
conf.getLong("rpo.numberOfPizzas"),
conf.getStringList("rpo.pizzaToppings"),
conf.getStringList("rpo.dietRestrictions")
)
}
case Failure(exception) => {
println("Can't get pizza order right now, try again later...")
RemotePizzaOrder(0, Nil, Nil)
}
}
}
}
class SaferPizzaHandler @Inject() (order: RemotePizzaOrderProviderLike) {
println(order.get())
}
val injector = Guice.createInjector(new SaferPizzaModuleBound)
val saferHandler = injector.getInstance(classOf[SaferPizzaHandler])
val injector2 = Guice.createInjector(new SaferPizzaModuleInstalled)
val saferHandler2 = injector2.getInstance(classOf[SaferPizzaHandler])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment