Git Product home page Git Product logo

scala-guice's Introduction

Scala extensions for Google Guice 7.0.0

Getting Started

Add dependency

We currently support Scala 2.11, 2.12, 2.13, and 3

maven:
<dependency>
    <groupId>net.codingwell</groupId>
    <artifactId>scala-guice_2.13</artifactId>
    <version>7.0.0</version>
</dependency>
sbt:
"net.codingwell" %% "scala-guice" % "7.0.0"
gradle:
'net.codingwell:scala-guice_2.13:7.0.0'

Mixin

Mixin ScalaModule with your AbstractModule for rich scala magic (or ScalaPrivateModule with your PrivateModule):

import com.google.inject.{AbstractModule, PrivateModule}
import net.codingwell.scalaguice.{ScalaModule, ScalaPrivateModule}

class MyModule extends AbstractModule with ScalaModule {
  def configure(): Unit = {
    bind[Service].to[ServiceImpl].in[Singleton]
    bind[CreditCardPaymentService]
    bind[Bar[Foo]].to[FooBarImpl]
    bind[PaymentService].annotatedWith(Names.named("paypal")).to[CreditCardPaymentService]
  }
}

class MyPrivateModule extends PrivateModule with ScalaPrivateModule {
  def configure(): Unit = {
    bind[Foo].to[RealFoo]
    expose[Foo]

    install(new TransactionalBarModule())
    expose[Bar].annotatedWith[Transactional]

    bind[SomeImplementationDetail]
    install(new MoreImplementationDetailsModule())
  }
}

Inject

Wrap the injector in a ScalaInjector for even more rich scala magic:

import com.google.inject.Guice

object MyServer {
  def main(args: Array[String]) {
    val injector = Guice.createInjector(new MyModule(), new MyPrivateModule)

    import net.codingwell.scalaguice.InjectorExtensions._
    val service = injector.instance[Service]
    val foo = injector.instance[Foo]

    // Retrieve a Bar annotated with Transactional
    val bar = injector.instance[Bar, Transactional]

    // Retrieve a PaymentService annotated with a specific Annotation instance.
    val paymentService = injector.instance[PaymentService](Names.named("paypal"))
    ...
  }
}

Additional Features

Module Traits

class MyModule extends AbstractModule with ScalaModule
class MyPrivateModule extends PrivateModule with ScalaPrivateModule

This gives to access to scala style bindings:

bind[A].to[B]
bind[A].to(classOf[B])
bind[A].to(typeLiteral[B])
bind[A].toInstance("A")
bind[A].annotatedWith[Ann].to[B]
bind[A].annotatedWith( classOf[Ann] ).to[B]
bind[A].annotatedWith( Names.named("name") ).to[B]
bind[A].annotatedWithName("name").to[B]
bind[A].toProvider[BProvider]
bind[A].toProvider[TypeProvider[B]]
bind[A[String]].to[B[String]]
bind[A].to[B].in[Singleton]

bindInterceptor[AOPI](methodMatcher = annotatedWith[AOP])

Injector Extensions

import net.codingwell.scalaguice.InjectorExtensions._

injector.instance[A]
injector.instance[A, Ann]
injector.instance[A]( Names.named("name") )

injector.provider[A]
injector.provider[A, Ann]
injector.provider[A]( Names.named("name") )

//Returns Option[A]
injector.existingBinding[A]
injector.existingBinding[A, Ann]
injector.existingBinding[A]( Names.named("name") )

Multibinding

The ScalaMultibinder adds scala style multibindings:

class MyModule extends AbstractModule with ScalaModule {
  def configure(): Unit = {
    val stringMulti = ScalaMultibinder.newSetBinder[String](binder)
    stringMulti.addBinding.toInstance("A")

    val annotatedMulti = ScalaMultibinder.newSetBinder[A, Annotation](binder)
    annotatedMulti.addBinding.to[A]

    val namedMulti = ScalaMultibinder.newSetBinder[ServiceConfiguration](binder, Names.named("backend"))
    namedMulti.addBinding.toInstance(config.getAdminServiceConfiguration)
  }
}

And then they may be retrieved as immutable.Set[T]. (examples in order)

class StringThing @Inject() (strings: immutable.Set[String]) { ... }

class AThing @Inject() (@Annotation configs: immutable.Set[A]) { ... }

class Service @Inject() (@Names.named("backend") configs: immutable.Set[ServiceConfiguration]) { ... }

Generic Multibinding

trait Provider[T] {
  def provide: T
}

class StringProvider extends Provider[String] {
  override def provide = "Hello world!"
}

class IntProvider extends Provider[Int] {
  override def provide = 42
}
val multibinder = ScalaMultibinder.newSetBinder[Provider[_]](binder)

multibinder.addBinding.toInstance(new StringProvider)
multibinder.addBinding.toInstance(new IntProvider)

Provider[_] is actually translated to Provider[java.lang.Object]. So you need to use immutable.Set[Provider[Any]] and not immutable.Set[Provider[_]] :

class SomeClass @Inject() (providers: immutable.Set[Provider[Any]]) { ... }

OptionBinding

Newly available in Guice 4.0-beta5, we've got some support for OptionalBinder.

class MyModule extends AbstractModule with ScalaModule {
  def configure(): Unit = {
    val optBinder = ScalaOptionBinder.newOptionBinder[String](binder)
    optBinder.setDefault.toInstance("A")
    // To override the default binding (likely in another module):
    optBinder.setBinding.toInstance("B")

    val annotatedOptBinder = ScalaOptionBinder.newOptionBinder[A, Annotation](binder)
    annotatedOptBinder.setDefault.to[A]

    val namedOptBinder = ScalaOptionBinder.newOptionBinder[ServiceConfiguration](binder, Names.named("backend"))
    namedOptBinder.setBinding.toInstance(config.getAdminServiceConfiguration)
  }
}

And then they may be retrieved as Option[T], Option[Provider[T]], and Option[jakarta.inject.Provider[T]]. (examples in order)

class StringThing @Inject() (name: Option[String]) { ... }

class AThing @Inject() (@Annotation aProvider: Option[Provider[T]]) { ... }

class Service @Inject() (@Names.named("backend") configProvider: Option[jakarta.inject.Provider[ServiceConfiguration]]) { ... }

MapBinding

The ScalaMapBinder adds scala style mapbindings:

class MyModule extends AbstractModule with ScalaModule {
  def configure(): Unit = {
    val mBinder = ScalaMapBinder.newMapBinder[String, Int](binder)
    mBinder.addBinding("1").toInstance(1)
  }
}

And then may be retrieved as any of the following:

  • immutable.Map[K, V]
  • immutable.Map[K, Provider[V]]
  • immutable.Map[K, jakarta.inject.Provider[V]]

If you call mapBinder.permitDuplicates() on the binder then you may also inject:

  • immutable.Map[K, immutable.Set[V]]
  • immutable.Map[K, immutable.Set[Provider[V]]]

Interceptor Binding

bindInterceptor adds scala style interceptor binding

bindInterceptor(Matchers.any(), Matchers.annotatedWith(classOf[Logging]), new LoggingInterceptor())
bindInterceptor[LoggingInterceptor](methodMatcher = annotatedWith[Logging])

Gotchas

Reserved Words

In Scala, the words override and with are reserved and must be escaped to be used.

Modules.`override`(new BaseModule).`with`(new TestModule)

Mixins

To our knowledge there is no way to represent a Mixin A with B in Google Guice. Guice sees a mixin as its base type and therefore A with B is equivalent to A. This means only one can be bound in the injector. It is also possible to try to inject A where A with B is expected, most likely resulting in a ClassCastException.

And the stuff we forgot...

If you find a feature we support but don't mention here, submit an issue and we will add it.

If you find a feature we don't support but want, implement it and send us a pull request. Alternatively, you can file an issue and we may or may not get to it.

scala-guice's People

Contributors

ashleymercer avatar benlings avatar cacoco avatar dnomyar avatar dportabella avatar gallalouche avatar heuermh avatar jw3 avatar michalkowol avatar mscharley avatar mslinn avatar n4to4 avatar nbauernfeind avatar nlochschmidt avatar octonato avatar skress avatar tsuckow avatar vrolijkx avatar zhaojunz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

scala-guice's Issues

scala-guice does not bind primitive types correctly

class IntProvider extends Provider[Int] {
def get() = 1
}

trait Foo[T] { def get(): T }

class FooProvider extends Provider[Foo[Long]] {
def get() = new Foo[Long] { def get() = 1L }
}

IntProvider is bound as java.lang.Object, and FooProvider is bound as Foo[java.lang.Object]. If instead I bind to the java primitives directly, types are right.

does not bind `AnyVal` properly...

This throws java.lang.reflect.MalformedParameterizedTypeException. I think the problem is Guice depends on Java reflection which cannot properly interpret AnyVal, as I cannot assign null to the initial value of Foo in BarProvider, since Foo is not bounded by Null

class Foo(val foo: String) extends AnyVal {}
object Foo {
  def apply(foo: String): Foo = new Foo(foo)

  def empty: Foo = new Foo("")
}

import com.google.inject._
import net.codingwell.scalaguice._

class BarProvider extends Provider[String] {
  @Inject val foo: Foo = Foo.empty

  def get() = foo.toString()
}

class Module extends AbstractModule with ScalaModule {
  def configure() {
    bind[Foo].toInstance(Foo("abc"))
    bind[String].toProvider[BarProvider]
  }
}

import net.codingwell.scalaguice.InjectorExtensions._

val injector = Guice.createInjector(new Module)
Console.println(injector.instance[String])

bind[F[T]], where T is a value type (long, int, etc), does not work with @Inject constructors

example:

scala> val module = new AbstractModule with ScalaModule {
     |   def configure() {
     |     bind[Option[Int]].toInstance(Some(1))
     |   }
     | }
module: com.google.inject.AbstractModule with net.codingwell.scalaguice.ScalaModule = $anon$1@4bd5a2cc

scala> class Test @Inject()(val optionOfInt: Option[Int])
defined class Test

scala> Guice.createInjector(module).getInstance(classOf[Test])
com.google.inject.ConfigurationException: Guice configuration errors:

1) No implementation for scala.Option<java.lang.Object> was bound.
  while locating scala.Option<java.lang.Object>
    for parameter 0 at Test.<init>(<console>:10)
  while locating Test

1 error
  at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1042)
  at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1001)
  at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1051)
  ... 43 elided

Note that it works fine if you use @provides instead

scala> val module = new AbstractModule with ScalaModule {
     |   @Provides
     |   def providesOptionOfInt: Option[Int] = Some(1)
     |   def configure = {}
     | }
module: com.google.inject.AbstractModule with net.codingwell.scalaguice.ScalaModule{def providesOptionOfInt: Option[Int]} = $anon$1@7ba2e475

scala> class Test @Inject()(val optionOfInt: Option[Int])
defined class Test

scala> Guice.createInjector(module).getInstance(classOf[Test])
res0: Test = Test@10042b25

or if T is a reference type

scala> val module = new AbstractModule with ScalaModule {
     |   def configure {
     |     bind[Option[String]].toInstance(Some("fish"))
     |   }
     | }
module: com.google.inject.AbstractModule with net.codingwell.scalaguice.ScalaModule = $anon$1@3ea66c1d

scala> class Test @Inject()(val optionOfString: Option[String])
defined class Test

scala> Guice.createInjector(module).getInstance(classOf[Test])
res1: Test = Test@22e84b6e

Problems with Play framework's dev mode after upgrading to scala-guice 4.2.2

First of all: thanks a lot for maintaining this library, it is really helpful.

I've run into problems with scala-guice after upgrading to 4.2.2 in combination with Play framework's dev mode. There is a minimal Play project which you could use to reproduce the error: https://github.com/skress/issue-play-scala-guice

If I am not doing anything terribly wrong then even this simple configuration does not work:

trait Service {
  def message: String
}

class ServiceProvider extends Service {
  def message: String = "..."
}

class Module extends ScalaModule {

  override def configure() {
    bind[Service].to[ServiceProvider]
  }

}

The exception looks like this:

java.lang.ClassNotFoundException: modules.Service
     java.net.URLClassLoader.findClass(URLClassLoader.java:382)
     java.lang.ClassLoader.loadClass(ClassLoader.java:424)
     java.lang.ClassLoader.loadClass(ClassLoader.java:357)
     java.lang.Class.forName0(Native Method)
     java.lang.Class.forName(Class.java:348)
     scala.reflect.runtime.JavaMirrors$JavaMirror.javaClass(JavaMirrors.scala:570)
     scala.reflect.runtime.JavaMirrors$JavaMirror.$anonfun$classToJava$1(JavaMirrors.scala:1246)
     scala.reflect.runtime.TwoWayCaches$TwoWayCache.$anonfun$toJava$1(TwoWayCaches.scala:61)
     scala.reflect.runtime.TwoWayCaches$TwoWayCache.toJava(TwoWayCaches.scala:57)
     scala.reflect.runtime.JavaMirrors$JavaMirror.classToJava(JavaMirrors.scala:1238)
     scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:209)
     scala.reflect.runtime.JavaMirrors$JavaMirror.runtimeClass(JavaMirrors.scala:67)
     net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:66)
     net.codingwell.scalaguice.package$.typeLiteral(package.scala:38)
     net.codingwell.scalaguice.InternalModule$BindingBuilder.<init>(ScalaModule.scala:69)
     net.codingwell.scalaguice.InternalModule.bind(ScalaModule.scala:74)
     net.codingwell.scalaguice.InternalModule.bind$(ScalaModule.scala:74)
     modules.Module.bind(Module.scala:13)

Reverting the two changes from Manifest to TypeTag in ScalaModule.scala (8ebf64e#diff-3f5a5d294b9751c91b703f2e10f25a04) seems to fix the issue.

The reason for the problem seems to be that the classloader that scala-guice sees is not the classloader that Play uses to load the project's classes in dev mode (running the app in production mode does not exhibit the error.) In Play's dev mode the project's dependencies are loaded, the framework is started and Play then creates a new classloader that loads the project's classes. When you change anything in your project the changes are compiled, the classloader is abandoned and a new one is created that loads the just compiled classes.

From my limited understanding this should explain why scala-guice does not work. But it should have never worked. What's the difference between Manifest and TypeTag that one is working and the other is not, even if in both cases the same (wrong) classloader is used?

Binding of generic types

I have issue with binding some generic types, namely bind[Unit => String] and bind[(=> Unit) => String]. Guice itself is able to both bind and inject types like this, but current implementation of this library stops me from doing that, because:

  • bind[Unit => String]
    Unit is converted to Void, but only for binding thus the binding is not found when injecting
  • bind[(=> Unit) => String]
    (=> Unit) => String is in fact Function1[Function0[Unit], String] but compiler is unable to generate Manifest for that

I would like to ask if there is some reason why the Unit is converted to Void.
I would be very happy to fix it (it's easily solvable by a short macro which I already wrote for myself as a workaround), I just want to be sure there's not some good reason for having it like these.

Thx.

injector.instance[Foo[Array[Byte]] does not properly find the bound type

We have a case where a Module provides a type of Foo[Array[Byte]] to the object graph via an @Provides-annotated method in a Module. When that type is manually looked up via injector.instance[Foo[Array[Byte]]] it is not found.

It appears as though the TypeLiteral built by net.codingwell.scalaguice.typeLiteral ends up using the java.lang.Byte[] boxed type as the generic arg type, so the Key is built for type Foo[java.lang.Byte[]], though the type bound is actually the primitive, byte[] type -- Foo[byte[]].

This is on Scala 2.12.10 running JDK 8 and using scalaguice 4.2.7 and google guice 4.2.3.

Example:

Welcome to Scala 2.12.10 (JDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.

scala> import com.google.inject.{AbstractModule, Guice, Provides}
import com.google.inject.{AbstractModule, Guice, Provides}

scala> import javax.inject.Singleton
import javax.inject.Singleton

scala> import net.codingwell.scalaguice.ScalaModule
import net.codingwell.scalaguice.ScalaModule

scala> val module = new AbstractModule with ScalaModule {
     |   @Singleton
     |   @Provides
     |   def provideSeq: Seq[Array[Byte]] =
     |     Seq.empty[Array[Byte]]
     | }
module: com.google.inject.AbstractModule with net.codingwell.scalaguice.ScalaModule{def provideSeq: Seq[Array[Byte]]} = $anon$1@2e1291a4

scala> val i = Guice.createInjector(module)
i: com.google.inject.Injector = Injector{bindings=[InstanceBinding{key=Key[type=com.google.inject.Stage, annotation=[none]], source=[unknown source], instance=DEVELOPMENT}, ProviderInstanceBinding{key=Key[type=com.google.inject.Injector, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Injector>}, ProviderInstanceBinding{key=Key[type=java.util.logging.Logger, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Logger>}, ProviderInstanceBinding{key=Key[type=scala.collection.Seq<byte[]>, annotation=[none]], source=public scala.collection.Seq $anon$1.provideSeq(), scope=Scopes.SINGLETON, provider=@Provides $anon$1.provideSeq(<console>:18)}]}

scala> import net.codingwell.scalaguice.InjectorExtensions._
import net.codingwell.scalaguice.InjectorExtensions._

scala> i.instance[Seq[Array[Byte]]]
com.google.inject.ConfigurationException: Guice configuration errors:

1) No implementation for scala.collection.Seq<java.lang.Byte[]> was bound.
  while locating scala.collection.Seq<java.lang.Byte[]>

1 error
  at com.google.inject.internal.InjectorImpl.getProvider(InjectorImpl.java:1120)
  at com.google.inject.internal.InjectorImpl.getInstance(InjectorImpl.java:1126)
  at net.codingwell.scalaguice.InjectorExtensions$ScalaInjector.instance(InjectorExtensions.scala:27)
  ... 34 elided

scala> 

Tracing it through, it seems that the TypeConversions.scalaTypeToJavaType does not properly handle this case for building a JavaType or at least builds one that will not match the type bound to the object graph. Any help or guidance would be appreciated. Thanks!

ScalaMultibinder doesn't allow multiple modules to contribute, as Guice Multibinder does

If I create a Guice Multibinder for some type, I am allowed to do this from multiple modules, specifically the

Multibinder.newSetBinder(binder, type)

method can be called in as many modules as I'd like, which allows me to have multiple modules dynamically contribute to the set of types, instances, etc. configured.

However, if I do the following:

val multi1 = ScalaMultibinder.newSetBinder[Int](binder)
multi1.addBinding().toInstance(1)
val multi2 = ScalaMultibinder.newSetBinder[Int](binder)
multi2.addBinding().toInstance(2)

I get an exception from Guice that the binding configuration has already occurred:

A binding to scala.collection.immutable.Set<java.lang.Integer> was already configured at     
net.codingwell.scalaguice.ScalaMultibinder$.newSetBinder(MultiBinder.scala:106).

This makes the ScalaMultibinder much less useful. Not sure if this is a bug or by design but it prevents me from being able to use the ScalaMultibinder.

Bind Instances with annotation

I'm trying to do something like this;

Binding:
bind[A].annotatedWith( Names.named("name") ).toInstance["B"]

Getting from injector:
val b = injector.instance[A]

There is a compilation error if i try get from injector with ( Names.named("name") )
type mismatch; found : com.google.inject.name.Named required: Manifest[A]

Thanks

Release to Sonatype Nexus

It has been driven me nuts that this extremely useful tool has yet to hit a real repository. Now that you're sitting in the pilot seat, can you get this published to sonatype? It's fairly simple.

Type named is not a member of object com.google.inject.name.Names

I have the following repo on GitHub which uses this library for DI of a very simple Scala app.

If you clone it an run ./gradlew run you'll see that its complaining about my use of @Names.named when specifying parameters to inject, specifically right here:

class DefaultListener @Inject() (@Names.named("username") val username : String) extends ActionListener with InputTypes {
  override def onAction(name: String, isPressed: Boolean, tpf: Float): Unit = {
    println(s"I am responding to something important for ${username}!")
  }
}

The specific exception states:

"type named is not a member of object com.google.inject.name.Names"

However I believe I've followed your documentation precisely. Can anyone spot where I'm going awry? Thanks in advance!

Where did `@Inject` annotations go?

I've used Guice for Java + Groovy projects for years now, and am used to property and/or constructor injection like so:

class Fizz {
    @Inject
    BillingService billingService

    // ...
}

class Buzz {
    @Inject
    Buzz(DataService dataService) {
        // ...
    }

    // ...
}

But in Scala constructors work a lot different:

class Fizz(val billingService : BillingService) {
    // ...
}

And in all of your code examples + docs I don't see @Inject used anywhere. So I ask: does @Inject still exist in scala-guice, or has it been replaced by something else? If it doesn't exist, then how does Guice know which fields to inject and which ones not to?

Improve documentation

There basically isn't any except for what is on the main page. It is pretty self explanatory but doesn't show everything (multibindings). That change I pulled...

Duplicate Binding: ScalaModule believes A == A with B.

@shengc Provided an example that involves a binding error when it probably shouldn't be.

trait A { def a {} }
trait B

type AB = A with B

class Model extends AbstractModule with ScalaModule {
    def configure() {
        bind[A].toInstance(new A {})
        bind[AB].toInstance(new A with B {})
    }
}

Guice.createInjector(new Model()) // This fails at run time. Guice thinks binds are dups.

Binding wildcard types doesn't use correct type literal

I have problems with binding wildcard types in scala Guice. If you define a binding like bind[Container[_]].toInstance(someInstance) or bind[Containter[_ <: SomeImpl].toInstance(someInstance) then injections of these types into constructors/fields will fail.
I have pinpointed the problem to the way scalaGuice generates a typeLiteral see spec below:

val typeLiteralScalaGuice = typeLiteral[Container[_ <: Impl1]]
val typeLiteralJavaWay = new TypeLiteral[Container[_ <: Impl1]] {}

typeLiteralJavaWay shouldEqual typeLiteralScalaGuice //this failes

Create named Actors

We need another provideActorRef that provides named actors. Named actors (at least at my experience level) are essential to connecting distributed systems.

GuiceActorProducer's magic is too deep for me to see how to create the needed changes. It seems that probable only an additional argument passed into IndirectActorProducer would do it.

Thanks, this wrapper is really needed. I only wish I could have made more use of it.

ScalaMapBinder does not support traits as type

Suppose you've a trait that you will implement in child classes -

trait AppMessage {}

Given this injections

    val mapBinder = ScalaMapBinder.newMapBinder[String, AppMessage](binder)
    mapBinder.addBinding("A").toInstance(new AppMessageSampleA)
    mapBinder.addBinding("B").toInstance(new AppMessageSampleB)

and calling them with val map = injector.instance[im.Map[String, AppMessage]] throws not found exception.

Need to support traits

Shall we make this the main scala-guice repo?

Hey all,

Based on the fact that you guys are accepting pull requests and have set up a CI server, it appears to me that this is now the main repo for this project. Thanks for continuing to contribute to it and keep it current!

I just wanted to mark my intent here to contact the original author of this project and have put up either a redirect or a notice to this one in his README.md.

How one can use assisted injection with scala-guice? Is it supported ?

Hi,

I have been using google-guice with the assisted inject mechanism for a while. As i am in scala, and just discover scala-guice, i'm interested in using it as well. However i'm confused as to how to use assisted injection with it. There is not example of using assisted inject with it. Especially, would it support assisted-inject library ?

Hence my question here is: is it possible to use assisted injection with scala-guice, if yes, please could provide a simple example ? or at least indicated the library needed.

For google guice i use, javax.inject.jar, guice-3.0.jar, guice-assistedInject.jar

Can't attach Singleton scope toProvider

I have the following binding in my Module

bind[HttpRequestClient].toProvider[OKHttpRequestClientProvider].in[Singleton]

getting following error

type arguments [Singleton] do not conform to method in's type parameter bounds [TAnn <: java.lang.annotation.Annotation]

Any help ?

Replace uses of Manifest with ClassTag / TypeTag

If a type is owned by an object then it looks like binding is special-cased so, for example, the following works as expected:

object Foo {
  case class Bar(value: Int)
}

bind[Foo.Bar].toInstance(Foo.Bar(value = 1))

However, if the type Bar is instead used as a type parameter for some generic type, then the binding fails with ScalaReflectException because the enclosing type cannot be found in the Mirror, so this:

trait Factory[T] {
  def get(): T
}

class FactoryImpl[T] @Inject()(value: T) extends Factory[T] {
  def get(): T = value
}

bind[Factory[Foo.Bar]].to[FactoryImpl[Foo.Bar]]

results in

scala.ScalaReflectionException: class Foo in JavaMirror with DependencyClassLoader{...elided...}
	at scala.reflect.internal.Mirrors$RootsBase.staticClass(Mirrors.scala:129)
	at scala.reflect.internal.Mirrors$RootsBase.staticClass(Mirrors.scala:29)
	at net.codingwell.scalaguice.TypeConversions$.findOwnerOf(TypeConversions.scala:87)
	at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:66)
	at net.codingwell.scalaguice.TypeConversions$.$anonfun$scalaTypeToJavaType$1(TypeConversions.scala:67)
	...

Publish 2.11_3.0.2

Can you publish one of the 3.0.x versions of this libraries for scala 2.11? Dependency issues are forcing some of us to stay on Guice 3 even as we upgrade to Scala 2.11.

SingletonType bindings not working since 4.2.2

Since version 4.2.2 and upwards, this binding results in an exception:

// A sealed domain
sealed trait Root
final case object Foo extends Root
final case object Bar extends Root
final case object Baz extends Root

// A case class using said domain
case class MyContainerClass[T <: Root](inner: T)

// In a ScalaModule:
bind[MyContainerClass[Foo.type]].toProvider[FooProvider]
bind[MyContainerClass[Bar.type]].toProvider[BarProvider]
bind[MyContainerClass[Baz.type]].toProvider[BazProvider]

This results in the following exception:

[Error] Caused by: java.lang.UnsupportedOperationException: Could not convert scalaType Foo.type to a javaType: scala.reflect.internal.Types$UniqueSingleType at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:78)

With 4.2.1, this works without a problem. Am I doing something wrong, or is this type of binding simply not supported (anymore), @tsuckow? Thanks in advance for the assistance. ๐Ÿ‘

Non-matchng SHAs when depending on jenkins repository in new project

Hello,

My team is having troubles pulling the artifact from the jenkins repository. We get this error when including the artifact -- looks like a SHA that doesn' tmatch. Have you seen anything like this?

[error] {file:/Users/eboto/code/eg-eclipsetest/}website/*:update: sbt.ResolveException: download failed: uk.me.lings#scala-guice_2.9.1;3.0.1-SNAPSHOT!scala-guice_2.9.1.jar
[info] Updating {file:/Users/eboto/code/eg-eclipsetest/}website...
[info] downloading https://jenkins-codingwell.rhcloud.com/job/Scala-Guice/lastSuccessfulBuild/artifact/repo/uk/me/lings/scala-guice_2.9.1/3.0.1-SNAPSHOT/scala-guice_2.9.1-3.0.1-SNAPSHOT.jar ...
[warn]  [FAILED     ] uk.me.lings#scala-guice_2.9.1;3.0.1-SNAPSHOT!scala-guice_2.9.1.jar: invalid sha1: expected=81b0b0b06ddf524792748a1daed6f21ad3299ac9 computed=671c89ef2d215bf3c0c81bcb6c2ee339f7a58be0 (10596ms)
[warn]  [FAILED     ] uk.me.lings#scala-guice_2.9.1;3.0.1-SNAPSHOT!scala-guice_2.9.1.jar: invalid sha1: expected=81b0b0b06ddf524792748a1daed6f21ad3299ac9 computed=671c89ef2d215bf3c0c81bcb6c2ee339f7a58be0 (10596ms)
[warn] ==== scala-guice: tried
[warn]   https://jenkins-codingwell.rhcloud.com/job/Scala-Guice/lastSuccessfulBuild/artifact/repo/uk/me/lings/scala-guice_2.9.1/3.0.1-SNAPSHOT/scala-guice_2.9.1-3.0.1-SNAPSHOT.jar

Dealing with extensions to Guice

Currently multibindings is a dependency of scala-guice. I think idealy this should only be a compile time dependency and if you want guice-multibindings at runtime you have to include it yourself.

We should look into supporting assisted inject and how we want to handle that new dependency. Guice has many extensions, we don't want to force everyone to load them all.

Binding Providers of parameterised types

Consider:

private class AnOptionProvider @Inject()(s:String) extends Provider[Option[String]] {
  def get = Option(s)
}

I can do:

bind(new TypeLiteral[Option[String]]() {}).toProvider(classOf[AnOptionProvider])

But I can't do:

bind(new TypeLiteral[Option[String]]() {}).toProvider[AnOptionProvider]

Which is slightly more elegant.

I get the feeling I'm missing something from the README when it says:

bind[A].toProvider[BProvider]
bind[A].toProvider[TypeProvider[B]]

Assuming this solved it, could that example be fleshed out?

2.12-ification

We should start publishing against 2.12.0-M4 if possible, since it looks like 2.12.0 final is around the corner.

Akka and Guice

Do you any example code somewhere, that shows how to use Scala-guice/guice to inject actors ?

injector.instance[Class with Trait] throws UnsupportedException

Hey there, back again. We're continuing our upgrade of Finatra to the latest version of scalaguice and have encountered another issue around the TypeConversions logic.

We have a fair amount of usage of the finagle-mysql library where a common pattern is to create a MySQL client, then augment it for doing transactions, e.g., Client with Transactions. An instance of this type is then typically provided to the object graph via an @Provides-annotated method in a Module. However, with the latest scalaguice library, the TypeConversions fails to translate the ScalaType to a JavaType and thus any attempt to get the bound instance via an injector lookup throws an exception like this:

java.lang.UnsupportedOperationException: Could not convert scalaType com.twitter.finagle.mysql.Client with com.twitter.finagle.mysql.Transactions to a javaType: scala.reflect.internal.Types$RefinedType0
                     	at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:89)
                     	at net.codingwell.scalaguice.package$.typeLiteral(package.scala:37)

Here's an example repro using Scala 2.12.10, JDK 8, scalaguice 4.2.8 and guice 4.2.3.

Welcome to Scala 2.12.10 (JDK 64-Bit Server VM, Java 1.8.0_222).
Type in expressions for evaluation. Or try :help.

scala> class SomeClient {
     |   private[this] val underlying: String = "Alice"
     |
     |   def doSomething: String = s"Hello, $underlying."
     | }
defined class SomeClient

scala>

scala> trait Augmentation { self: SomeClient =>
     |   override def doSomething: String = s"${self.doSomething} Welcome to Wonderland."
     | }
defined trait Augmentation

scala>

scala> import com.google.inject.{AbstractModule, Guice, Provides}
import com.google.inject.{AbstractModule, Guice, Provides}

scala> import net.codingwell.scalaguice.ScalaModule
import net.codingwell.scalaguice.ScalaModule

scala> import net.codingwell.scalaguice.InjectorExtensions._
import net.codingwell.scalaguice.InjectorExtensions._

scala> import javax.inject.Singleton
import javax.inject.Singleton

scala>

scala> val i = Guice.createInjector(new AbstractModule with ScalaModule {
     |   @Provides
     |   @Singleton
     |   def provideSomeClient: SomeClient with Augmentation = {
     |     new SomeClient with Augmentation
     |   }
     | })
i: com.google.inject.Injector = Injector{bindings=[InstanceBinding{key=Key[type=com.google.inject.Stage, annotation=[none]], source=[unknown source], instance=DEVELOPMENT}, ProviderInstanceBinding{key=Key[type=com.google.inject.Injector, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Injector>}, ProviderInstanceBinding{key=Key[type=java.util.logging.Logger, annotation=[none]], source=[unknown source], scope=Scopes.NO_SCOPE, provider=Provider<Logger>}, ProviderInstanceBinding{key=Key[type=SomeClient, annotation=[none]], source=public SomeClient $anon$1.provideSomeClient(), scope=Scopes.SINGLETON, provider=@Provides $anon$1.provideSomeClient(<console>:23)}]}

scala> i.instance[SomeClient with Augmentation]
java.lang.UnsupportedOperationException: Could not convert scalaType SomeClient with Augmentation to a javaType: scala.reflect.internal.Types$RefinedType0
  at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:89)
  at net.codingwell.scalaguice.package$.typeLiteral(package.scala:37)
  at net.codingwell.scalaguice.InjectorExtensions$ScalaInjector.instance(InjectorExtensions.scala:27)
  ... 34 elided

scala>

Binding this type via the scalaguice bind DSL in the ScalaModule also fails similarly:

scala> val ii = Guice.createInjector(new AbstractModule with ScalaModule {
     |   override def configure(): Unit = {
     |     bind[SomeClient with Augmentation].toInstance(new SomeClient with Augmentation)
     |   }
     | })
Jun 23, 2020 7:54:25 AM com.google.inject.internal.MessageProcessor visit
INFO: An exception was caught and reported. Message: java.lang.UnsupportedOperationException: Could not convert scalaType SomeClient with Augmentation to a javaType: scala.reflect.internal.Types$RefinedType0
java.lang.UnsupportedOperationException: Could not convert scalaType SomeClient with Augmentation to a javaType: scala.reflect.internal.Types$RefinedType0
        at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:89)
        at net.codingwell.scalaguice.package$.typeLiteral(package.scala:37)
        at net.codingwell.scalaguice.InternalModule$BindingBuilder.<init>(ScalaModule.scala:70)
        at net.codingwell.scalaguice.InternalModule.bind(ScalaModule.scala:75)
        at net.codingwell.scalaguice.InternalModule.bind$(ScalaModule.scala:75)
        at $line13.$read$$iw$$iw$$iw$$iw$$anon$1.bind(<console>:19)
        at $line13.$read$$iw$$iw$$iw$$iw$$anon$1.configure(<console>:21)
        at com.google.inject.AbstractModule.configure(AbstractModule.java:61)
        at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:347)
        at com.google.inject.spi.Elements.getElements(Elements.java:104)
        at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:137)
        at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:105)
        at com.google.inject.Guice.createInjector(Guice.java:87)
        at com.google.inject.Guice.createInjector(Guice.java:69)
        at com.google.inject.Guice.createInjector(Guice.java:59)
        at $line13.$read$$iw$$iw$$iw$$iw$.<init>(<console>:19)
        at $line13.$read$$iw$$iw$$iw$$iw$.<clinit>(<console>)
        at $line13.$eval$.$print$lzycompute(<console>:7)
        at $line13.$eval$.$print(<console>:6)
        at $line13.$eval.$print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at scala.tools.nsc.interpreter.IMain$ReadEvalPrint.call(IMain.scala:745)
        at scala.tools.nsc.interpreter.IMain$Request.loadAndRun(IMain.scala:1021)
        at scala.tools.nsc.interpreter.IMain.$anonfun$interpret$1(IMain.scala:574)
        at scala.reflect.internal.util.ScalaClassLoader.asContext(ScalaClassLoader.scala:41)
        at scala.reflect.internal.util.ScalaClassLoader.asContext$(ScalaClassLoader.scala:37)
        at scala.reflect.internal.util.AbstractFileClassLoader.asContext(AbstractFileClassLoader.scala:41)
        at scala.tools.nsc.interpreter.IMain.loadAndRunReq$1(IMain.scala:573)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:600)
        at scala.tools.nsc.interpreter.IMain.interpret(IMain.scala:570)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:894)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:912)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:912)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:912)
        at scala.tools.nsc.interpreter.ILoop.interpretStartingWith(ILoop.scala:912)
        at scala.tools.nsc.interpreter.ILoop.command(ILoop.scala:762)
        at scala.tools.nsc.interpreter.ILoop.processLine(ILoop.scala:464)
        at scala.tools.nsc.interpreter.ILoop.loop(ILoop.scala:485)
        at scala.tools.nsc.interpreter.ILoop.process(ILoop.scala:1077)
        at scala.tools.nsc.MainGenericRunner.runTarget$1(MainGenericRunner.scala:89)
        at scala.tools.nsc.MainGenericRunner.run$1(MainGenericRunner.scala:92)
        at scala.tools.nsc.MainGenericRunner.process(MainGenericRunner.scala:103)
        at scala.tools.nsc.MainGenericRunner$.main(MainGenericRunner.scala:108)
        at scala.tools.nsc.MainGenericRunner.main(MainGenericRunner.scala)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.pantsbuild.tools.runner.PantsRunner.runMainMethod(PantsRunner.java:98)
        at org.pantsbuild.tools.runner.PantsRunner.main(PantsRunner.java:42)

com.google.inject.CreationException: Unable to create injector, see the following errors:

1) An exception was caught and reported. Message: Could not convert scalaType SomeClient with Augmentation to a javaType: scala.reflect.internal.Types$RefinedType0
  at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:137)

1 error
  at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:554)
  at com.google.inject.internal.InternalInjectorCreator.initializeStatically(InternalInjectorCreator.java:161)
  at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:108)
  at com.google.inject.Guice.createInjector(Guice.java:87)
  at com.google.inject.Guice.createInjector(Guice.java:69)
  at com.google.inject.Guice.createInjector(Guice.java:59)
  ... 38 elided
Caused by: java.lang.UnsupportedOperationException: Could not convert scalaType SomeClient with Augmentation to a javaType: scala.reflect.internal.Types$RefinedType0
  at net.codingwell.scalaguice.TypeConversions$.scalaTypeToJavaType(TypeConversions.scala:89)
  at net.codingwell.scalaguice.package$.typeLiteral(package.scala:37)
  at net.codingwell.scalaguice.InternalModule$BindingBuilder.<init>(ScalaModule.scala:70)
  at net.codingwell.scalaguice.InternalModule.bind(ScalaModule.scala:75)
  at net.codingwell.scalaguice.InternalModule.bind$(ScalaModule.scala:75)
  at $anon$1.bind(<console>:19)
  at $anon$1.configure(<console>:21)
  at com.google.inject.AbstractModule.configure(AbstractModule.java:61)
  at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:347)
  at com.google.inject.spi.Elements.getElements(Elements.java:104)
  at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:137)
  at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:105)
  ... 41 more

scala>

And like before this worked in scalaguice 4.2.0 pre the transition from Manifests to TypeTags.

I wanted to also point out that translation from Scala types to Java types is something we have to do a lot in JSON serialization/deserialization and after years of having our own hand-rolled stuff we switched to make use of the reflection portion of the json4s library which handles a fair amount of these cases -- not sure if it's worth taking a look but thought I'd mention.

Any help here would be greatly appreciated as this blocks our Finatra library upgrade to the latest version of scalaguice which enables us to get to Scala 2.13 support. Thanks!

Release for Scala 2.10.0

First of all, thanks a lot for this nice project. It is very useful.

Once issue: I was trying to use 3.0.1 with Scala 2.10.0, which has been released recently. Unfortunately, I get errors like

java.lang.NoClassDefFoundError: scala/reflect/ClassManifest
    at net.codingwell.scalaguice.ScalaModule$$anon$1.<init>(ScalaModule.scala:67)
    at net.codingwell.scalaguice.ScalaModule$class.bind(ScalaModule.scala:62)

Do you see a possibility to publish a 2.10.0 build to the Typesafe Maven repository http://repo.typesafe.com/typesafe/repo/net/codingwell/?

I hope I am asking at the right place, here. Please let me know otherwise.

Best,
Kaspar

java.lang.NoClassDefFoundError: net/codingwell/scalaguice/InternalModule$BindingBuilder

My project uses guice 4.0.1 My project was working correctly until one of the libraries which I use evicted the 4.0.1 version with 4.1.0

And now my project gets an error

Exception in thread "main" java.lang.NoClassDefFoundError: net/codingwell/scalaguice/InternalModule$BindingBuilder
	at java.lang.Class.getDeclaredMethods0(Native Method)
	at java.lang.Class.privateGetDeclaredMethods(Class.java:2701)
	at java.lang.Class.getDeclaredMethods(Class.java:1975)
	at com.google.inject.internal.ProviderMethodsModule.getProviderMethods(ProviderMethodsModule.java:132)
	at com.google.inject.internal.ProviderMethodsModule.configure(ProviderMethodsModule.java:123)
	at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:340)
	at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:349)
	at com.google.inject.AbstractModule.install(AbstractModule.java:122)
	at com.foo.MainModule.configure(MainModule.scala:18)
	at com.google.inject.AbstractModule.configure(AbstractModule.java:62)
	at com.google.inject.spi.Elements$RecordingBinder.install(Elements.java:340)
	at com.google.inject.spi.Elements.getElements(Elements.java:110)
	at com.google.inject.internal.InjectorShell$Builder.build(InjectorShell.java:138)
	at com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator.java:104)
	at com.google.inject.Guice.createInjector(Guice.java:96)
	at com.google.inject.Guice.createInjector(Guice.java:73)
	at com.foo.FooInjector$.initializeWith(FooInjector.scala:35)
	at com.foo.FooInjector$.initialize(FooInjector.scala:26)
	at com.foo.Foo$.main(Foo.scala:43)
	at com.foo.Foo.main(Foo.scala)
Caused by: java.lang.ClassNotFoundException: net.codingwell.scalaguice.InternalModule$BindingBuilder
	at java.net.URLClassLoader.findClass(URLClassLoader.java:381)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
	at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331)
	at java.lang.ClassLoader.loadClass(ClassLoader.java:357)

I am not sure if there is a migration path from 4.0.1 to 4.1.0? what changes are required to be made to the code so that it works with 4.1.0?

cannot bind or create instance of scalaz reader (due to miss of Manifest)

import com.google.inject._
import net.codingwell.scalaguice._

import scalaz.Reader

class AModule extends AbstractModule with ScalaModule {
def configure() {
bind[Reader[Int, Int]].toInstance(Reader { (_: Int) * 2 })
}
}

gives me error: "Manifest of Reader[Int, Int] is not found"

so I have to resort to the legacy way of creating guice module

class AnotherModule extends AbstractModule {
def configure() {
bind(classOf[Reader[Int, Int]]).toInstance(Reader { (_: Int) * 2 })
}
}

but then when I tried to create instance using scalaguice, got the same error again

val injector = Guice.createInjector(new AnotherModule())

injector.instance[Reader[Int, Int]] // same error as above

getInstance() with constructor parametrs

Hello,
I'm using guise version 4.1.0 in my project, but I'm not right with getInstance () function. injector.getInstance (classOf [startgame]), where startgame has to take a parameter. But if I do parameter purely, I take a error "starting game does not take a parameter." Can someone explain that to me? Thanks in advance.

Use javax.inject.Provider

If you use import: import javax.inject.Provider you cannot use bind[A].toProvider[AProvider], but tou must use bind[A].toProvider(classOf[AProvider])

Example:

trait A {
  def foo(): String
}

class AProvider extends javax.inject.Provider[A] {
  def get(): A = new A {
    def foo(): String = "foo"
  }
}

class AModule extends AbstractModule with ScalaModule {
  def configure() {
    bind[A].toProvider(classOf[AProvider]) // ok
   //bind[A].toProvider[AProvider] // fails!
  }
}

Solution:
argument in net.codingwell.scalaguice.ScalaLinkedBindingBuilder.toProvider should be javax.inject.Provider insted of com.google.inject.Provider (note that Provider from guice only extends Provider from javax.inject).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.