river-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Peter Firmstone <j...@zeus.net.au>
Subject Re: Service API, class visibility, isolation and garbage collection - ClassLoaders
Date Wed, 26 May 2010 11:27:14 GMT
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.

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.

With great power comes great responsibility... yada yada yada.

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.        
>>>
>>>
>>>   
>>
>>
>>
>
>


Mime
View raw message