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