N
Nicholas Zigarovich
Hi folks,
I have some RMI questions, and I'd like to start by giving
background on the software and our intentions with RMI.
Our software is a molecular biology analysis and simulation tool.
Since the technology is very proprietary, we've used RMI to split
the Java software into a front-end GUI client that that's
distributed to users and a back-end server that makes use of our
in-house C++ tools.
At the moment our user base is small, mostly a handful of academic
researchers, but even with these few releases are getting difficult
to manage. The problem, of course, is that when the server back-end
is upgraded, if any serializable or remotable APIs are changed, the
user must also upgrade their client. Our users have made it clear
that this is an unacceptable inconvenience, and since we frequently
push new feature-filled releases, its difficult to keep the APIs
stable. Additionally, 40% of the original code base is absolute
tripe, the inflexible, ill-thought, and monolithic work of a
contractor who was at at deadline and had to 'just get the job
done'. With only two developers (one of them part-time), we are
forced to rewrite portions of this old code in small spurts as most
of our time is taken up either adding features or working on the C++
tools. In short, there's simply no way we can stabilize our APIs at
this time.
My first (perhaps naive) solution was simple: the client/server
pair are built with a common API version number that is incremented
each time the API of a serializable or remotable API must be changed
for a new release. This version number is appended to the RMI URI
for the server. For example, when using API version 13, if the
back-end service is told to listen on the following URI:
//remote.mycompany.com/myobject
....the back-end would implicitly bind to...
//remote.mycompany.com/myobject-13
Since the client and server are built with the same API version,
the client knows to append -13 to whatever URI the user specifies to
connect to a back-end with a compatible API. This lets the user
think they're connecting to the 'usual' URI, but allows us to run
servers with varying APIs concurrently on the same physical machine,
thus our users can upgrade at their leisure and we can push new
releases whenever we please.
I prefer this solution because its very non-intrusive code-wise
and allows us developers complete freedom, but it seems prone to
subtle problems. We have three releases (hence three servers) and
clients are able to function initially, but eventually they start
reporting not-bound and class UID/checksum errors - exactly what
this system is supposed to prevent. The three services appear to
work when started initially but fail sporadically after some period
of time. It seems to me that the RMI registry is getting confused
when deciding which classes from which service should be sent to
which client.
I should note that rmiregistry requires us to pass in the path to
our classes to its JVM (with -J) or it throws NoClassDefFoundError
errors when a client connects to the back-end service (if memory
serves, our logger is the offending class, so perhaps the registry
is executing a static initializer that prints log messages or
something). We only pass in the path to one set of classes right now
(the oldest of our API revisions). I'm not sure if passing all three
would help, but I don't see how it would, and I really don't have
the time to experiment. Interestingly, this class path requirement
only started happening a few weeks ago - previously rmiregistry
didn't seem to care where our logging classes were. I'm not sure if
this is contributing to the concurrency problem stated above, but if
there's reasonable suspicion I'm more than happy to figure out why
this is happening. Also, I remember reading on some web site that
setting the rmiregistry class path with -J could cause other strange
problems relating to RMI stubs, so could this be a likely suspect? I
wish I was more familiar with the guts of RMI as to know what's
going on in the registry. Should I be seeing such problems when
binding multiple versions of the same class instance?
I suppose I'd really like, aside from the informed opinions and
suggestions of experienced Java programmers, is an architectural
overview of the RMI implementation so I can answer these questions
myself, but I haven't found anything in that vein. Ideally I'd like
to look at the code for the RMI implementation. Is this possible?
I'm not 'hip' to Sun's code sharing policies, what portions of
their code base they open up if any. Can anyone offer any hints?
We did come up with some other possible solutions for the API
versioning problem. Dynamic class loading via HTTP in particular is
my next best hope. Tentatively speaking, it seems that this would
help minimize API breakage so long as modifications are limited to
adding elements to the API and not removing them. It seems to me
that allowing users to download classes at runtime would allow us
developers more flexibility, even though users would be forced to
upgrade every once in a while, albeit less frequently than they
otherwise would.
Also considered was a separate tool for automated management of
the client software, i.e., an auto-update utility. I'd rather not
go down this path; I think it would annoy our users, and our
two-man development team is busy enough without worrying about more
cruft.
Any additional suggestions are welcome, as is any insight into
the Sun's RMI implementation. Advice, links to documentation, "hey
stupid, google for <somthing obvious>" would be appreciated.
Cheers
-Nick
<[email protected]>
PS - Sorry for the long length of this message. If you're still
reading, thanks. ;-)
I have some RMI questions, and I'd like to start by giving
background on the software and our intentions with RMI.
Our software is a molecular biology analysis and simulation tool.
Since the technology is very proprietary, we've used RMI to split
the Java software into a front-end GUI client that that's
distributed to users and a back-end server that makes use of our
in-house C++ tools.
At the moment our user base is small, mostly a handful of academic
researchers, but even with these few releases are getting difficult
to manage. The problem, of course, is that when the server back-end
is upgraded, if any serializable or remotable APIs are changed, the
user must also upgrade their client. Our users have made it clear
that this is an unacceptable inconvenience, and since we frequently
push new feature-filled releases, its difficult to keep the APIs
stable. Additionally, 40% of the original code base is absolute
tripe, the inflexible, ill-thought, and monolithic work of a
contractor who was at at deadline and had to 'just get the job
done'. With only two developers (one of them part-time), we are
forced to rewrite portions of this old code in small spurts as most
of our time is taken up either adding features or working on the C++
tools. In short, there's simply no way we can stabilize our APIs at
this time.
My first (perhaps naive) solution was simple: the client/server
pair are built with a common API version number that is incremented
each time the API of a serializable or remotable API must be changed
for a new release. This version number is appended to the RMI URI
for the server. For example, when using API version 13, if the
back-end service is told to listen on the following URI:
//remote.mycompany.com/myobject
....the back-end would implicitly bind to...
//remote.mycompany.com/myobject-13
Since the client and server are built with the same API version,
the client knows to append -13 to whatever URI the user specifies to
connect to a back-end with a compatible API. This lets the user
think they're connecting to the 'usual' URI, but allows us to run
servers with varying APIs concurrently on the same physical machine,
thus our users can upgrade at their leisure and we can push new
releases whenever we please.
I prefer this solution because its very non-intrusive code-wise
and allows us developers complete freedom, but it seems prone to
subtle problems. We have three releases (hence three servers) and
clients are able to function initially, but eventually they start
reporting not-bound and class UID/checksum errors - exactly what
this system is supposed to prevent. The three services appear to
work when started initially but fail sporadically after some period
of time. It seems to me that the RMI registry is getting confused
when deciding which classes from which service should be sent to
which client.
I should note that rmiregistry requires us to pass in the path to
our classes to its JVM (with -J) or it throws NoClassDefFoundError
errors when a client connects to the back-end service (if memory
serves, our logger is the offending class, so perhaps the registry
is executing a static initializer that prints log messages or
something). We only pass in the path to one set of classes right now
(the oldest of our API revisions). I'm not sure if passing all three
would help, but I don't see how it would, and I really don't have
the time to experiment. Interestingly, this class path requirement
only started happening a few weeks ago - previously rmiregistry
didn't seem to care where our logging classes were. I'm not sure if
this is contributing to the concurrency problem stated above, but if
there's reasonable suspicion I'm more than happy to figure out why
this is happening. Also, I remember reading on some web site that
setting the rmiregistry class path with -J could cause other strange
problems relating to RMI stubs, so could this be a likely suspect? I
wish I was more familiar with the guts of RMI as to know what's
going on in the registry. Should I be seeing such problems when
binding multiple versions of the same class instance?
I suppose I'd really like, aside from the informed opinions and
suggestions of experienced Java programmers, is an architectural
overview of the RMI implementation so I can answer these questions
myself, but I haven't found anything in that vein. Ideally I'd like
to look at the code for the RMI implementation. Is this possible?
I'm not 'hip' to Sun's code sharing policies, what portions of
their code base they open up if any. Can anyone offer any hints?
We did come up with some other possible solutions for the API
versioning problem. Dynamic class loading via HTTP in particular is
my next best hope. Tentatively speaking, it seems that this would
help minimize API breakage so long as modifications are limited to
adding elements to the API and not removing them. It seems to me
that allowing users to download classes at runtime would allow us
developers more flexibility, even though users would be forced to
upgrade every once in a while, albeit less frequently than they
otherwise would.
Also considered was a separate tool for automated management of
the client software, i.e., an auto-update utility. I'd rather not
go down this path; I think it would annoy our users, and our
two-man development team is busy enough without worrying about more
cruft.
Any additional suggestions are welcome, as is any insight into
the Sun's RMI implementation. Advice, links to documentation, "hey
stupid, google for <somthing obvious>" would be appreciated.
Cheers
-Nick
<[email protected]>
PS - Sorry for the long length of this message. If you're still
reading, thanks. ;-)