Reputation: 449
I'm trying to implement a proxy pattern so that I can swap out an underlying instance dynamically under the covers when necessary along with an extension method to trigger the swap. I've implemented this in Java before, but I'm having trouble with it in Scala.
This is my scenario:
class Client { ...library code... }
trait DynamicClient extends Client {
def swap: Client
}
class Provider extends Provider[DynamicClient] {
def get():DynamicClient {
java.lang.reflect.Proxy.newProxyInstance(
classOf[DynamicClient].getClassLoader,
Array(classOf[DynamicClient]),
handler)
.asInstanceOf[DynamicClient]
}
}
class DynamicClientHandler extends java.lang.reflect.InvocationHandler {
var client:Client = createNewClient()
def swap(): {
client = createNewClient()
client
}
def createNewClient():Client: { ... }
def invoke(proxy: AnyRef, method: java.lang.reflect.Method, args: Array[AnyRef]): AnyRef = {
method.getDeclaringClass match {
case dyn if dyn == classOf[DynamicClient] => swap()
case _ => method.invoke(client, args: _*)
}
}
}
Now the problem: When I invoke methods from the DynamicClient or Object on the Proxy object, they work just fine.
val dynamicClient = injector.instanceOf[DynamicClient]
val initial = dynamicClient.client
val client = dynamicClient.swap()
val dynamicClient.toString // "Client@1234" (Object impl of toString via client instance)
assert(client != initial) //passes just fine, the underlying client is re-initialized
Any call to a method belonging to the Client class fails before it ever gets to the Invocation Handler.
//Both of the following scenarios fail independently of the other
//ERROR:
dynamicClient.asInstanceOf[Client]
//ERROR:
dynamicClient.doSomeClientMethod()
With this exception trace:
java.lang.ClassCastException: com.sun.proxy.$Proxy22 cannot be cast to Client
Why do I get this cast exception? Is there a better way to handle proxy invocation handling in Scala vs. the Java way?
Upvotes: 0
Views: 249
Reputation: 16318
Ok. I've tried to make your example really reproducible, here what it have become:
import java.lang.reflect.{Method, Proxy}
class Client
trait DynamicClient extends Client {
def swap: Client
}
def mkClient =
Proxy.newProxyInstance(
classOf[Client].getClassLoader,
Array(classOf[DynamicClient]),
new DynamicClientHandler
).asInstanceOf[DynamicClient]
class DynamicClientHandler extends java.lang.reflect.InvocationHandler {
val client = new Client{}
def invoke(proxy: AnyRef, method: Method, args: Array[AnyRef]): AnyRef =
if (method.getDeclaringClass == classOf[DynamicClient])
swap
else method.invoke(client, args: _*)
def swap = createNewClient
def createNewClient = mkClient
}
mkClient.swap
This example will work as soon you change class
to trait
in definition of Client
.
Why? Because it's clear from the answer you linked in your comment that trait
extending class
is really a restriction, that working only in scala compiler. So from java perspective interface DynamicClient
still has nothing common with class Client
as reflection error says.
So you can't really create Proxy
of a class
and should think of some workaround.
Upvotes: 2