Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ bind[A].toProvider[BProvider]
bind[A].toProvider[TypeProvider[B]]
bind[A[String]].to[B[String]]
bind[A].to[B].in[Singleton]

bindInterceptor[A,B]
```

### Multibinding
Expand Down Expand Up @@ -190,6 +192,18 @@ If you call `mapBinder.permitDuplicates()` on the binder then you may also injec
- `immutable.Map[K, immutable.Set[V]]`
- `immutable.Map[K, immutable.Set[Provider[V]]]`

### Interceptor Binding

bindInterceptor adds scala style interceptor binding

```java
bindInterceptor(Matchers.any(), Matchers.annotatedWith(classOf[Logging]), new LoggingInterceptor())
```

```scala
bindInterceptor[Logging, LoggingInterceptor]
```

## Gotchas

In Scala, the words `override` and `with` are reserved and must be escaped to be used.
Expand Down
15 changes: 14 additions & 1 deletion src/main/scala/net/codingwell/scalaguice/ScalaModule.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,13 @@
*/
package net.codingwell.scalaguice

import com.google.inject.{PrivateModule, PrivateBinder, Binder, Scope, AbstractModule}
import binder._
import com.google.inject.matcher.Matchers
import com.google.inject.{PrivateModule, PrivateBinder, Binder, Scope, AbstractModule}
import java.lang.annotation.Annotation
import javax.inject.Provider
import org.aopalliance.intercept.MethodInterceptor


/**
* Allows binding via type parameters. Mix into <code>AbstractModule</code>
Expand All @@ -33,6 +36,8 @@ import javax.inject.Provider
* bind(classOf[CreditCardPaymentService])
* bind(new TypeLiteral[Bar[Foo]]{}).to(classOf[FooBarImpl])
* bind(classOf[PaymentService]).to(classOf[CreditCardPaymentService])
*
* bindInterceptor(Matchers.any(), Matchers.annotatedWith(classOf[Logging]), new LoggingInterceptor())
* }
* }
* }}}
Expand All @@ -44,6 +49,8 @@ import javax.inject.Provider
* bind[CreditCardPaymentService]
* bind[Bar[Foo]].to[FooBarImpl]
* bind[PaymentService].to[CreditCardPaymentService]
*
* bindInterceptor[Logging, LoggingInterceptor]
* }
* }
* }}}
Expand All @@ -63,6 +70,12 @@ trait InternalModule[B <: Binder] {
val self = myBinder.bind(typeLiteral[T])
}

protected[this] def bindInterceptor[T <: Annotation : Manifest, T2: Manifest] {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rename the type parameters to something more like this:

protected[this] def bindInterceptor[A <: Annotation : Manifest, I <: MethodInterceptor : Manifest] {
 // ...
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

val myBinder = binderAccess
val interceptor = manifest[T2].runtimeClass.newInstance.asInstanceOf[MethodInterceptor]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can drop the asInstanceOf[...] cast if you properly bind the Interceptor Type Param.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is mean?
If I dros asInstanceOf[...] it has compile error.

[error] found : Any
[error] required: org.aopalliance.intercept.MethodInterceptor
[error] myBinder.bindInterceptor(Matchers.any(), Matchers.annotatedWith(cls[A]), interceptor)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh weird. You're right: manifest[T].runtimeClass returns a Class[_]. I thought it was a Class[T]. Sorry about that.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

myBinder.bindInterceptor(Matchers.any(), Matchers.annotatedWith(cls[T]), interceptor)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to see Matchers as a parameter to bindInterceptor. Matching all objects is not always the right answer.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also add a call to requestInjection(interceptor). It's harmless for objects that have no injections.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's "requestInjection(interceptor)" this mean?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plesae help me one thing,
I don't know how to pass Matcher as a parameter.

I tried bindInterceptor[m: Matcher[_ >: Class[_]], A <: Annotation : Manifest, I <: MethodInterceptor : Manifest]

It show compile error "com.google.inject.matcher.Matcher[_ >: Class[_]] does not take type parameters"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

requestInjection is a method call that you can make in the binder, maybe you need myBinder.requestInjection.

If your interceptor has any @Inject setters then it will invoke them when the injector is created.

About the matcher as a parameter:
I'm trying to think about how to structure this for other people to use and I'm not sure the best way to generalize it. Github sent my comment off right away without me having a chance to think twice about it. It's a little odd because technically speaking all three of the parameters are just that, parameters. There's very little that you can do generalize them.

What I had meant was to do something like this:

def bindInterceptor[A ..., I ...] { 
  bindInterceptor[A, I](Matchers.any())
}

def bindInterceptor[A ..., I ...](m: Matcher[_ >: Class[_]]) {
  ...
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow really thank you.

}

protected[this] def bindScope[T <: Annotation : Manifest](scope: Scope) = binderAccess.bindScope(cls[T], scope)
protected[this] def requestStaticInjection[T: Manifest](): Unit = binderAccess.requestStaticInjection(cls[T])
protected[this] def getProvider[T: Manifest] = binderAccess.getProvider(cls[T])
Expand Down
32 changes: 32 additions & 0 deletions src/test/java/net/codingwell/scalaguice/AOP.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, you've got to stick to the copyright already in the project. Please omit this block.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

* Copyright 2010-2014 Peerapat Asoktummarungsri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.codingwell.scalaguice;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* There is no support for runtime annotation in Scala, so far
* Java interfaces need to be used.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AOP {

}
28 changes: 28 additions & 0 deletions src/test/java/net/codingwell/scalaguice/AOPI.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright 2010-2014 Peerapat Asoktummarungsri
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.codingwell.scalaguice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class AOPI implements MethodInterceptor {

@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return null;
}

}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You might as well write this class in Scala. Stick it in ClassesForTesting.scala.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

11 changes: 10 additions & 1 deletion src/test/scala/net/codingwell/scalaguice/ScalaModuleSpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import org.scalatest.{Matchers, WordSpec}

import com.google.inject._

class ScalaModuleSpec extends WordSpec with Matchers {
class ScalaModuleSpec extends WordSpec with Matchers {

"A Scala Guice module" should {

Expand Down Expand Up @@ -123,6 +123,15 @@ class ScalaModuleSpec extends WordSpec with Matchers {
twoStrings.first should be ("first")
twoStrings.second should be ("second")
}

"allow binding annotation interceptro" in {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: interceptor

val module = new AbstractModule with ScalaModule {
def configure() = {
bindInterceptor[AOP, AOPI]
}
}
Guice.createInjector(module).getInstance(classOf[AOPI])
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd prefer to see a test that checks that the interceptor intercepts an annotated method.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For bonus points, you're welcome to add a test with a class annotated with AOP. But by no means is that a requirement to merge this!

}

}