On 2/1/2011 3:17 AM, Dan Creswell wrote:
So...

On 1 February 2011 01:29, Gregg Wonderly<[email protected]>  wrote:

One of the important things for my use of my changes to the
ServiceRegistrar interface was separating deserialization of the Entrys from
the service object.

Note "separating" used here. I was trying to say that I need to be able to "pick" which Entry objects to deserialize, and deserialize them independent of deserializing the service proxy/object.

Being able to defer downloading was also controlled by the changes to
ClassLoading to include the "neverPrefer" settings on a class name.  I can
do that now through the recent RMIClassLoaderSPI override capabilities.  But
I still need to be able to control which Entry is deserialized.  I need to
be able to ask what classes, by name, are in the namespace of each Entry
object as my implementation allowed. I had made changes to Reggie
marshalling to use reflection to get the class hierarchy, and then provided
access to the list of classnames as part of the returned, marshalled value
in my API.


Digging through classes belonging to a service implementation that is
designed to be encapsulated/not your concern? Does that sound right? I gotta
say, it sounds horribly invasive, very hacky and speaks of an overly complex
solution as the result of treating symptoms, not problems.

Okay, I want to go through the problems that I was dealing with, the attributes of the current mechanisms, and how I inserted points of control to manage these issues.

First, let's look at the simple client mechanisms I wanted to have. I have a desktop environment, which does lookup, and then shows treeviews of services based on "machine"->"group"->Service Instance structure. The deployed services contain Entry objects that provide values for these three name spaces. Since I need to look at these, they can not be preferred by the service or I can't read them. There may be a subclass that the client has used, and that can be preferred no problem. There are some other items in the Entrys that I need too, such as icons. Any of the Entry objects that I need access to, and any classes which they are dependent on, and which are publicly visible classes/interfaces, because of that attribute (public) can not be realistically preferred.

So my first assertion is that Entry objects will hardly ever be preferred, and thus downloading of the PREFERRED.LIST is not a necessary step to correctly resolve them for my use.

The next issues is this PREFERRED.LIST file. The implementation of that technology is that the file is retrieved from the first jar file listed in the codebase. So, in order to resolve any class in the proxy or Entry values, there must be a download from the associated codebase server.

Now, one of the first things that I think about with distributed computing is the business of partial failure and limiting that impact to the overall performance of the system. My deployments at my customers' sites consist of copies of the same service platform with the same services on it. There are copies for both redundancy and for capacity.

Each instance of my platform has as a starting point 20 some services each with a separate codebase, but with many common jars such as jsk-dk.jar. If a lookup service responds with a list of services but the codebase server doesn't respond with jars, then any attempt to download limits the performance of the display of available services, and I considered an "incorrect list of available services" less important than non-display or slow-display of available services.

The technicians in my customers' business have a lot of windshield time and use their cellular connectivity or satellite based WAN networking for many tasks. This means that there is a lot of latency, cost for bandwidth in term of time and bandwidth both. So, for me to make a distributed network based services environment work, I have to limit network to only what is absolutely necessary.

So, back to the two different changes that I made to service lookup and class resolution. The activities in ClassLoading to provide a "neverPreferred" functionality was instituted by having my client tell ClassLoading via public static API, which Entry classes and dependent classes should never be preferred. Recall, that in the beginning, the class loading mechanisms did have a "neverPreferred" policy for java.lang.* and for native types and arrays.

This was removed in an update later on in 2.1 if I remember right. This kept classloading from being triggered in those cases. That functionality, when removed, was declared as nonessential/unused because normal class loading would keep that from happening. However, because PreferredClassLoading will never ask the parent class loader (especially without my patch to allow RMIClassLoaderSPI to not be used) to resolve a class until the PREFERRED.LIST is on hand, the codebase will always be accessed, requiring a download of at least the first jar.

In my case, a customer with 100 codebases (5 machines) trying to use my desktop client, would experience 100 http connections and downloads of the first jar before they could see all the available services. And, because of the fact that not ever single codebase would be requested in parallel, any stuck codebase could potentially delay the display of available services for 3-5 minutes per problem codebase while TCP connect requests timeout.

For me, this was just not an acceptable outcome. It was simply not possible to manage my product environment with these issues at hand. No matter how much the first jar required to be transferred, it would simply take way too long for them to see a useful client display.

My changes in River-336 for RMIClassLoaderSPI override, use methods such as CodebaseAccessClassLoader.loadClass(...) which can be used to provide the "neverPreferred" functionality. So, I have a pluggable mechanism that allows me to manage this issue. So if no one else has such requirements, there is no need to discuss this as a point of argument for inclusion in River. I think it is a valid point of control because the client can declare the platform that they are compatible with by using this facility to never prefer certain classes which will then cause services overrides to not appear and the client could even provide some feedback when this happens by allow the CodebaseAccessClassLoader implementation to provide some data points about when it finds it is ignoring something declared as preferred.

The other business at hand is the deferred unmarshalling. And in that case, I don't think that there is too much of an argument to be had. If we focus on just allowing the client to access the entries selectively, and defer unmarshalling of the service proxy/object separately, that's the basics of what I need for my environment.

The API that I implemented allowed access to the list of classes and interfaces referred to by the Entry so that the client could discover that it can't unmarshall the Entry because it requires network access to download something that it doesn't have locally. If that usage pattern doesn't seem valuable, then something more along the lines of providing a Class object as Peter's API shows might work well enough. But we need to figure out how to deal with Entry values which are subclasses. If I pass in Name.class, and there are several subclasses of Name visible in the Entry values, then which one would you return? All of them? What if one of those contained a rather large object graph which was not what I wanted. Would I get it unmarshalled so that I could still inspect it to decide if it was the one I wanted to unmarshall and look at?

Sorry for going on and on about this, but I wanted to try and put out all the details that I had dealt with and the thoughts around my path of solution.

Gregg Wonderly

Reply via email to