You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@river.apache.org by Mark Brouwer <ma...@cheiron.org> on 2007/08/08 16:57:10 UTC

PreferredClassProvider performs 'unlucky' caching?

Chasing a bug for the last few days I found the origin in
net.jini.loader.pref.PreferredClassProvider where there is a table
maintained of all class loaders created and the ones found through
findOrigin.

If looking at the method PreferredClassProvider.lookupLoader line 1532
(Sun JTSK) you can see there is a map that associates class loaders with
a key that is a tuple of the codebase annotation and the parent class
loader of the class loader created/found (representing the value of the
entry).

In case a new class loader is created that class loader is put in the
map to be able to find it when that method is consulted with the same
urls and parent class loader, but also in case a class loader is found
through findOriginLoader the class loader found is put in the map.

The problem in my case is that the above logic assumes that one class
loader has one codebase annotation associated with it and this
assumption was true for a long timer except now that Seven supports
multiple codebase annotations per class loader [1] and as a result
codebase boomerang logic seems to be broken under some conditions. I
noticed the wrong class loaders where selected by lookupLoader in some
deployment cases.

I leave the finer details out but the following assumptions in my usage
of PreferredClassProvider are effective.

- each service has its own unique codebase annotation (that can evolve
   in time);
- a class loader can have multiple (deployed) services associated with
   it, the class loader the service is defined within might have as a
   result multiple codebase annotations associated with it;
- a class loader created by PreferredClassProvider will have only one
   codebase annotation associated with it;
- codebase boomerangs are detected by a subclass of
   PreferredClassProvider (RIVER-147) that takes the context in which
   'unmarshalling' takes place into account for associating a codebase
   annotation with a class loader;

As the class loaders created by PreferredClassProvider have a one on one
relation between codebase and class loader the current map is OK.
However the map should not cache for class loaders found through
findOriginLoader as these might result in class loaders that have an
annotation that depends on the context a class loader operates under.
When I only put an entry in the map when a class loader is created
everything works as expected as the class loaders out of control of
PreferredClassProvider which can have multiple codebase annotations
don't end up for matching purposes in the map.

I ask to check whether it was deliberate to put an entry in the map for
the class loader found through findOriginLoader or that it was an
optimization to prevent from calling findOriginLoader each time. If my
understanding is correct I would like to create an issue and modify the
code to prevent from caching loaders found through findOriginLoader.
Please your opinion.

[1] Seven upcoming allows services to share a common class loader while
each having their own unique codebase annotation and security policy.
The codebase annotation and security policy are chosen based on the
SecurityContext the services operates under. This allows intra service
communication (through static constructs) while with respect to the
security policy effective and mobile code they each act as an isolated
entity.
-- 
Mark




Re: PreferredClassProvider performs 'unlucky' caching?

Posted by Mark Brouwer <ma...@cheiron.org>.
Peter Jones wrote:

> It would seem useful to understand the performance impact.

True, but I think it is very difficult to come up with something
representative in terms of a test that could provide a reasonable
answer. Especially since a large part of measuring performance impact
might be related to URL equality (calls into hostname resolving cache).

As the 'fix' will only prevent from caching class loaders that are not
created by the PreferredClassProvider I think that the impact won't be
that large. What at least I try to accomplish with Seven is to have a
null annotation for classes which are part of the platform (another
reason to have one common base platform) which will not only result in
more compact marshalled streams but also prevent from the lookupLoader
method having to do a lot of computations. And if there is an annotation
chances are very big that they represent class loaders created by
PreferredClassProvider which means they will be found in the cache.

Also in the Seven case the test whether a codebase is related to the
class loader the service is defined within is based on string matching
(taking codebase evolution into account) which prevents from the tests
involving URLs, so in my case I'm not worried about the performance
impact, although I can't make any statement for the impact for others.

>                                                          But if the
> RIVER-147 hook is allowed to return different results from different
> invocations with the same arguments (as a function of time or some
> other context), then it would seem that something must be done about
> this caching.

Indeed this can get interesting to specify. In my case I assume a class
loader created by PreferredClassProvider maintains the same codebase
annotation (over time and context), which for the ones not created by
PreferredClassProvider I'm not willing to assume.

I don't know whether that is right or wrong, it is just enough for
solving the problems I face with codebase versioning and multiple
codebase annotations associated with the class loader the service is
defined within.

> Talk of simultaneously having "isolated security policy" and
> "intra-service communication through static constructs" makes me
> nervous.  Do you mean that the code of one service can invoke static
> methods of another service's classes?

Yes.

 >                                       Wouldn't that allow the second
> service's code to execute with the first service's security policy?
> Or do you mean something different than I'm assuming?

Without trying to resort to a very long explanation, it is that the JSC
Specification defines the concept of 'service group definition', which
is to a service what a class is to an object (metaphorically speaking).
A service group definition defines one or multiple services, which
consist of the classes (libraries) that make up all the server side
logic and the associated download JAR files.

When a service group definition indicates it is 'group-sharable' it
means that at deployment time services (defined in the same service
group definition) can be deployed with a common class loader that would
allow for intra-service communication through static constructs. Each
service has its own associated codebase annotation and each has its own
associated security policy which is chosen [1] based on a context marker
[2] carried by each thread executing in the container. Seven will ensure
that each operation related to a service will take place under the right
security policy (otherwise it is considered a bug). A service could only
execute with the other services' security policy if it could lay its
hand on the SecurityContext associated with the other service, or when
there is the ability to 'inject' code into the other service that would
be executed with more privileges.

But as we are talking about communication between services that have
been developed with the ability to collaborate or sharing state (you
must indicate that in the JSC deployment descriptor), service developers
have to be aware of this and in combination with the other assumption
that all services sharing the same class loader (a service group) will
be executed under the same 'security domain' I don't see any harm. A
'security domain' represents *one* entity and controls the identities a
service can operate under and the trust relationship with clients
(key/trust stores, Kerberos principals and RBAC-policies).

So 'group-sharable' is not intended to deploy services sharing the same
class loader that are operated on behalf of company X *and* Y. It is
intended to operate services for company X *or* Y, for services that
provide either a completely different business interface, or the same
business interface but are 'exported' differently while sharing the same
state, but likely using different transports and/or principals.

The reason for group-sharable services could be seen as a workaround for
limitations that one service instance can't be exported multiple times
with various effective codebase annotations and scope related to joining
lookup services. Also in the past there was a need to have various
distinct services with their own life-cycle for which it was necessary
to share state and external connections for various reasons. The latter
can't be solved with one instance having multiple exports, and while the
first seems to be possible, it turned out that there were various
problems with creating secondary proxies (which dynamic are you going to
hand out) that in the end this seemed to be the best solution.

Developing a service to be group sharable is not the default as it
requires additional thinking of the developer. But e.g. JSC Lookup, JSC
JavaSpace and JSC TransactionManager (the derivatives of the JTSK
services) are 'group-sharable' but don't share any state, here they just
safe a few bytes in the permanent heap due to just having one class
loader opposed to multiple ones.

I hope this made things a bit clearer. There is no arbitrary
intra-service communication possible between various service group
definition.

[1] I believe in Jini Starter Framework the security policy effective is
chosen based on the context class loader effective.

[2] the same context marker is e.g. also used to perform context based
filtering of logging to help with tracing what is going on in a JVM that
deploys more than one service (typical deployments with Seven are 8-30
services in one JVM).
-- 
Mark





Re: PreferredClassProvider performs 'unlucky' caching?

Posted by Peter Jones <pe...@sun.com>.
On Wed, Aug 08, 2007 at 04:57:10PM +0200, Mark Brouwer wrote:

> I ask to check whether it was deliberate to put an entry in the map
> for the class loader found through findOriginLoader or that it was
> an optimization to prevent from calling findOriginLoader each time.

I'm pretty sure that it was a deliberate optimization (it exists in
the earliest version of PreferredClassProvider.java that I can find,
from 2000).  I think that findOriginLoader was believed or at least
suspected to be a potentially expensive operation.  This caching (of
findOriginLoader results) is not required by the spec, and the current
spec doesn't accommodate possibilities opened by RIVER-147, so I think
that this caching can reasonably be considered a detail of the present
implementation.

> If my understanding is correct I would like to create an issue and
> modify the code to prevent from caching loaders found through
> findOriginLoader.  Please your opinion.

It would seem useful to understand the performance impact.  But if the
RIVER-147 hook is allowed to return different results from different
invocations with the same arguments (as a function of time or some
other context), then it would seem that something must be done about
this caching.

> [1] Seven upcoming allows services to share a common class loader
> while each having their own unique codebase annotation and security
> policy.  The codebase annotation and security policy are chosen
> based on the SecurityContext the services operates under. This
> allows intra service communication (through static constructs) while
> with respect to the security policy effective and mobile code they
> each act as an isolated entity.

Talk of simultaneously having "isolated security policy" and
"intra-service communication through static constructs" makes me
nervous.  Do you mean that the code of one service can invoke static
methods of another service's classes?  Wouldn't that allow the second
service's code to execute with the first service's security policy?
Or do you mean something different than I'm assuming?

-- Peter