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: Distributed Garbage Collection & Security - InvocationConstraints
Date Mon, 20 Jun 2011 23:24:19 GMT
Peter Jones wrote:
> On Jun 19, 2011, at 5:37 AM, Peter Firmstone wrote:
>   
>> The easiest way to set DGC constraints would be via configuration.
>>
>> Perhaps the reason this hasn't been implemented previously is, the constraints would
apply to all services that use DGC, so if you've set Authentication and Integrity as minimal
constraints, then this would apply to all services.
>>     
>
> As mentioned offline, I'm not sure how much of this I can page into my consciousness
at the moment.
>
> Configuring the server-side constraints for DGC could probably be supported with additional
parameters to the export process (and thus set via configuration).  The bigger issue, I think,
is what client constraints to apply, and what client subject to use, when JERI's client-side
DGC system (defined within BasicObjectEndpoint) makes "dirty" and "clean" calls on behalf
of the application.  In the traditional RMI DGC model, those calls happen implicitly as remote
references appear and disappear from a JVM.  But in the JERI security model, the client application
controls the security behavior of remote calls by explicitly (with respect to the standard
JERI layers) specifying constraints and controlling the current subject.
>
> So when the system wants to make a "dirty" or "clean" call for a given remote reference
(forget batching for the moment), what constraints to apply, or what subject to use?  There
didn't seem to be an answer, without requiring the client application to interact with the
DGC system more explicitly, which would be a significant change from the RMI DGC model-- and,
I think, not something that seemed worth investing effort on at the time, especially given
that Jini services didn't seem to make use of RMI's DGC functionality in practice anyway (instead
they used higher-level leasing mechanisms to detect client "failure", and most interest was
around just being disable DGC for Jini services).
>
>   
The higher level mechanisms I've seen duplicate a lot of functionality, 
eg use weak reference notifications, leases etc, this is what DGC uses, 
so it would appear to save some work.

Restoring the Subject after privileged execution blocks can be expensive 
too.

I guess the main concern is, the call hasn't been tampered with and 
originates from the correct client, eg the clientID hasn't been forged 
or altered.

SSLEndpoint can provide integrity but it requires at least server 
authentication, but it's the client that needs authenticating.

I wonder if ImplRef can be used to store the Subject at the server, 
different services might use different authenticating subjects.

The client would need an object table that tracked the authenticating 
subject used for each BasicObjectEndpoint.


> FWIW, aborting the execution of in-progress calls, such as via thread interruption, wasn't
really the intent of that last sentence-- it was more that an implementation should feel free
(or encouraged) to prevent communicating the eventual result of such a call, when control
of the dispatching thread is returned to this layer of the system.
>
> Cheers,
>
> -- Peter
Funny how intent can be misinterpreted.

Were you and Ann the original authors of the code?  Can I put your name 
on it?  We know it's donated by Sun, but that doesn't help interpretation.

I changed the call counter from an int to a Set<Thread> , which allows 
force to interrupt the threads, the private dispatch call won't be 
interrupted unless the thread's sleeping.  I've got a boolean field 
which indicates whether the thread was interrupted by unexport, and if 
so dispatch swallows the interrupt before returning, otherwise it sets 
the interrupted status (interrupts can skip the dispatch before 
obtaining a lock), so it can be handled elsewhere. 

The two methods are appended below.

Cheers,

Peter.

    void dispatch(InboundRequest request) throws IOException, NoSuchObject {
        if (!exported){ // optimisation to avoid locking.
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST, "this={0}, not exported", this);
            }
            throw new NoSuchObject();
        }
        Thread current = Thread.currentThread();
        boolean exitNormally = true;
        boolean callerAdded = false;
        try {
            InvocationDispatcher id = null;
            lock.lockInterruptibly();
            try {
                callerAdded = calls.add(current);
                if (!exported || invocationDispatcher == null) { // 
check again now we've got the lock.
                    if (logger.isLoggable(Level.FINEST)) {
                        logger.log(Level.FINEST, "this={0}, not 
exported", this);
                    }
                    throw new NoSuchObject();
                }
                id = invocationDispatcher;
            } finally {
                lock.unlock();
            }
            Remote impl = implRef.getImpl();
            if (impl == null) {
                if (logger.isLoggable(Level.FINEST)) {
                    logger.log(Level.FINEST, "this={0}, garbage 
collected", this);
                }
                throw new NoSuchObject();
            }
            interrupted(current);
            dispatch(request, id, impl, current);
            interrupted(current);
        } catch (InterruptedException ex) {
            exitNormally = false;
            request.abort();
            if (!interrupted){
                // Not interrupted by unexport, reset interrupted status.
                current.interrupt();
            }// else interrupt is swallowed.
            if (logger.isLoggable(Level.FINEST)) {
                logger.log(Level.FINEST, "this={0}, interrupted" , this);
            }
        }finally {
            // Either exit normally with clean up, or clean up if caller 
was added and unexport didn't interrupt.
            if ( exitNormally || (callerAdded && !interrupted)) {
                lock.lock();
                try {
                    calls.remove(current);
                    if (!exported && calls.isEmpty()) { // In case 
Target was collected while call in progress.
                        decrementKeepAliveCount();
                    }
                }finally {
                    lock.unlock();
                }
            } // else exit without cleanup.
        }
    }

    boolean unexport(boolean force) {
        if (!exported) return true;
        lock.lock();
        try {
            if (!force && !calls.isEmpty()) {
                return false;
            }
            unexported = true;
            exported = false;
            if ( force && !calls.isEmpty()){
                interrupted = true;
                Iterator<Thread> i = calls.iterator();
                while (i.hasNext()){
                    i.next().interrupt();
                    i.remove();
                }
            }
            if (calls.isEmpty()) {
                decrementKeepAliveCount();
            }
            if (allowDGC) {
                if (!referencedSet.isEmpty()) {
                    for (Iterator i = referencedSet.iterator(); 
i.hasNext();) {
                        Uuid clientID = (Uuid) i.next();
                        objTable.unregisterTarget(this, clientID);
                    }
                    referencedSet.clear();
                }
                sequenceTable.clear();
            }
        } finally {
            lock.unlock();
        }
        implRef.release(this);
        for (int i = 0; i < requestDispatchers.length; i++) {
            requestDispatchers[i].remove(this, false);
        }
        return true;
    }

Mime
View raw message