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 06:20:50 GMT
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