2

searching for a long time, but didn't find any answer on that one:

I have a server and I have a client. The server should receive an object via ObjectInputStream and ObjectOutputStream. That already works for any class, known on the server. Now I want to send an Object of a class the server doesn't know. He only knows the interface of that class. And that obviously fails...

How can I avoid the ClassNotFoundException? I thought interfaces were the solution. I only want to access the functions I know by the interface, but Java wants to have the class anyway.

Thank you.

Shimon Rachlenko
  • 5,469
  • 40
  • 51
manima
  • 21
  • 2

3 Answers3

3

There is no way. To be able to execute a method on an object, you need to have the byte-code of the concrete class of the object. Its interface isn't sufficient.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
  • Well, that's not what I liked to hear ;-) – manima Feb 26 '13 at 13:24
  • So then I'll send the .class to the server and load it, before receiving my object. – manima Feb 26 '13 at 13:27
  • @JBNizet "There is no way" does not sound like an answer for me. Actually there are some ways, i.e. to send `.class` file before sending the object. – Mikhail Vladimirov Feb 26 '13 at 13:28
  • 1
    @MatthiasNiemann You need to make server to receive .class files form client, create `ClassLoader` instance that is able to load these classes and then somehow tell to `ObjectInputStream` to use this classloader which is not a trivial task, because `ObjectInputStream` is a piece of ... em ... strange design. But though, "there is no way" does not sound as correct answer for me. – Mikhail Vladimirov Feb 26 '13 at 13:31
  • What I mean is that "there is no way" to load an object without having its bytecode. Of course, you could design a system where a class's bytecode is sent before the object, and a specific classloader is used in order to load the class and instantiate the object. But that is an very hard task. Just sending the class of the object is not sufficient. You also need to send its dependencies, recursively. And you'd better not do that for every object sent. So it's very, very, very far from trivial, and I wouldn't advise going this way. – JB Nizet Feb 26 '13 at 15:31
  • @JBNizet There is a big difference for me between "there is no way" and "this is a very hard task". The former is depressive, while the latter is challenging. – Mikhail Vladimirov Feb 27 '13 at 05:20
  • @JBNizet I do not think sending dependencies is such a big deal. Algorithm for doing that would be trivial, byte size isn't so bad and how many of those would that be? only a few in most cases. You only need to find a way how to send only those classes that you know are not located in server. This could be an amazing little framework. – MarianP Feb 27 '13 at 17:46
0

Object of some class may exist in JVM only if JVM has this class loaded. Probably instead of sending objects of unknown type, you better send some well-known data structure that describes the object, i.e. HashMap. So, instead of doing:

MySecretClass o = new MySecretClass ();
o.setFoo ("Hello, World!");
o.setBar (123);
send (o);

you do the following:

Map o = new HashMap ();
o.put ("foo", "Hello, World!");
o.put ("bar", 123);
send (o);
Mikhail Vladimirov
  • 13,572
  • 1
  • 38
  • 40
  • I already thought of that - but I want to access a method in that object, not only the data in it. – manima Feb 26 '13 at 13:22
  • @MatthiasNiemann If you need methods, then you definitely need `.class` file of the class to be available on server, because logic of the methods is in classes, not in objects. Consider sending `.class` files from client to server. – Mikhail Vladimirov Feb 26 '13 at 13:24
  • There are other ways. For example you may send object using RMI instead of serialization. In this case, when server will call method on received object, the call will be marshalled to the client, client will execute the method and will send result back to the server. – Mikhail Vladimirov Feb 26 '13 at 13:33
  • I believe RMI only defines a different channel for passed objects. And the issue remains the same. It's a pretty interesting question I have been thinking about for while though. – MarianP Feb 28 '13 at 20:59
  • @MarianP RMI passes only object identity (i.e. reference), not object itself. – Mikhail Vladimirov Feb 28 '13 at 21:38
  • sorry for not looking it up. I just remembered that RMI requires a class to be serializable, or is it wrong? – MarianP Feb 28 '13 at 22:10
  • @MarianP Object which methods are invoked remotely, only identity is transferred. For method parameters and return values, either identities or objects itself are transferred depending on whether they are remote objects themselves. – Mikhail Vladimirov Mar 01 '13 at 05:20
0

You need the particular class' bytecode on server, so it won't work as you imagined.

You could send a proxy object of a class that would be known to both the client and the server. The proxy would contain the object of the class unknown to the server and its class would be stored in the proxy as a byte array. The proxy class would be Externalizable and would use Classloader.defineClass to dynamically create the unknown class in the server JVM when the proxy object is deserialized (using the byte array with class definition passed from the client). It won't be exactly fast, but could be optimized, like do not send and load definition of a class twice etc.

In case you need to call the interface specific methods directly on the object passed from client, make the proxy implement your interface and use dynamic proxy pattern on the proxy object (java.lang.reflect.Proxy/InvocationHandler) to pass calls to the proxied object of the previously unknown class.

It's rather complicated, but could be done like this, I believe.

MarianP
  • 2,719
  • 18
  • 17
  • After some discussion under a different answer, it's worth noting that one would need to send the class' dependencies too. – MarianP Feb 27 '13 at 17:47