pclem12
pclem12

Reputation: 449

Scala Invocation Handler causes ClassCastException

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

Answers (1)

Odomontois
Odomontois

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

Related Questions