3

I'm getting some weirdness when I'm casting a Dynamic Proxy Class to the object I want it to be. At runtime, under certain conditions, I receive a ClassCastException.

In order to explain this better, here are the definitions for the classes/interfaces I want to use. Brackets have been put around any extended interfaces that (should be) irrelevant.

public interface CommandSender (extends Permissible)
public interface ConsoleCommandSender extends CommandSender, (Conversable)
public interface Player extends (HumanEntity, Conversable), CommandSender, (OfflinePlayer, PluginMessageRecipient)

Full Javadocs can be found here: http://jd.bukkit.org/apidocs/org/bukkit/command/CommandSender.html

Now, here is the code for my proxy class:

public class CommandSignsMessagingProxy implements InvocationHandler {

    private Object sender;
    private Object receiver;
    private boolean silent;

    public static Object newInstance(Object proxy) {
        return newInstance(proxy, proxy, false);
    }
    public static Object newInstance(Object proxy, boolean silent) {
        return newInstance(proxy, proxy, silent);
    }
    public static Object newInstance(Object sender, Object receiver) {
        return newInstance(sender, receiver, false);
    }
    public static Object newInstance(Object sender, Object receiver, boolean silent) {
        return Proxy.newProxyInstance(
                sender.getClass().getClassLoader(),
                sender.getClass().getInterfaces(),
                new CommandSignsMessagingProxy(sender, receiver, silent));
    }

    private CommandSignsMessagingProxy(Object sender, Object receiver, boolean silent) {
        this.sender = sender;
        this.receiver = receiver;
        this.silent = silent;
    }

    // Is called whenever a method is invoked
    public Object invoke(Object p, Method m, Object[] args) throws Throwable {
        Object result = null;
        try {
            String name = m.getName();
            // If the receiver is being sent a message, only do so if the silent flag is not set
            if (name == "sendMessage" || name == "sendRawMessage") {
                if (!silent && receiver != null)
                    result = m.invoke(receiver, args);
            } else {
                result = m.invoke(sender, args);
            }
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        } catch (Exception e) {
            throw new RuntimeException("Unexpected invocation exception: " + e.getMessage());
        }
        return result;
    }

}

And here is a fully working instance of the class:

Player proxy = (Player)CommandSignsMessagingProxy.newInstance(player, false);
proxy.sendMessage("Hi! Silent is turned off, so you can see this!");
proxy.setOp(true);
proxy.other_stuff();

Yet, this one doesn't work:

ConsoleCommandSender ccs = plugin.getServer().getConsoleSender();
CommandSender cs = (CommandSender)CommandSignsMessagingProxy.newInstance(ccs, false);

At run time, this example would produce the following:

java.lang.ClassCastException: $Proxy18 cannot be cast to org.bukkit.command.CommandSender
Chris Watts
  • 6,197
  • 7
  • 49
  • 98
  • How about an [SSCCE](http://sscce.org/)? (You have posted code unrelated to the problem, and not posted all the code we need to run it). Hint: by omitting `public` from the class declaration, they can all go in one file for simple copy&pasting. – meriton Sep 29 '12 at 12:52

1 Answers1

4

The created proxy class need to pass the interfaces it suppose to implement,

return Proxy.newProxyInstance(
                sender.getClass().getClassLoader(),
                sender.getClass().getInterfaces(),
                new CommandSignsMessagingProxy(sender, receiver, silent));

failure seems to happen because CommandSender interface may not be returned from the call sender.getClass().getInterfaces() method. So try to see if it properly passes by debugging. If not try sending the interface manually to the method and see if it works.

Low Flying Pelican
  • 5,974
  • 1
  • 32
  • 43
  • 1
    Yep, looks like that's the case. Any ideas on an elegant fix? – Chris Watts Sep 29 '12 at 17:19
  • 1
    how about using something like,public static T newInstance(Object sender, Object receiver), where T is required interface and when creating instance make sure to add T to the list of interface. And casting is not needed when method returns – Low Flying Pelican Oct 01 '12 at 06:07
  • 1
    Sorry I didn't read this earlier - I like the idea of the T variable, but the solution is incomplete. I just can't understand how it's going to know the interface that it should be able to cast to. I'll post another question on doing this. – Chris Watts Oct 12 '12 at 14:11