Merge branch 'ueinfo_with_downcall_locking' into 'ibm-trunk'

Ueinfo with downcall locking

Clean up locking in Downcall to use java.util.concurrent.locks.
Unknown exception info service context was being unmarshalled on the wrong thread and did not have the correct class loading context.
TypeDescriptor caching was incorrect and allowed finding the wrong class in some circumstances.
FullValueDescription from ValueDescriptor incorrectly reported non-custom chunked types as custom, leading to errors when the client ORB tried to unmarshal the associated data.

See merge request !53
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/CollocatedServer.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/CollocatedServer.java
index bd3ac2a..78c0e74 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/CollocatedServer.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/CollocatedServer.java
@@ -146,7 +146,7 @@
         // dispatch thread is used, we could avoid initializing the
         // state monitor when it is not needed.
         //
-        down.initStateMonitor();
+        down.allowWaiting();
 
         Upcall up;
 
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/Downcall.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/Downcall.java
index cda88f5..5ae2c2c 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/Downcall.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/Downcall.java
@@ -17,19 +17,16 @@
 
 package org.apache.yoko.orb.OB;
 
-import static org.apache.yoko.orb.OB.CodeSetDatabase.UTF16;
-import static org.apache.yoko.orb.OCI.GiopVersion.GIOP1_2;
-
 import java.util.Vector;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.locks.Condition;
 
-import org.apache.yoko.orb.OCI.Buffer;
 import org.apache.yoko.orb.OCI.GiopVersion;
-import org.omg.CORBA.MARSHAL;
-import org.omg.CORBA.SystemException;
-import org.omg.CORBA.UNKNOWN;
-import org.omg.CORBA.portable.UnknownException;
+import org.apache.yoko.orb.util.AutoLock;
+import org.apache.yoko.orb.util.AutoReadWriteLock;
+import org.omg.CORBA.CompletionStatus;
+import org.omg.CORBA.NO_RESPONSE;
 import org.omg.IOP.ServiceContext;
-import org.omg.IOP.UnknownExceptionInfo;
 
 public class Downcall {
     //
@@ -87,25 +84,13 @@
     //
     // The state of this invocation
     //
-    protected static final int DowncallStateUnsent = 0;
-
-    protected static final int DowncallStatePending = 1;
-
-    protected static final int DowncallStateNoException = 2;
-
-    protected static final int DowncallStateUserException = 3;
-
-    protected static final int DowncallStateSystemException = 4;
-
-    protected static final int DowncallStateFailureException = 5;
-
-    protected static final int DowncallStateForward = 6;
-
-    protected static final int DowncallStateForwardPerm = 7;
-
-    protected int state_;
-
-    protected java.lang.Object stateMonitor_;
+    protected enum State { UNSENT, PENDING, NO_EXCEPTION, USER_EXCEPTION, SYSTEM_EXCEPTION, FAILURE_EXCEPTION, FORWARD, FORWARD_PERM }
+    
+    protected final AutoReadWriteLock stateLock = new AutoReadWriteLock();
+    
+    protected State state = State.UNSENT;
+    
+    protected Condition stateWaitCondition;
 
     //
     // Holds the exception if state_ is DowncallStateUserException,
@@ -142,36 +127,38 @@
     // Raise an exception if necessary
     //
     void checkForException() throws LocationForward, FailureException {
-        switch (state_) {
-        case DowncallStateUserException:
-            //
-            // Do not raise UserException in Java
-            //
-            // if(ex_ != null) // Only raise if a user exception has been set
-            // throw ex_;
-            break;
+        try (AutoLock readLock = stateLock.getReadLock()) {
+            switch (state) {
+                case USER_EXCEPTION:
+                    //
+                    // Do not raise UserException in Java
+                    //
+                    // if(ex_ != null) // Only raise if a user exception has been set
+                    // throw ex_;
+                    break;
 
-        case DowncallStateSystemException:
-            Assert._OB_assert(ex_ != null);
-            // update the stack trace to have the caller's stack rather than the 
-            // receiver thread. 
-            ex_.fillInStackTrace();    
-            throw (org.omg.CORBA.SystemException) ex_;
+                case SYSTEM_EXCEPTION:
+                    Assert._OB_assert(ex_ != null);
+                    // update the stack trace to have the caller's stack rather than the 
+                    // receiver thread. 
+                    ex_.fillInStackTrace();    
+                    throw (org.omg.CORBA.SystemException) ex_;
 
-        case DowncallStateFailureException:
-            Assert._OB_assert(ex_ != null);
-            throw new FailureException((org.omg.CORBA.SystemException) ex_);
+                case FAILURE_EXCEPTION:
+                    Assert._OB_assert(ex_ != null);
+                    throw new FailureException((org.omg.CORBA.SystemException) ex_);
 
-        case DowncallStateForward:
-            Assert._OB_assert(forwardIOR_ != null);
-            throw new LocationForward(forwardIOR_, false);
+                case FORWARD:
+                    Assert._OB_assert(forwardIOR_ != null);
+                    throw new LocationForward(forwardIOR_, false);
 
-        case DowncallStateForwardPerm:
-            Assert._OB_assert(forwardIOR_ != null);
-            throw new LocationForward(forwardIOR_, true);
+                case FORWARD_PERM:
+                    Assert._OB_assert(forwardIOR_ != null);
+                    throw new LocationForward(forwardIOR_, true);
 
-        default:
-            break;
+                default:
+                    break;
+            }
         }
     }
 
@@ -203,8 +190,10 @@
         policies_ = policies;
         op_ = op;
         responseExpected_ = resp;
-        state_ = DowncallStateUnsent;
-        stateMonitor_ = null;
+        // since this.state is not volatile we must use a lock to guarantee consistency
+        try (AutoLock writeLock = stateLock.getWriteLock()) {
+            state = State.UNSENT;
+        }
         ex_ = null;
 
         //
@@ -215,58 +204,58 @@
         logger_.debug("Downcall created for operation " + op + " with id " + reqId_); 
     }
 
-    public ORBInstance orbInstance() {
+    public final ORBInstance orbInstance() {
         return orbInstance_;
     }
 
-    public Client client() {
+    public final Client client() {
         return client_;
     }
 
-    public org.apache.yoko.orb.OCI.ProfileInfo profileInfo() {
+    public final org.apache.yoko.orb.OCI.ProfileInfo profileInfo() {
         return profileInfo_;
     }
 
-    public RefCountPolicyList policies() {
+    public final RefCountPolicyList policies() {
         return policies_;
     }
 
-    public Exception excep() {
+    public final Exception excep() {
         return ex_;
     }
 
-    public int requestId() {
+    public final int requestId() {
         return reqId_;
     }
 
-    public String operation() {
+    public final String operation() {
         return op_;
     }
 
-    public boolean responseExpected() {
+    public final boolean responseExpected() {
         return responseExpected_;
     }
 
-    public org.apache.yoko.orb.CORBA.OutputStream output() {
+    public final org.apache.yoko.orb.CORBA.OutputStream output() {
         return out_;
     }
 
-    public org.apache.yoko.orb.CORBA.InputStream input() {
+    public final org.apache.yoko.orb.CORBA.InputStream input() {
         return in_;
     }
 
-    public org.omg.IOP.ServiceContext[] getRequestSCL() {
+    public final org.omg.IOP.ServiceContext[] getRequestSCL() {
         org.omg.IOP.ServiceContext[] scl = new org.omg.IOP.ServiceContext[requestSCL_
                 .size()];
         requestSCL_.copyInto(scl);
         return scl;
     }
 
-    public void addToRequestSCL(org.omg.IOP.ServiceContext sc) {
+    public final void addToRequestSCL(org.omg.IOP.ServiceContext sc) {
         requestSCL_.addElement(sc);
     }
 
-    public void setReplySCL(org.omg.IOP.ServiceContext[] scl) {
+    public final void setReplySCL(org.omg.IOP.ServiceContext[] scl) {
         // Don't create a new Vector
         Assert._OB_assert(replySCL_.size() == 0);
         replySCL_.setSize(scl.length);
@@ -279,17 +268,17 @@
         return preMarshalBase();
     }
 
-    public void marshalEx(org.omg.CORBA.SystemException ex)
+    public final void marshalEx(org.omg.CORBA.SystemException ex)
             throws LocationForward, FailureException {
         setFailureException(ex);
         checkForException();
         Assert._OB_assert(false);
     }
 
-    public void postMarshal() throws LocationForward, FailureException {
+    public final void postMarshal() throws LocationForward, FailureException {
     }
 
-    public void locate() throws LocationForward, FailureException {
+    public final void locate() throws LocationForward, FailureException {
         Assert._OB_assert(responseExpected_);
         Assert._OB_assert(op_.equals("_locate"));
 
@@ -311,7 +300,7 @@
         checkForException();
     }
 
-    public void request() throws LocationForward, FailureException {
+    public final void request() throws LocationForward, FailureException {
         Assert._OB_assert(responseExpected_);
 
         //
@@ -331,7 +320,7 @@
         checkForException();
     }
 
-    public void oneway() throws LocationForward, FailureException {
+    public final void oneway() throws LocationForward, FailureException {
         Assert._OB_assert(!responseExpected_);
 
         if (policies_.syncScope == org.omg.Messaging.SYNC_WITH_TRANSPORT.value) {
@@ -345,7 +334,7 @@
         }
     }
 
-    public void deferred() throws LocationForward, FailureException {
+    public final void deferred() throws LocationForward, FailureException {
         Assert._OB_assert(responseExpected_);
 
         boolean finished = emitter_.send(this, true);
@@ -353,7 +342,7 @@
             checkForException();
     }
 
-    public void response() throws LocationForward, FailureException {
+    public final void response() throws LocationForward, FailureException {
         Assert._OB_assert(responseExpected_);
 
         boolean finished = emitter_.receive(this, true);
@@ -361,23 +350,25 @@
         checkForException();
     }
 
-    public boolean poll() throws LocationForward, FailureException {
+    public final boolean poll() throws LocationForward, FailureException {
         Assert._OB_assert(responseExpected_);
 
         boolean finished = emitter_.receive(this, false);
         if (finished) {
-            checkForException();
-            return state_ != DowncallStatePending;
+            try (AutoLock lock = stateLock.getReadLock()) {
+                checkForException();
+                return state != State.PENDING;
+            }
         } else
             return false;
     }
 
-    public org.apache.yoko.orb.CORBA.InputStream preUnmarshal()
+    public final org.apache.yoko.orb.CORBA.InputStream preUnmarshal()
             throws LocationForward, FailureException {
         return in_;
     }
 
-    public void unmarshalEx(org.omg.CORBA.SystemException ex)
+    public final void unmarshalEx(org.omg.CORBA.SystemException ex)
             throws LocationForward, FailureException {
         setFailureException(ex);
         checkForException();
@@ -394,314 +385,213 @@
         // the user exception, so we only want to raise UNKNOWN if we
         // don't have the ID
         //
-        if (state_ == DowncallStateUserException && ex_ == null
-                && exId_ == null)
-            setSystemException(new org.omg.CORBA.UNKNOWN(org.apache.yoko.orb.OB.MinorCodes
-                    .describeUnknown(org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException),
-                    org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
-                    org.omg.CORBA.CompletionStatus.COMPLETED_YES));
-
-        checkForException();
+        try (AutoLock lock = stateLock.getReadLock()) {
+            if (state == State.USER_EXCEPTION && ex_ == null && exId_ == null)
+                setSystemException(new org.omg.CORBA.UNKNOWN(org.apache.yoko.orb.OB.MinorCodes
+                        .describeUnknown(org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException),
+                        org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
+                        org.omg.CORBA.CompletionStatus.COMPLETED_YES));
+            checkForException();
+        }
     }
 
-    public String unmarshalExceptionId() {
-        Assert._OB_assert(state_ == DowncallStateUserException);
-        int pos = in_._OB_pos();
-        String id = in_.read_string();
-        in_._OB_pos(pos);
-        return id;
+    public final String unmarshalExceptionId() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            Assert._OB_assert(state == State.USER_EXCEPTION);
+            int pos = in_._OB_pos();
+            String id = in_.read_string();
+            in_._OB_pos(pos);
+            return id;
+        }
     }
 
-    public boolean unsent() {
-        return state_ == DowncallStateUnsent;
+    public final boolean unsent() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.UNSENT;
+        }
     }
 
-    public boolean pending() {
-        return state_ == DowncallStatePending;
+    public final boolean pending() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.PENDING;
+        }
     }
 
-    public boolean noException() {
-        return state_ == DowncallStateNoException;
+    public final boolean noException() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.NO_EXCEPTION;
+        }
     }
 
-    public boolean userException() {
-        return state_ == DowncallStateUserException;
+    public final boolean userException() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.USER_EXCEPTION;
+        }
     }
 
-    public boolean failureException() {
-        return state_ == DowncallStateFailureException;
+    public final boolean failureException() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.FAILURE_EXCEPTION;
+        }
     }
 
-    public boolean systemException() {
-        return state_ == DowncallStateSystemException;
+    public final boolean systemException() {
+        try (AutoLock lock = stateLock.getReadLock()) {
+            return state == State.SYSTEM_EXCEPTION;
+        }
     }
 
-    private void setPendingImpl() {
-        Assert._OB_assert(responseExpected_);
-        state_ = DowncallStatePending;
-    }
-
-    public void setPending() {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setPendingImpl();
-                stateMonitor_.notify();
-            }
-        } else
-            setPendingImpl();
-    }
-
-    private void setNoExceptionImpl(org.apache.yoko.orb.CORBA.InputStream in) {
-        state_ = DowncallStateNoException;
-        if (in == null) {
-            Assert._OB_assert(!responseExpected_);
-        } else {
+    public final void setPending() {
+        try (AutoLock lock = stateLock.getWriteLock()) {
             Assert._OB_assert(responseExpected_);
+            state = State.PENDING;
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
+    }
+
+    public final void setNoException(org.apache.yoko.orb.CORBA.InputStream in) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            state = State.NO_EXCEPTION;
+            if (in == null) {
+                Assert._OB_assert(!responseExpected_);
+            } else {
+                Assert._OB_assert(responseExpected_);
+                in_ = in;
+                in_._OB_ORBInstance(orbInstance_);
+                CodeConverters codeConverters = client_.codeConverters();
+                in_._OB_codeConverters(codeConverters, GiopVersion.get(profileInfo_.major, profileInfo_.minor));
+            }
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
+    }
+
+    public final void setUserException(org.apache.yoko.orb.CORBA.InputStream in) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(in != null);
+            Assert._OB_assert(responseExpected_);
+            state = State.USER_EXCEPTION;
             in_ = in;
             in_._OB_ORBInstance(orbInstance_);
             CodeConverters codeConverters = client_.codeConverters();
             in_._OB_codeConverters(codeConverters, GiopVersion.get(profileInfo_.major, profileInfo_.minor));
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
         }
     }
 
-    public void setNoException(org.apache.yoko.orb.CORBA.InputStream in) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setNoExceptionImpl(in);
-                stateMonitor_.notify();
-            }
-        } else
-            setNoExceptionImpl(in);
-    }
-
-    private void setUserExceptionImpl(org.apache.yoko.orb.CORBA.InputStream in) {
-        Assert._OB_assert(in != null);
-        Assert._OB_assert(responseExpected_);
-        state_ = DowncallStateUserException;
-        in_ = in;
-        in_._OB_ORBInstance(orbInstance_);
-        CodeConverters codeConverters = client_.codeConverters();
-        in_._OB_codeConverters(codeConverters, GiopVersion.get(profileInfo_.major, profileInfo_.minor));
-    }
-
-    public void setUserException(org.apache.yoko.orb.CORBA.InputStream in) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setUserExceptionImpl(in);
-                stateMonitor_.notify();
-            }
-        } else
-            setUserExceptionImpl(in);
-    }
-
-    private void setUserExceptionImpl(org.omg.CORBA.UserException ex,
-            String exId) {
-        Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateUserException;
-        ex_ = ex;
-    }
-
     public void setUserException(org.omg.CORBA.UserException ex, String exId) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setUserExceptionImpl(ex, exId);
-                stateMonitor_.notify();
-            }
-        } else
-            setUserExceptionImpl(ex, exId);
-    }
-
-    private void setUserExceptionImpl(org.omg.CORBA.UserException ex) {
-        Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateUserException;
-        ex_ = ex;
-    }
-
-    public void setUserException(org.omg.CORBA.UserException ex) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setUserExceptionImpl(ex);
-                stateMonitor_.notify();
-            }
-        } else
-            setUserExceptionImpl(ex);
-    }
-
-    //
-    // Java only
-    //
-    // Required for portable stubs, which do not make the UserException
-    // instance available to the ORB
-    //
-    private void setUserExceptionImpl(String exId) {
-        Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateUserException;
-        exId_ = exId;
-        logger_.debug("Received user exception " + exId);
-    }
-
-    public void setUserException(String exId) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setUserExceptionImpl(exId);
-                stateMonitor_.notify();
-            }
-        } else
-            setUserExceptionImpl(exId);
-    }
-
-    private void setSystemExceptionImpl(org.omg.CORBA.SystemException ex) {
-        Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateSystemException;
-        ex_ = convertToUnknownExceptionIfAppropriate(ex);
-        logger_.debug("Received system exception", ex);
-    }
-
-    private SystemException convertToUnknownExceptionIfAppropriate(org.omg.CORBA.SystemException ex) {
-        if (ex instanceof UNKNOWN) {
-            for (ServiceContext sc : replySCL_) {
-                if (sc.context_id == UnknownExceptionInfo.value) {
-                    final byte[] data = sc.context_data;
-                    CodeConverters codeConverters = new CodeConverters();
-                    codeConverters.outputWcharConverter = CodeSetDatabase.instance().getConverter(UTF16, UTF16);
-                    Buffer buf = new Buffer(data, data.length);
-                    try (org.apache.yoko.orb.CORBA.InputStream in =
-                            new org.apache.yoko.orb.CORBA.InputStream(buf, 0, false, codeConverters, GIOP1_2)) {
-                        in._OB_readEndian();
-                        Throwable t = (Throwable) in.read_value();
-                        UnknownException x = new UnknownException(t);
-                        x.completed = ex.completed;
-                        x.minor = ex.minor;
-                        return x;
-                    } catch (Exception e) {
-                        throw (MARSHAL)(new MARSHAL(e.getMessage())).initCause(e);
-                    }
-                }
-            }
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(responseExpected_);
+            Assert._OB_assert(ex_ == null);
+            state = State.USER_EXCEPTION;
+            ex_ = ex;
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
         }
-        return ex;
     }
 
-    public void setSystemException(org.omg.CORBA.SystemException ex) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setSystemExceptionImpl(ex);
-                stateMonitor_.notify();
-            }
-        } else
-            setSystemExceptionImpl(ex);
+    public final void setUserException(org.omg.CORBA.UserException ex) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(responseExpected_);
+            Assert._OB_assert(ex_ == null);
+            state = State.USER_EXCEPTION;
+            ex_ = ex;
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
     }
 
-    private void setFailureExceptionImpl(org.omg.CORBA.SystemException ex) {
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateFailureException;
-        ex_ = ex;
-        logger_.debug("Received failure exception", ex);
+    public final void setUserException(String exId) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(responseExpected_);
+            Assert._OB_assert(ex_ == null);
+            state = State.USER_EXCEPTION;
+            exId_ = exId;
+            logger_.debug("Received user exception " + exId);
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
     }
 
-    public void setFailureException(org.omg.CORBA.SystemException ex) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setFailureExceptionImpl(ex);
-                stateMonitor_.notify();
-            }
-        } else
-            setFailureExceptionImpl(ex);
+    public final void setSystemException(org.omg.CORBA.SystemException ex) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(responseExpected_);
+            Assert._OB_assert(ex_ == null);
+            state = State.SYSTEM_EXCEPTION;
+            ex_ = ex;
+            logger_.debug("Received system exception", ex);
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
     }
 
-    private void setLocationForwardImpl(org.omg.IOP.IOR ior, boolean perm) {
+    public final void setFailureException(org.omg.CORBA.SystemException ex) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(ex_ == null);
+            state = State.FAILURE_EXCEPTION;
+            ex_ = ex;
+            logger_.debug("Received failure exception", ex);
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
+    }
+
+    public final void setLocationForward(org.omg.IOP.IOR ior, boolean perm) {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(responseExpected_);
+            Assert._OB_assert(forwardIOR_ == null);
+            state = perm ? State.FORWARD_PERM : State.FORWARD;
+            forwardIOR_ = ior;
+            if (null != stateWaitCondition) stateWaitCondition.signalAll();
+        }
         Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(forwardIOR_ == null);
-        if (perm)
-            state_ = DowncallStateForwardPerm;
-        else
-            state_ = DowncallStateForward;
-        forwardIOR_ = ior;
-    }
-
-    public void setLocationForward(org.omg.IOP.IOR ior, boolean perm) {
-        if (stateMonitor_ != null) {
-            synchronized (stateMonitor_) {
-                setLocationForwardImpl(ior, perm);
-                stateMonitor_.notify();
-            }
-        } else
-            setLocationForwardImpl(ior, perm);
     }
 
     //
-    // Initialize the state monitor. This operation must be called in
+    // Initialize the wait condition. This operation must be called in
     // order to be able to use one of the waitUntil...() operations
     // below
     //
-    public void initStateMonitor() {
-        Assert._OB_assert(stateMonitor_ == null);
-        stateMonitor_ = new java.lang.Object();
+    public final void allowWaiting() {
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(stateWaitCondition == null);
+            stateWaitCondition = lock.newCondition();
+        }
     }
 
-    //
-    // This operation try waits for a specific state, using the
-    // timeout from this downcall's policies. If the timeout expires,
-    // a NO_RESPONSE exception is raised.
-    //
-    // - If the first parameter is set to false, the operation returns
-    // immediately with false if the desired state cannot be
-    // reached.
-    //
-    // - If the return value is true, it's safe to access or modify
-    // the downcall object. If the return value if false, accessing
-    // or modifying the downcall object is not allowed, for thread
-    // safety reasons. (Because the downcall object is not thread
-    // safe.)
-    //
-    public boolean waitUntilCompleted(boolean block) {
+    /**
+     * This operation try waits for a completed state, using the
+     * timeout from this downcall's policies.
+     *
+     * @param block whether to wait for the call to complete
+     * @return true if the call has completed
+     * @throws NO_RESPONSE if a timeout was set and has elapsed
+     */
+    public final boolean waitUntilCompleted(boolean block) {
         //
         // Get the timeout
         //
         int t = policies_.requestTimeout;
 
         //
-        // Yield if non-blocking or blocking with zero timeout
-        //
-        if (!block || (block && t == 0)) {
-            Thread.yield();
-        }
-
-        //
         // Wait for the desired state, taking the timeout and blocking
         // flag into account
         //
-        Assert._OB_assert(stateMonitor_ != null);
-        synchronized (stateMonitor_) {
-            while (state_ == DowncallStateUnsent
-                    || state_ == DowncallStatePending) {
-                if (!block) {
-                    return false;
-                }
+        try (AutoLock lock = stateLock.getWriteLock()) {
+            Assert._OB_assert(stateWaitCondition != null);
+            while (state == State.UNSENT || state == State.PENDING) {
+                if (!block) return false;
 
                 try {
-                    if (t < 0) {
-                        stateMonitor_.wait();
+                    if (t <= 0) {
+                        stateWaitCondition.await();
                     } else {
-                        int oldState = state_;
+                        State oldState = state;
 
-                        stateMonitor_.wait(t);
+                        stateWaitCondition.await(t, TimeUnit.MILLISECONDS);
 
-                        if (state_ == oldState) {
-                            throw new org.omg.CORBA.NO_RESPONSE(
-                                    "Timeout during receive",
-                                    0,
-                                    org.omg.CORBA.CompletionStatus.COMPLETED_MAYBE);
+                        if (state == oldState) {
+                            throw new NO_RESPONSE("Timeout during receive", 0, CompletionStatus.COMPLETED_MAYBE);
                         }
                     }
                 } catch (InterruptedException ex) {
                 }
             }
-
+            if (ex_ instanceof UnresolvedException) ex_ = ((UnresolvedException)ex_).resolve();
             //
             // The downcall has completed
             //
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPClient.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPClient.java
index bee1f0d..6f9d2ee 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPClient.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPClient.java
@@ -18,6 +18,7 @@
 package org.apache.yoko.orb.OB;
 
 import org.apache.yoko.orb.OCI.GiopVersion;
+import org.omg.SendingContext.CodeBaseHelper;
 
 final class GIOPClient extends Client {
     protected ORBInstance orbInstance_; // The ORB instance
@@ -285,7 +286,7 @@
         if (codeBaseSC_ == null) {
 
             javax.rmi.CORBA.ValueHandler valueHandler = javax.rmi.CORBA.Util.createValueHandler();
-            org.omg.SendingContext.CodeBase codeBase = (org.omg.SendingContext.CodeBase) valueHandler.getRunTimeCodeBase();
+            org.omg.SendingContext.CodeBase codeBase = CodeBaseHelper.narrow(valueHandler.getRunTimeCodeBase());
 
 
             org.apache.yoko.orb.OCI.Buffer buf = new org.apache.yoko.orb.OCI.Buffer();
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnection.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnection.java
index 2cd687b..2280516 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnection.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnection.java
@@ -17,10 +17,21 @@
 
 package org.apache.yoko.orb.OB;
 
+import static org.apache.yoko.orb.OCI.GiopVersion.GIOP1_2;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
 import org.apache.yoko.orb.CORBA.InputStream;
 import org.apache.yoko.orb.OB.Logger;
+import org.apache.yoko.orb.OCI.Buffer;
 import org.apache.yoko.orb.OCI.GiopVersion;
+import org.omg.CORBA.SystemException;
+import org.omg.CORBA.UNKNOWN;
+import org.omg.CORBA.portable.UnknownException;
 import org.omg.IOP.ServiceContext;
+import org.omg.IOP.UnknownExceptionInfo;
 import org.omg.SendingContext.CodeBase;
 
 abstract public class GIOPConnection implements DowncallEmitter, UpcallReturn {
@@ -557,8 +568,8 @@
 
         case org.omg.GIOP.ReplyStatusType_1_2._SYSTEM_EXCEPTION: {
             try {
-                org.omg.CORBA.SystemException ex = Util
-                        .unmarshalSystemException(in);
+                org.omg.CORBA.SystemException ex = Util.unmarshalSystemException(in);
+                ex = convertToUnknownExceptionIfAppropriate(ex, in, scl.value);
                 down.setSystemException(ex);
             } catch (org.omg.CORBA.SystemException ex) {
                 processException(State.Error, ex, false);
@@ -614,6 +625,18 @@
         }
     }
 
+    private SystemException convertToUnknownExceptionIfAppropriate(org.omg.CORBA.SystemException ex, InputStream is,
+            ServiceContext[] scl) {
+        if (ex instanceof UNKNOWN) {
+            for (ServiceContext sc : scl) {
+                if (sc.context_id == UnknownExceptionInfo.value) {
+                    return new UnresolvedException((UNKNOWN) ex, sc.context_data, is);
+                }
+            }
+        }
+        return ex;
+    }
+
     private void assignSendingContextRuntime(InputStream in, ServiceContext[] scl) {
         if (serverRuntime_ == null) {
             serverRuntime_
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnectionThreaded.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnectionThreaded.java
index 269edf5..c6d6c2e 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnectionThreaded.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/GIOPConnectionThreaded.java
@@ -378,7 +378,7 @@
             //
             try {
                 inMsg.extractHeader(buf);
-                logger.fine("Header received for message of size " + inMsg.size()); 
+                logger.fine("Header received for message of size " + inMsg.size());
                 buf.realloc(12 + inMsg.size());
             } catch (org.omg.CORBA.SystemException ex) {
                 processException(State.Error, ex, false);
@@ -539,7 +539,7 @@
             // make the downcall thread-safe
             //
             if (down.responseExpected()) {
-                down.initStateMonitor();
+                down.allowWaiting();
             }
 
             // 
@@ -718,7 +718,7 @@
         //
         try {
             boolean result = down.waitUntilCompleted(block);
-            logger.fine("Completed eceiving response with Downcall of type " + down.getClass().getName()); 
+            logger.fine("Completed receiving response with Downcall of type " + down.getClass().getName());
             return result; 
         } catch (org.omg.CORBA.SystemException ex) {
             processException(State.Closed, ex, false);
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/PIDowncall.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/PIDowncall.java
index ac094c5..6aa738b 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/PIDowncall.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/PIDowncall.java
@@ -17,6 +17,8 @@
 
 package org.apache.yoko.orb.OB;
 
+import org.apache.yoko.orb.util.AutoLock;
+
 public class PIDowncall extends Downcall {
     //
     // The IOR and the original IOR
@@ -48,74 +50,76 @@
     // ----------------------------------------------------------------------
 
     void checkForException() throws LocationForward, FailureException {
-        //
-        // If ex_ is set, but exId_ is not, then set it now
-        //
-        // TODO: Postpone this in Java?
-        //
-        if (ex_ != null && exId_ == null)
-            exId_ = Util.getExceptionId(ex_);
+        try (AutoLock lock = stateLock.getReadLock()) {
+            //
+            // If ex_ is set, but exId_ is not, then set it now
+            //
+            // TODO: Postpone this in Java?
+            //
+            if (ex_ != null && exId_ == null)
+                exId_ = Util.getExceptionId(ex_);
 
-        switch (state_) {
-        case DowncallStateUserException:
-            //
-            // For Java portable stubs, we'll have the repository ID
-            // but not the exception instance, so we pass UNKNOWN to
-            // the interceptors but DO NOT modify the Downcall state.
-            //
-            if (ex_ == null && exId_ != null) {
-                org.omg.CORBA.Any any = new org.apache.yoko.orb.CORBA.Any(
-                        orbInstance_);
-                org.omg.CORBA.UNKNOWN sys = new org.omg.CORBA.UNKNOWN(
-                        MinorCodes
+            switch (state) {
+                case USER_EXCEPTION:
+                    //
+                    // For Java portable stubs, we'll have the repository ID
+                    // but not the exception instance, so we pass UNKNOWN to
+                    // the interceptors but DO NOT modify the Downcall state.
+                    //
+                    if (ex_ == null && exId_ != null) {
+                        org.omg.CORBA.Any any = new org.apache.yoko.orb.CORBA.Any(
+                                orbInstance_);
+                        org.omg.CORBA.UNKNOWN sys = new org.omg.CORBA.UNKNOWN(
+                                MinorCodes
                                 .describeUnknown(org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException)
                                 + ": " + exId_,
-                        org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
-                        org.omg.CORBA.CompletionStatus.COMPLETED_YES);
-                org.omg.CORBA.UNKNOWNHelper.insert(any, sys);
-                org.omg.CORBA.UnknownUserException unk = new org.omg.CORBA.UnknownUserException(
-                        any);
-                piManager_.clientReceiveException(requestInfo_, false, unk,
-                        exId_);
+                                org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
+                                org.omg.CORBA.CompletionStatus.COMPLETED_YES);
+                        org.omg.CORBA.UNKNOWNHelper.insert(any, sys);
+                        org.omg.CORBA.UnknownUserException unk = new org.omg.CORBA.UnknownUserException(
+                                any);
+                        piManager_.clientReceiveException(requestInfo_, false, unk,
+                                exId_);
+                    }
+                    //
+                    // Only invoke interceptor if a user exception has been
+                    // set
+                    //
+                    if (ex_ != null)
+                        piManager_.clientReceiveException(requestInfo_, false, ex_,
+                                exId_);
+                    break;
+
+                case SYSTEM_EXCEPTION:
+                    Assert._OB_assert(ex_ != null);
+                    piManager_.clientReceiveException(requestInfo_, true, ex_, exId_);
+                    break;
+
+                case FAILURE_EXCEPTION:
+                    try {
+                        Assert._OB_assert(ex_ != null);
+                        piManager_.clientReceiveException(requestInfo_, true, ex_,
+                                exId_);
+                    } catch (org.omg.CORBA.SystemException ex) {
+                        //
+                        // Ignore any exception translations for failure
+                        // exceptions
+                        //
+                    }
+                    break;
+
+                case FORWARD:
+                case FORWARD_PERM:
+                    Assert._OB_assert(forwardIOR_ != null);
+                    piManager_.clientReceiveLocationForward(requestInfo_, forwardIOR_);
+                    break;
+
+                default:
+                    break;
             }
-            //
-            // Only invoke interceptor if a user exception has been
-            // set
-            //
-            if (ex_ != null)
-                piManager_.clientReceiveException(requestInfo_, false, ex_,
-                        exId_);
-            break;
 
-        case DowncallStateSystemException:
-            Assert._OB_assert(ex_ != null);
-            piManager_.clientReceiveException(requestInfo_, true, ex_, exId_);
-            break;
-
-        case DowncallStateFailureException:
-            try {
-                Assert._OB_assert(ex_ != null);
-                piManager_.clientReceiveException(requestInfo_, true, ex_,
-                        exId_);
-            } catch (org.omg.CORBA.SystemException ex) {
-                //
-                // Ignore any exception translations for failure
-                // exceptions
-                //
-            }
-            break;
-
-        case DowncallStateForward:
-        case DowncallStateForwardPerm:
-            Assert._OB_assert(forwardIOR_ != null);
-            piManager_.clientReceiveLocationForward(requestInfo_, forwardIOR_);
-            break;
-
-        default:
-            break;
+            super.checkForException();
         }
-
-        super.checkForException();
     }
 
     // ----------------------------------------------------------------------
@@ -143,52 +147,45 @@
     }
 
     public void postUnmarshal() throws LocationForward, FailureException {
-        //
-        // If the result of this downcall is a user exception, but no user
-        // exception could be unmarshalled, then use the system exception
-        // UNKNOWN, but keep the original exception ID
-        //
-        // In Java, the portable stubs only provide the repository ID of
-        // the user exception, not the exception instance. We want to
-        // report UNKNOWN to the interceptors, but do not want to change
-        // the downcall status if we have the repository ID.
-        //
-        if (state_ == DowncallStateUserException && ex_ == null
-                && exId_ == null) {
-            String id = unmarshalExceptionId();
-            setSystemException(new org.omg.CORBA.UNKNOWN(org.apache.yoko.orb.OB.MinorCodes
-                    .describeUnknown(org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException)
-                    + ": " + id, org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
-                    org.omg.CORBA.CompletionStatus.COMPLETED_YES));
-            exId_ = id;
+        try (AutoLock lock = stateLock.getReadLock()) {
+            //
+            // If the result of this downcall is a user exception, but no user
+            // exception could be unmarshalled, then use the system exception
+            // UNKNOWN, but keep the original exception ID
+            //
+            // In Java, the portable stubs only provide the repository ID of
+            // the user exception, not the exception instance. We want to
+            // report UNKNOWN to the interceptors, but do not want to change
+            // the downcall status if we have the repository ID.
+            //
+            if (state == State.USER_EXCEPTION && ex_ == null && exId_ == null) {
+                String id = unmarshalExceptionId();
+                setSystemException(new org.omg.CORBA.UNKNOWN(org.apache.yoko.orb.OB.MinorCodes
+                        .describeUnknown(org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException)
+                        + ": " + id, org.apache.yoko.orb.OB.MinorCodes.MinorUnknownUserException,
+                        org.omg.CORBA.CompletionStatus.COMPLETED_YES));
+                exId_ = id;
+            }
+    
+            super.postUnmarshal();
+    
+            //
+            // Java only - Downcall.checkForException() does not raise
+            // UserExceptions, so we return now and let the stub handle it
+            //
+            if (state == State.USER_EXCEPTION)
+                return;
+    
+            if (responseExpected_)
+                Assert._OB_assert(state == State.NO_EXCEPTION);
+            else
+                Assert._OB_assert(state == State.UNSENT || state == State.NO_EXCEPTION);
+            piManager_.clientReceiveReply(requestInfo_);
         }
-
-        super.postUnmarshal();
-
-        //
-        // Java only - Downcall.checkForException() does not raise
-        // UserExceptions, so we return now and let the stub handle it
-        //
-        if (state_ == DowncallStateUserException)
-            return;
-
-        if (responseExpected_)
-            Assert._OB_assert(state_ == DowncallStateNoException);
-        else
-            Assert._OB_assert(state_ == DowncallStateUnsent
-                    || state_ == DowncallStateNoException);
-        piManager_.clientReceiveReply(requestInfo_);
     }
 
     public void setUserException(org.omg.CORBA.UserException ex, String exId) {
         super.setUserException(ex, exId);
         exId_ = exId;
     }
-
-    public void setUserException(org.omg.CORBA.UserException ex) {
-        Assert._OB_assert(responseExpected_);
-        Assert._OB_assert(ex_ == null);
-        state_ = DowncallStateUserException;
-        ex_ = ex;
-    }
 }
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/UnresolvedException.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/UnresolvedException.java
new file mode 100644
index 0000000..8d0aaf5
--- /dev/null
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/UnresolvedException.java
@@ -0,0 +1,82 @@
+package org.apache.yoko.orb.OB;
+
+import static org.apache.yoko.orb.OCI.GiopVersion.GIOP1_2;
+
+import java.io.IOException;
+import java.io.NotSerializableException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.apache.yoko.orb.CORBA.InputStream;
+import org.apache.yoko.orb.OCI.Buffer;
+import org.omg.CORBA.SystemException;
+import org.omg.CORBA.UNKNOWN;
+import org.omg.CORBA.portable.UnknownException;
+import org.omg.SendingContext.CodeBase;
+
+public class UnresolvedException extends UnknownException {
+    private static final Logger LOGGER = Logger.getLogger(UnresolvedException.class.getName());
+    private final UNKNOWN ex;
+    private final byte[] data;
+    private final CodeConverters converters;
+    private final CodeBase sendingContextRuntime;
+    private final String codebase;
+
+    UnresolvedException(UNKNOWN ex, byte[] data, InputStream is) {
+        super(ex);
+        super.completed = ex.completed;
+        super.minor = ex.minor;
+        this.ex = ex;
+        this.data = data;
+        this.converters = is._OB_codeConverters();
+        this.sendingContextRuntime = is.__getSendingContextRuntime();
+        this.codebase = is.__getCodeBase();
+    }
+
+    public SystemException resolve() {
+        Buffer buf = new Buffer(data, data.length);
+        try (org.apache.yoko.orb.CORBA.InputStream in =
+                new org.apache.yoko.orb.CORBA.InputStream(buf, 0, false, converters, GIOP1_2)) {
+            if (LOGGER.isLoggable(Level.FINE))
+                LOGGER.fine(String.format("Unpacking Unknown Exception Info%n%s", in.dumpData()));
+            try {
+                in.__setSendingContextRuntime(sendingContextRuntime);
+                in.__setCodeBase(codebase);
+                in._OB_readEndian();
+                Throwable t = (Throwable) in.read_value();
+                UnknownException x = new UnknownException(t);
+                x.completed = ex.completed;
+                x.minor = ex.minor;
+                return x;
+            } catch (Exception e) {
+                final String dump = in.dumpData();
+                final int curPos = in.buf_.pos();
+                in.buf_.pos(0);
+                final String fullDump = in.dumpData();
+                in.buf_.pos(curPos);
+                try (StringWriter sw = new StringWriter();
+                        PrintWriter pw = new PrintWriter(sw)) {
+                    e.printStackTrace(pw);
+                    LOGGER.severe(String.format("%s:%n%s:%n%s%n%s:%n%s%n%s:%n%s",
+                            "Exception reading UnknownExceptionInfo service context",
+                            "Remaining data", dump, "Full data", fullDump,
+                            "Exception thrown", sw.toString()));
+                }
+            }
+        } catch (IOException e) {
+            //ignore IOException from AutoCloseable.close()
+        }
+        return ex;
+    }
+
+    private void readObject(ObjectInputStream ois) throws IOException {
+        throw new NotSerializableException();
+    }
+    private void writeObject(ObjectOutputStream oos) throws IOException {
+        throw new NotSerializableException();
+    }
+}
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
index b4df52f..f9ee9f6 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OB/ValueWriter.java
@@ -480,7 +480,7 @@
         //
         // Determine if chunked encoding is needed.
         //
-        boolean isChunked = valueHandler.isCustomMarshaled (clz);
+        boolean isChunked = valueHandler.isCustomMarshaled(clz);
 
         int pos = beginValue (tag, ids, codebase, isChunked);
         instanceTable_.put (value, new Integer (pos));
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/AutoReadWriteLock.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/AutoReadWriteLock.java
deleted file mode 100644
index 85ae80f..0000000
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/AutoReadWriteLock.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package org.apache.yoko.orb.OBCORBA;
-
-import java.util.concurrent.atomic.AtomicReference;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-class AutoLock implements AutoCloseable{
-    final AtomicReference<Lock> lockRef;
-    
-    AutoLock(Lock lock) {
-        lockRef = new AtomicReference<>(lock);
-        lock.lock();
-    }
-    
-    @Override
-    public void close() {
-        Lock lock = lockRef.getAndSet(null);
-        if (lock != null) lock.unlock();
-    }
-}
-
-class AutoReadWriteLock {
-    private final ReadWriteLock lock = new ReentrantReadWriteLock();
-    
-    public AutoLock getReadLock() {
-        return new AutoLock(lock.readLock());
-    }
-
-    public AutoLock getWriteLock() {
-        return new AutoLock(lock.writeLock());
-    }
-}
\ No newline at end of file
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java b/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java
index c16544b..766f9aa 100644
--- a/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/OBCORBA/ORB_impl.java
@@ -25,6 +25,8 @@
 import org.apache.yoko.orb.cmsf.CmsfClientInterceptor;
 import org.apache.yoko.orb.cmsf.CmsfIORInterceptor;
 import org.apache.yoko.orb.cmsf.CmsfServerInterceptor;
+import org.apache.yoko.orb.util.AutoLock;
+import org.apache.yoko.orb.util.AutoReadWriteLock;
 import org.apache.yoko.orb.util.GetSystemPropertyAction;
 import org.apache.yoko.util.osgi.ProviderLocator;
 import org.omg.CORBA.OBJECT_NOT_EXIST;
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoLock.java b/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoLock.java
new file mode 100644
index 0000000..c2dbf5d
--- /dev/null
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoLock.java
@@ -0,0 +1,51 @@
+package org.apache.yoko.orb.util;
+
+import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.locks.Condition;
+import java.util.concurrent.locks.Lock;
+
+public class AutoLock implements AutoCloseable {
+    final AtomicReference<Lock> lockRef;
+    final Lock downgradeLock;
+    final int downgradeHeld;
+
+    AutoLock(Lock lock) {
+        this(lock, null, 0);
+    }
+
+    AutoLock(Lock lock, Lock downgradeLock, int downgradeHeld) {
+        lockRef = new AtomicReference<>(lock);
+        this.downgradeLock = downgradeLock;
+        this.downgradeHeld = downgradeHeld;
+        for (int i = downgradeHeld; i > 0; i--) {
+            downgradeLock.unlock();
+        }
+        lock.lock();
+    }
+
+    @Override
+    public void close() {
+        Lock lock = lockRef.getAndSet(null);
+        if (lock == null)
+            return;
+        for (int i = 0; i < downgradeHeld; i++) {
+            downgradeLock.lock();
+        }
+        lock.unlock();
+    }
+
+    public Condition newCondition() {
+        return lockRef.get().newCondition();
+    }
+
+    public boolean downgrade() {
+        if (downgradeLock == null)
+            return false;
+        Lock oldLock = lockRef.getAndSet(downgradeLock);
+        if (oldLock == downgradeLock)
+            return false;
+        downgradeLock.lock();
+        oldLock.unlock();
+        return true;
+    }
+}
diff --git a/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoReadWriteLock.java b/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoReadWriteLock.java
new file mode 100644
index 0000000..f9b47d0
--- /dev/null
+++ b/yoko-core/src/main/java/org/apache/yoko/orb/util/AutoReadWriteLock.java
@@ -0,0 +1,15 @@
+package org.apache.yoko.orb.util;
+
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
+public class AutoReadWriteLock {
+    private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
+    
+    public AutoLock getReadLock() {
+        return new AutoLock(lock.readLock());
+    }
+
+    public AutoLock getWriteLock() {
+        return new AutoLock(lock.writeLock(), lock.readLock(), lock.getReadHoldCount());
+    }
+}
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FVDValueDescriptor.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FVDValueDescriptor.java
index 01e7da4..5c20871 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FVDValueDescriptor.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FVDValueDescriptor.java
@@ -17,10 +17,9 @@
 */ 
 package org.apache.yoko.rmi.impl;
 
-import org.omg.CORBA.AttributeDescription;
 import org.omg.CORBA.TypeCode;
-import org.omg.CORBA.ValueDefPackage.FullValueDescription;
 import org.omg.CORBA.ValueMember;
+import org.omg.CORBA.ValueDefPackage.FullValueDescription;
 
 /**
  * @author krab
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FieldDescriptor.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FieldDescriptor.java
index a19d21b..87f37da 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FieldDescriptor.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/FieldDescriptor.java
@@ -1,20 +1,20 @@
 /**
-*
-* Licensed to the Apache Software Foundation (ASF) under one or more
-*  contributor license agreements.  See the NOTICE file distributed with
-*  this work for additional information regarding copyright ownership.
-*  The ASF licenses this file to You under the Apache License, Version 2.0
-*  (the "License"); you may not use this file except in compliance with
-*  the License.  You may obtain a copy of the License at
-*
-*     http://www.apache.org/licenses/LICENSE-2.0
-*
-*  Unless required by applicable law or agreed to in writing, software
-*  distributed under the License is distributed on an "AS IS" BASIS,
-*  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-*  See the License for the specific language governing permissions and
-*  limitations under the License.
-*/ 
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ *  contributor license agreements.  See the NOTICE file distributed with
+ *  this work for additional information regarding copyright ownership.
+ *  The ASF licenses this file to You under the Apache License, Version 2.0
+ *  (the "License"); you may not use this file except in compliance with
+ *  the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */ 
 
 package org.apache.yoko.rmi.impl;
 
@@ -29,7 +29,7 @@
 import org.omg.CORBA.ValueMember;
 
 public abstract class FieldDescriptor extends ModelElement implements
-        Comparable {
+Comparable {
     static Logger logger = Logger.getLogger(FieldDescriptor.class.getName());
 
     org.apache.yoko.rmi.util.corba.Field field;
@@ -160,7 +160,7 @@
 
         } else {
             if(org.omg.CORBA.Object.class.isAssignableFrom(type)) {
-        	return new CorbaObjectFieldDescriptor(owner, type, name, f);
+                return new CorbaObjectFieldDescriptor(owner, type, name, f);
             }
             if (java.lang.Object.class.equals(type)
                     || java.io.Externalizable.class.equals(type)
@@ -168,7 +168,7 @@
                 return new AnyFieldDescriptor(owner, type, name, f);
 
             } else if (java.rmi.Remote.class.isAssignableFrom(type) 
-        	    || java.rmi.Remote.class.equals(type))
+                    || java.rmi.Remote.class.equals(type))
             {
                 return new RemoteFieldDescriptor(owner, type, name, f);
 
@@ -1143,7 +1143,7 @@
 class CorbaObjectFieldDescriptor extends FieldDescriptor {
 
     protected CorbaObjectFieldDescriptor(Class owner, Class type, String name, Field f) {
-	super(owner, type, name, f);
+        super(owner, type, name, f);
     }
 
     void copyState(final Object orig, final Object copy, CopyState state) {
@@ -1167,31 +1167,31 @@
     void read(ObjectReader reader, Object obj) throws IOException {
         Object value = reader.readCorbaObject(null);
         try {
-	    field.set(obj, value);
-	} catch (IllegalAccessException e) {
-	    throw (IOException)new IOException(e.getMessage()).initCause(e);
-	}
+            field.set(obj, value);
+        } catch (IllegalAccessException e) {
+            throw (IOException)new IOException(e.getMessage()).initCause(e);
+        }
     }
 
     void readFieldIntoMap(ObjectReader reader, Map map) throws IOException {
-	Object value = reader.readCorbaObject(null);
-	map.put(getJavaName(), value);
-	
+        Object value = reader.readCorbaObject(null);
+        map.put(getJavaName(), value);
+
     }
 
     void write(ObjectWriter writer, Object obj) throws IOException {
-	try {
-	    writer.writeCorbaObject(field.get(obj));
-	}
-	catch(IllegalAccessException e) {
-	    throw (IOException)new IOException(e.getMessage()).initCause(e);
-	}
+        try {
+            writer.writeCorbaObject(field.get(obj));
+        }
+        catch(IllegalAccessException e) {
+            throw (IOException)new IOException(e.getMessage()).initCause(e);
+        }
     }
 
     void writeFieldFromMap(ObjectWriter writer, Map map) throws IOException {
         org.omg.CORBA.Object value = (org.omg.CORBA.Object) map.get(getJavaName());
         writer.writeCorbaObject(value);
-	
+
     }
-    
+
 }
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeDescriptor.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeDescriptor.java
index 5b8b833..78303c9 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeDescriptor.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeDescriptor.java
@@ -22,6 +22,7 @@
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.util.Map;
+import java.util.Objects;
 import java.util.logging.Logger;
 
 import org.omg.CORBA.portable.InputStream;
@@ -35,10 +36,68 @@
 
     protected RemoteInterfaceDescriptor remoteDescriptor;
 
+    private FullKey _key;
+
     public Class getJavaClass() {
         return _java_class;
     }
 
+    public final FullKey getKey() {
+        if (null == _key) {
+            _key = new FullKey(getRepositoryID(), getJavaClass());
+        }
+        return _key;
+    }
+
+    public static class SimpleKey {
+        private final String repid;
+
+        public SimpleKey(String repid) {
+            this.repid = repid;
+        }
+
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((repid == null) ? 0 : repid.hashCode());
+            return result;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (!(obj instanceof SimpleKey)) return false;
+            return Objects.equals(repid, ((SimpleKey)obj).repid);
+        }
+    }
+
+    public static final class FullKey extends SimpleKey {
+        private final Class<?> localType;
+
+        public FullKey(String repid, Class<?> localType) {
+            super(repid);
+            this.localType = localType;
+        }
+
+        @Override
+        public int hashCode() {
+            // must just be the same as SimpleKey's hashCode
+            return super.hashCode();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) return true;
+            if (obj == null) return false;
+            if (!(obj instanceof SimpleKey)) return false;
+            if (obj instanceof FullKey &&
+                    !!!Objects.equals(localType, ((FullKey)obj).localType)) return false;
+            return super.equals(obj);
+        }
+    }
+
     @Override
     public String toString() {
         return String.format("%s{class=\"%s\",repId=\"%s\"}",
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
index 2663c2c..ea1afce 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/TypeRepository.java
@@ -39,16 +39,17 @@
 
 import javax.rmi.CORBA.ClassDesc;
 
-import org.apache.yoko.rmi.util.ByteBuffer;
-import org.apache.yoko.rmi.util.ByteString;
+import org.apache.yoko.rmi.util.SearchKey;
+import org.apache.yoko.rmi.util.WeakKey;
 import org.omg.CORBA.MARSHAL;
 import org.omg.CORBA.ValueDefPackage.FullValueDescription;
 import org.omg.CORBA.portable.IDLEntity;
 import org.omg.SendingContext.CodeBase;
 import org.omg.SendingContext.CodeBaseHelper;
 import org.omg.SendingContext.RunTime;
-import org.apache.yoko.rmi.util.SearchKey;
-import org.apache.yoko.rmi.util.WeakKey;
+
+import org.apache.yoko.rmi.impl.TypeDescriptor.FullKey;
+import org.apache.yoko.rmi.impl.TypeDescriptor.SimpleKey;
 
 public class TypeRepository {
     static final Logger logger = Logger.getLogger(TypeRepository.class.getName());
@@ -56,23 +57,32 @@
     org.omg.CORBA.ORB orb;
 
     private static final class TypeDescriptorCache {
-        private final ConcurrentMap<WeakKey<String>, WeakReference<TypeDescriptor>> map = new ConcurrentHashMap<>();
-        private final ReferenceQueue<String> staleKeys = new ReferenceQueue<>();
+        private final ConcurrentMap<WeakKey<FullKey>, WeakReference<TypeDescriptor>> map =
+                new ConcurrentHashMap<>();
+        private final ReferenceQueue<FullKey> staleKeys = new ReferenceQueue<>();
 
-        public TypeDescriptor get(String repId) {
+        public TypeDescriptor get(String repid) {
             cleanStaleKeys();
-            WeakReference<TypeDescriptor> ref = map.get(new SearchKey<String>(repId));
+            WeakReference<TypeDescriptor> ref =
+                    map.get(new SearchKey<SimpleKey>(new SimpleKey(repid)));
+            return (null == ref) ? null : ref.get();
+        }
+
+        public TypeDescriptor get(String repid, Class<?> localType) {
+            cleanStaleKeys();
+            WeakReference<TypeDescriptor> ref =
+                    map.get(new SearchKey<FullKey>(new FullKey(repid, localType)));
             return (null == ref) ? null : ref.get();
         }
 
         public void put(TypeDescriptor typeDesc) {
             cleanStaleKeys();
             final WeakReference<TypeDescriptor> value = new WeakReference<>(typeDesc);
-            map.putIfAbsent(new WeakKey<String>(typeDesc.getRepositoryID(), staleKeys), value);
+            map.putIfAbsent(new WeakKey<FullKey>(typeDesc.getKey(), staleKeys), value);
         }
 
         private void cleanStaleKeys() {
-            for (Reference<? extends String> staleKey = staleKeys.poll(); staleKey != null; staleKey = staleKeys.poll()) {
+            for (Reference<? extends Object> staleKey = staleKeys.poll(); staleKey != null; staleKey = staleKeys.poll()) {
                 map.remove(staleKey);
             }
         }
@@ -314,13 +324,13 @@
             return (ValueDescriptor) getDescriptor(clz);
         }
 
-        ValueDescriptor clzdesc = (ValueDescriptor) repIdDescriptors.get(repid);
+        ValueDescriptor clzdesc = (ValueDescriptor) repIdDescriptors.get(repid, clz);
         if (clzdesc != null) {
             return clzdesc;
         }
 
         if (clz != null) {
-            logger.fine("Requesting type descriptor for class " + clz.getName() + " with repid " + repid); 
+            logger.fine("Requesting type descriptor for class " + clz.getName() + " with repid " + repid);
             // special handling for array value types.
             if (clz.isArray()) {
                 //TODO don't we need to look up the FVD for the array element?
@@ -339,7 +349,7 @@
             // and padding and these can't be reliably identified without this remote info.  cf YOKO-434.
         }
 
-        logger.fine("Requesting type descriptor for repid " + repid); 
+        logger.fine("Requesting type descriptor for repid " + repid);
         CodeBase codebase = CodeBaseHelper.narrow(runtime);
         if (codebase == null) {
             throw new MARSHAL("cannot locate RunTime CodeBase");
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueDescriptor.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueDescriptor.java
index a4b869f..5308360 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueDescriptor.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueDescriptor.java
@@ -445,11 +445,12 @@
     }
 
     public boolean isCustomMarshalled() {
-        if ((_is_externalizable || (_write_object_method != null)))
-            return true;
+        return (_is_externalizable || (_write_object_method != null));
+    }
 
-        return (_super_descriptor != null) && _super_descriptor.isCustomMarshalled();
-
+    public boolean isChunked() {
+        if (isCustomMarshalled()) return true;
+        return (_super_descriptor != null) && _super_descriptor.isChunked();
     }
 
     public Serializable writeReplace(Serializable val) {
diff --git a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueHandlerImpl.java b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueHandlerImpl.java
index 01f70fe..7d898c6 100755
--- a/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueHandlerImpl.java
+++ b/yoko-rmi-impl/src/main/java/org/apache/yoko/rmi/impl/ValueHandlerImpl.java
@@ -131,8 +131,9 @@
         return getRepository().getDescriptor(clz).getRepositoryID();
     }
 
+    @Override
     public boolean isCustomMarshaled(java.lang.Class clz) {
-        return desc(clz).isCustomMarshalled();
+        return desc(clz).isChunked();
     }
 
     public synchronized org.omg.SendingContext.RunTime getRunTimeCodeBase() {