You will have to explain the dynamic marshaller more fully. Why should the server side be able to arbitrarily change the protocol it unmarshalls without some mechanism of expressing this to the client? If that is really needed then the client should provide no marshaller and it should be picked up from the server based on a runtime query of the transport properties.
Can you put something under the covers that has the client pull down the marshaller from the server side if it isn't found on the client? Of course, this means that this "pre-request" would have to use a default, well known, marshaller. The server-side would get this pre-request, send back the, say, the class name of the custom marshaller, then the client now has the what you need. It takes the class name and instantiates the marshaller and uses it for the "real" request. The client-side MarshalFactory could remember this so it would only have to do this "pre-request" once.
Sort of like dynamic classloading.
All of this could be under the covers - client doesn't need to know about it. The server-side would have to be coded up to accept this "pre-request" - maybe the first few bytes of the pre-request is a magic cookie value that if detected, the server-side reverts to the well know marshaller, looks up the marshaller class based on the name given to it via the pre-request and sends back the class name.
Did that make sense? Its' alot of work but - I see no other way than to encode the class name like you requested.
Another way - you could play games with system properties. I've done this with factories I've built before. Say your locator is this:
Your factory would look it up and not find it. So, it falls back to looking at a system property using that dataType name as part of the system property. The system property value is a class name. Your system property would look like this:
Your factory would look for a system property called "jboss.remoting.datatype.<the value from locator>". You can therefore define multiple ones - each one has a different system property.
This of course, woudl require the client to set up these system properties via some config file or cmdline args.
The MarshalFactory has two marshallers/unmarshallers that are statically defined within a static block (Serializable and HTTP). Since these are core to remoting and part of the remoting package, is ok to have them statically defined. So if the remoting server is created with the locator url having dataType=serializable for example, is ok because when the remoting client asks the MarshalFactory for the marshaller for type 'serializable' it will return the SerializableMarshaller, since it is statically defined.
However, if the server adds a custom marshaller/unmarshaller to the MarshalFactory when it starts up and reflects this in its locator url (via dataType param), then the client is only aware of the dataType value and asks the MarshalFactory for the appropriate marshaller/unmarshaller. Problem is, the MarshalFactory in the client VM will not be aware of this new custom marshaller/unmarshaller (since was added on the server side).
The real world example for this is the UnifiedInvoker. So when the UnifiedInvoker starts, it will add the InvocationMarshaller/InvocationUnMarshaller to the MarshalFactory with the data type of 'invocation'. The server invoker is started with it's locator url having dataType=invocation as part of it's parameter. For example:
Then on the client side, when the UnifiedInvokerProxy is re-constituted in the client VM (from doing the JNDI lookup), it only knows about the locator url (i.e. socket://localhost:8081/?datatype=invocation). So when the remoting client is dynamically created on the client by the UnifiedInvokerProxy, it will try to look up the appropriate marshaller/unmarshallers for the client based on the data type (i.e. invocation). Since the MarshalFactory on the client does not have anything registered for type 'invocation', it will return null.
I have hacked the UnifiedInvokerProxy to add the InvocationMarshaller/InvocationUnMarshaller to the MarshalFactory with associated type being 'invocatin' within a static block to make sure they are there. This is obviously very bad.
I do like your idea of having the client go back to ask the server for the marshaller/unmarshaller if it does not exist on the client, since would make it very flexible (although a little slow for thei first invocation). However, think this shoudl be done using dynamic, remote classloading, was would want to pull them from the server. Guess could just get the fully qualified class name from the server and try to load it on the client, then actually pull the classes from the server if they do not exist locally, but would then have to worry about version conflicts between the client's jars and the server's jars. Obviously the ultimate goal would be to not even require a jboss-client.jar on the client side.
Wow. Wish I had hit refresh on this page before writing my response (almost the exact same).
maybe the first few bytes of the pre-request is a magic cookie value that if detected, the server-side reverts to the well know marshaller, looks up the marshaller class based on the name given to it via the pre-request and sends back the class name.
This is the rub. Will think some more on the best way to initially handle this request on the server side. Certainly think this is going to require me putting all the dynamic classloading code back in, which is going to eat up some time.
In the case of the UnifiedInvoker, this is a layer on top of remoting that wants to introduce a serialization configuration. This has to come in pairs. Introduction of an extension of the remoting framework on the server side needs to have a component on the client side which propgates this configuration information. If the service locator maps to a proxy bound into jndi then the marshaller info should be bound to the proxy. If the service locator maps to some registry, then the registry should hold the marshaller information. As John mentions, one type of base marshaller directly supported by the remoting framework could be one with an embedded registry that include a header packet which the client uses on the initial connection to obtain the application specific marshaller.
Binding the marshaller information to the proxy works in the case of Unified Invoker, but would prefer a more generic solution that applies for all remoting users.
Will go for the approach that has the client call back to the server if tries looking up a marshaller that it does not know about. This should work for all cases.
As I was getting geared up to implement this I realized that there is yet another issue with doing it this way. The problem is that the marshaller is the closest thing to the wire. Therefore, if someone implements a custom UnMarshaller (since it is just an interface), there is no way to ensure that they unmarshall the request for a marshaller correctly.
The only way this would work would be to force people to extend a base unmarshaller that I supply, which would intercept the 'I need XXX marshaller/unmarshaller' requests. Although this is not terrible, I wonder if this is partical? Guess AOP can come to the rescue here? Is it acceptable to use AOP for remoting (since AOP depends on remoting)?
It should not be a requirement that this marshaller handshake be supported. There is nothing wrong with requiring that the client and server have to be in synch with regard to the marshaller configuration in order to communicate.
The ability to query for the marshaller either requires an admin message using some well know format (marshaller) or an abstraction that can even use some secondary channel to obtain the marshaller configuration (registry, local properties file, etc.). I don't see this an aop issue.
I had thought about the secondary channel, but think this just adds another piece, which will add to complexity.
Was originally thinking could use AOP to add my own code to be called before the unmarshaller?s read() so could inspect the stream (looking for marshaller/unmarshaller request). Even if could put this in, then adds a lot of overhead as would have to do it for every call and then basically feed back in the bytes to the target marshaller.
That does not make sense to me and is why this is not an aop issue as its only the initial handshake that needs to negotiate the marshallers. The aspect is the marshaller configuration and if aop fits, it fits as a wrapper around the remoting framework to establish its configuration.
Fundamentally the marshallers are a configuration issue and as such it is another piece on top of the bare wire transport. You cannot use the wire without some choice of a marshaller. In order to use the wire to select the marshallers there has to be a default for the initial handshake exchange and ability to reconfigure the marshalling layer to the new agreed on marshallers.
But there is no handshake. The server side just listens on the wire for anyone who wants to send it data. It then takes the stream and tries to convert it via whatever format it chooses.
If the server happens to be configured to use Foo unmarshaller and the client streams data to the server, the Foo unmarshaller will throw an exception if it does not understand how to unmarshall that data (in the case the client is asking for a Foo marshaller). The data the client sent is lost, so there is no way to throw this request up to a higher level.
Right, which is why there has to be a handshake based on some default marshaller. If you suddenly start speaking martian in this thread then nothing is going to come from it. There would have to be a handshake to switch from english to some other language.
The handshake could be a transient aspect of the communication setup. A channel which supports negotiation of the marshallers using the transport layer must be able to peform a handshake using default marshallers, or it has to occur using a secondary mechanism.
I have come up with two solutions for this. This first one is to provide datatype, marshaller, and unmarshaller as parameters to the locator url. The datatype is whatever name want to associate the custom marshaller/unmarshaller under. The values for marshaller/unmarshaller parameters is the fully qualified class name for the custom classes. They will then be dynamically loaded if exist on the classpath (of course requires that they do exist on both the client and the server). This has already been implemented and checked into CVS HEAD for jboss-head.
The second is to provide a port via the locator url which will signify the port on which the marshalling service will reside. This will be using the plain, default marshaller to load custom marshallers. This has not been implemented yet (but about to start on it).