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