Last active
March 31, 2020 13:54
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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