Comments below...
Cheers,
Greg.
On Wed, 2010-05-26 at 07:27, Peter Firmstone wrote:
> Hi Dennis,
>
> Looking back at my earlier reply when you presented the ClassLoader
> structure for Rio, I was unintentionally insensitive.
>
> I didn't mean to pull it apart, probably not the right approach, Rio is
> a success in it's own right!
>
> What I should have said and I may not have communicated too well is that
> I'm introducing a way for Service Proxy's to utilise ClassLoader
> visibility for maximum API class sharing. It is a new feature, to give
> Service implementers the power to share their Service API's, with as
> much local JVM visibility as the Jini Platform Service API's have.
>
Um.. why? API classes are the client's problem. The service
implementer just makes a few interfaces, and possibly a few domain
classes (data types) available, and if the client wants to use them,
includes the api-jar in its classpath. I'd argue that in almost all
cases, the client is compiled against a particular api, so can go ahead
and include those jars in its runtime classpath as well. If a proxy has
its own classes, the codebase classloading mechanism (along with
preferred classes) just puts those classes into their own classloader,
and will garbage-collect the whole classloader if the proxy instance
becomes unreferenced. Surely, even smart proxies will not be so huge
that a little duplication is a problem.
Now, if you're after a generic, portable module mechanism, like OSGi but
with remotely specifiable modules, that's fine, but I'm not convinced
that's in Jini/River's purview.
> As I pointed out there is a compromise, that Service API classes cannot
> be garbage collected. I think that if this can be well managed, using a
> Permission, then it should not affect uptime for critical applications.
>
I'm hard pressed to think of a case where an api class changes without
requiring a redeployment of its clients, which, even if you're in an
application container, would trigger garbage collection of the whole
classloader. Can you give an example?
> With great power comes great responsibility... yada yada yada.
>
In my books, simplicity always wins.
> Cheers,
>
> Peter.
>
> Peter Firmstone wrote:
> > Peter Firmstone wrote:
> >> IDENTICAL TO ANOTHER MESSAGE IN THREAD: Re: Maven repository Entry
> >> was Re: Codebase service?
> >>
> >> Note: A permission will be required to allow a proxy to introduce new
> >> Service API during unmarshalling to prevent against denial of service
> >> attacks.
> > However in the absence of this permission, the additional ServiceAPI
> > jar could be downloaded into the proxy's own ClassLoader, that would
> > require a ClassLoader even for a dumb proxy with an interface you
> > didn't want to load into your Service API space. This would enable
> > unmarshalling of that Proxy. If you, at a later point decided to load
> > that interface into your Service API space, I think you still wouldn't
> > be able to interact with this particular proxy using it, since the
> > interface class identity would be different.
> >
> > Denial of service here means unfairly growing a JVM's use of non
> > garbage collectable classes. Any ideas how to control download proxy
> > memory consumption.
> >>
> >> Hi Dennis,
> >>
> >> It sounds like you remain unconvinced or don't have a need to use common
> >> interfaces for your service proxy's?
> >>
> >> So I guess it's a tread lightly approach, make the feature available to
> >> those that want common Service API (The same interface instance for all
> >> proxy's implementing that interface, so they can be used in collections
> >> or batch operations), for maximum sharing of proxy's with differing
> >> implementations, but let those that don't want to publish their API
> >> continue doing what they usually do. A configuration parameter should
> >> be able to set the desired behaviour.
> >>
> >> The approach I've taken is a simple approach to a complex problem,
> >> alternative approaches leave the complexity in the hands of the
> >> implementer, my approach will enable them to practically ignore it.
> >>
> >> Before you write it off though, to answer your earlier question, I'll
> >> further explain the ClassLoader structure between multiple nodes.
> >>
> >> CLIENT NODE
> >> ________________________________________________
> >> | |
> >> | System |
> >> | ClassLoader |
> >> | | |
> >> | Extension |
> >> | ClassLoader |
> >> | | |
> >> | Jini Platform & |
> >> | Service API |
> >> | ClassLoader |
> >> | | |
> >> | _________|___ |
> >> | | | |
> >> | Application Smart Proxy |
> >> | ClassLoader ClassLoader's |
> >> |________________________________________________|
> >>
> >>
> >> SERVICE NODE
> >> ________________________________________________
> >> | |
> >> | System |
> >> | ClassLoader |
> >> | | |
> >> | Extension |
> >> | ClassLoader |
> >> | | |
> >> | Jini Platform & |
> >> | Service API |
> >> | ClassLoader |
> >> | | |
> >> | _________|_____________________ |
> >> | | | | |
> >> | Service Imp Smart Proxy Parameter Impl |
> >> | ClassLoader ClassLoader's ClassLoader's |
> >> |________________________________________________|
> >>
> >>
> >> All Proxy and Service implementations are free to vary at will, proxy
> >> instances can be shared in collections or iterative operations based on
> >> common Service API supertype's All Proxy's and Services are isolated in
> >> their own Domain, the only way to communicate externally is by using
> >> interfaces and classes in upper level ClassLoaders, they can utilise as
> >> many third party libraries jar archives as they need, these will all be
> >> loaded into a single ClassLoader unique to that Service or Proxy's
> >> Codebase and Principles. The Service or Proxy's namespace will be
> >> totally separate from Application or other Proxy implementation's,
> >> except in the case where sharing is permitted for identical
> >> implementations by the implementation developer.
> >>
> >> Service API should be carefully considered and designed, it forms the
> >> basis of network Dependency Injection, you can discover ANY Service
> >> implementation variant using the same Service API.
> >>
> >> The Service API may include other Service API or Java platform classes.
> >>
> >> public interface SimpleBookService {
> >>
> >> public Book get( Library lib, String name);
> >>
> >> }
> >>
> >> In the SimpleBookService above, given a Library and String name of a
> >> Book, a Book instance is returned by the proxy.
> >>
> >> The contents of simpleBookService-api.jar:
> >>
> >> SimpleBookService.class
> >> Book.class
> >> Library.class
> >>
> >> Let's say a client has extended the Library, with a class called
> >> PrivateLibrary, this class is an implementation class by the client,
> >> that the Service knows nothing about, the Service only uses the Library
> >> API, but it needs the PrivateLibrary.class
> >>
> >> The client makes an archive containing the PrivateLibrary.class publicly
> >> available in an archive called privateLibrary-param.jar. This archive
> >> depends on simpleBookService-api.jar The jar URL is marshalled with the
> >> class when the parameters are sent back to the service. When it gets to
> >> the service implementation, privateLibrary-param.jar is given it's own
> >> ClassLoader and ProtectionDomain and isn't given any Permissions. It is
> >> used by a SimpleBookService implementation to decide which Book to
> >> return to the client, after which, it's garbage collected, eventually if
> >> PrivateLibrary isn't used again its ClassLoader is garbage collected
> >> too.
> >>
> >> Each SimpleBookService proxy implementation will have their own
> >> ClassLoader namespace, but Client Application classes can still use any
> >> Service implementation or as many Service implementations as it can
> >> handle at the same time, all the while treating them as the same Type.
> >>
> >> One SimpleBookService implementation proxy, contains its own
> >> implementation of Book, and while the client is reading the book, it
> >> remains available from the proxy ClassLoader via the Service API Book
> >> interface, when finished reading the book, the proxy, book and
> >> ClassLoader can be garbage collected. Tomorrow, the client might read a
> >> book from another SimpleBookService
> >>
> >> Most Service API will be relatively small bytecodes as most will be
> >> abstract or have simple implementations, the fact they're not garbage
> >> collected, doesn't matter much if the function of the node doesn't
> >> change, the API will remain limited to the subset in use, by the node.
> >>
> >> Note that Jini Platform service implementations like Reggie, Outrigger
> >> etc, will exist in Service Impl ClassLoaders and Smart Proxy
> >> ClassLoaders, only the api will be in the Jini Platform ClassLoader.
> >>
> >> Think of it as an expandable platform, everything is shared using
> >> implementations of Service API classes.
> >>
> >> It's actually a good policy to be liberal with interfaces when building
> >> the Service API classes, even with parameters and return types. Since
> >> extending interfaces is relatively straight forward, you new interfaces
> >> will be discovered by older client software as the old interface while
> >> new implementation code discovers the new interface of your service.
> >> Older nodes will load your new interface classes into the Service API
> >> space when your new proxy versions are unmarshalled. That's why it's
> >> important to maintain backward compatibility in the Service API space.
> >>
> >> So best practise would be to create an experimental djinn group until
> >> your interfaces are stabilised and be prepared to restart your
> >> experimental group on a regular basis.
> >>
> >> I have thought about using OSGi for Service API classes to be served up
> >> so they can be garbage collected, this might work for serialization too
> >> using the context ClassLoader. I have also thought about using
> >> ClassLoader Tree's using bytecode dependency analysis as per Tim
> >> Blackman's research. These things start to get very complicated, just
> >> to be able to flush the Service API classes. Wouldn't it just be better
> >> to use mutiple services that are load balanced, enabling the jvm to be
> >> restarted if we want to?
> >>
> >> There are other ways to make the Service API classes garbage
> >> collectable, such as having a tier filled with Service API ClassLoaders
> >> where each Service and Proxy lives in a child ClassLoader in the tree,
> >> however this presents the problem of what if an application wants to use
> >> many Service API's or a combination, the different Service API classes
> >> couldn't see each other from separate ClassLoaders.
> >>
> >> Something to consider, best regards,
> >>
> >> Peter.
> >>
> >> Dennis Reedy wrote:
> >>> On May 25, 2010, at 710PM, Peter Firmstone wrote:
> >>>
> >>>
> >>>> This is a good question, which gets to the heart of the Jini's
> >>>> pattern.
> >>>>
> >>>> I think the proposed ClassLoader structure will benefit Rio, by
> >>>> enabling increased API commonality and class sharing among Services
> >>>> and their clients.
> >>>>
> >>>> You can get around having to shutdown your jvm if you manage
> >>>> evolution of your API interfaces correctly, set up a separate
> >>>> testing Registrar, to keep new API interfaces out of your
> >>>> deployment, until they have stabilised.
> >>>>
> >>>
> >>> Right now the JVM doesnt need to be shut down at all, services can
> >>> be loaded with different versions, unloaded, etc ... I think you're
> >>> making assumptions here.
> >>>
> >>>> Classes, once loaded into a ClassLoader, cannot be garbage
> >>>> collected, but if your API classes don't change there is no
> >>>> problem, when was the last time ServiceRegistrar changed it's
> >>>> public API? Unlike Jini's platform classes which are set in
> >>>> stone, new API classes can be introduced into older environments.
> >>>>
> >>>
> >>> Right, which is why service implementations get loaded into their
> >>> own class loader. You define the 'platform' as whatever that needs
> >>> to be for your case. For Rio it includes requisite bootstrapping and
> >>> infrastructure technology. For River it most likely just includes
> >>> the River 'platform', or nothing at all.
> >>> Consider ServiceStarter and the class loader created from that
> >>> bootstrapping process. Please explain what is missing from that
> >>> approach? Each service has it's own security policy. Why does this
> >>> need to change? What and how does your approach improve on? To my
> >>> eyes it seems overly complicated.
> >>>
> >>>
> >>>> Lets take Jini Platform services as an example, in Rio's
> >>>> ClassLoader tree below, the Interfaces for the Platform services
> >>>> exist in the CommonClassLoader, all classes in the
> >>>> CommonClassLoader are visible to any class in any child ClassLoader
> >>>> below in the tree.
> >>>>
> >>>> Platform services can be shared freely among all child ClassLoaders.
> >>>>
> >>>> Now take Service-1CL and Service-2CL, lets imagine for a moment
> >>>> that these two services both provide the same service, from
> >>>> different or the same node, it doesn't matter, let's imagine now
> >>>> another node with the same ClassLoader tree structure, which
> >>>> consumes these services.
> >>>>
> >>>> These services have their service interfaces bundled with their
> >>>> CodeSources, both on the client and at the Service, lets say that
> >>>> Service-2CL provides the same service, but has a different
> >>>> implementation. Now there's a client service that consumes these
> >>>> services, performs an operation then discards the service.
> >>>>
> >>>> Now which common API do the two service proxy's share?
> >>>>
> >>>
> >>> Common API? The service proxies dont share anything. They are each
> >>> loaded from an implementation of RMIClassLoaderApi
> >>>
> >>>> This forces you to load both proxy's into the same ClassLoader,
> >>>> making their implementations visible to each other and the client.
> >>>>
> >>>
> >>> Not so sure about that Peter.
> >>>
> >>>
> >>>> By separating the API into, in your case the CommonClassLoader,
> >>>>
> >>>
> >>> APIs are not added to the CommonClassLoader, and I would argue that
> >>> it should not happen. You generally do not want to add classes into
> >>> a class loader that does not get GC'd.
> >>>
> >>>
> >>>> each with their own ProtectionDomains, all Services and clients in
> >>>> that node, share the same API classes and can be isolated in their
> >>>> own ClassLoader's and can have different implementations but share
> >>>> the same common API types.
> >>>>
> >>>> The client service-param.jar is for clients who create new
> >>>> implementations / extend parameters in API methods, the Service
> >>>> server node will require these classes to unmarshall the
> >>>> parameters. Client parameter classes will never be granted
> >>>> permissions.
> >>>>
> >>>> I'll make up some separate ClassLoader tree diagrams showing the
> >>>> client node, the service node and the relationships between remote
> >>>> ClassLoaders.
> >>>>
> >>>
> >>>
> >>>
> >>>> Peter.
> >>>>
> >>>> Dennis Reedy wrote:
> >>>>
> >>>>> If I understand correctly I think this is the crux of the issue. I
> >>>>> dont understand why you need to load all API classes with the same
> >>>>> class loader. FWIW, in Rio we handle the loading (and unloading)
> >>>>> of services with the following structure
> >>>>> (http://www.rio-project.org/apidocs/org/rioproject/boot/package-summary.html#package_description):
> >>>>>
> >>>>>
> >>>>> AppCL
> >>>>> |
> >>>>> CommonClassLoader (http:// URLs of common JARs)
> >>>>> +
> >>>>> |
> >>>>> +
> >>>>> +-------+-------+----...---+
> >>>>> | | |
> >>>>> Service-1CL Service-2CL Service-nCL
> >>>>> AppCL - Contains the main() class of the container.
> >>>>> Main-Class in manifest points to com.sun.jini.start.ServiceStarter
> >>>>> Classpath: boot.jar, start.jar, jsk-platform.jar
> >>>>> Codebase: none
> >>>>>
> >>>>> CommonClassLoader - Contains the common Rio and Jini technology
> >>>>> classes (and other declared common platform JARs) to be made
> >>>>> available to its children.
> >>>>> Classpath: Common JARs such as rio.jar
> >>>>> Codebase: Context dependent. The codebase returned is the codebase
> >>>>> of the specific child CL that is the current context of the request.
> >>>>>
> >>>>> Service-nCL - Contains the service specific implementation classes.
> >>>>> Classpath: serviceImpl.jar
> >>>>> Codebase: "serviceX-dl.jar rio-dl.jar jsk-lib-dl.jar"
> >>>>>
> >>>>> Certainly not as sophisticated as OSGi (or what you are
> >>>>> targeting), but it meets the requirements of allowing multiple
> >>>>> service versions, applying security context per class loader using
> >>>>> the same approach as ActivateWrapper, and allows the JVM to stay
> >>>>> running.
> >>>
> >>>
> >>>
> >>
> >>
> >>
> >
> >
--
Greg Trasuk, President
StratusCom Manufacturing Systems Inc. - We use information technology to
solve business problems on your plant floor.
http://stratuscom.com