changeset 4651:15078025eed9

Merge
author mullan
date Wed, 19 Oct 2011 10:16:22 -0400
parents 57eb9136b73b 8640b7185be1
children c5c91589b126
files
diffstat 10 files changed, 636 insertions(+), 142 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/sun/security/pkcs11/KeyCache.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/pkcs11/KeyCache.java	Wed Oct 19 10:16:22 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -48,7 +48,7 @@
  */
 final class KeyCache {
 
-    private final Cache strongCache;
+    private final Cache<IdentityWrapper, P11Key> strongCache;
 
     private WeakReference<Map<Key,P11Key>> cacheReference;
 
@@ -77,7 +77,7 @@
     }
 
     synchronized P11Key get(Key key) {
-        P11Key p11Key = (P11Key)strongCache.get(new IdentityWrapper(key));
+        P11Key p11Key = strongCache.get(new IdentityWrapper(key));
         if (p11Key != null) {
             return p11Key;
         }
@@ -94,8 +94,8 @@
         Map<Key,P11Key> map =
                 (cacheReference == null) ? null : cacheReference.get();
         if (map == null) {
-            map = new IdentityHashMap<Key,P11Key>();
-            cacheReference = new WeakReference<Map<Key,P11Key>>(map);
+            map = new IdentityHashMap<>();
+            cacheReference = new WeakReference<>(map);
         }
         map.put(key, p11Key);
     }
--- a/src/share/classes/sun/security/provider/X509Factory.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/provider/X509Factory.java	Wed Oct 19 10:16:22 2011 -0400
@@ -64,8 +64,10 @@
 
     private static final int ENC_MAX_LENGTH = 4096 * 1024; // 4 MB MAX
 
-    private static final Cache certCache = Cache.newSoftMemoryCache(750);
-    private static final Cache crlCache = Cache.newSoftMemoryCache(750);
+    private static final Cache<Object, X509CertImpl> certCache
+        = Cache.newSoftMemoryCache(750);
+    private static final Cache<Object, X509CRLImpl> crlCache
+        = Cache.newSoftMemoryCache(750);
 
     /**
      * Generates an X.509 certificate object and initializes it with
@@ -90,7 +92,7 @@
         try {
             byte[] encoding = readOneBlock(is);
             if (encoding != null) {
-                X509CertImpl cert = (X509CertImpl)getFromCache(certCache, encoding);
+                X509CertImpl cert = getFromCache(certCache, encoding);
                 if (cert != null) {
                     return cert;
                 }
@@ -151,7 +153,7 @@
         } else {
             encoding = c.getEncoded();
         }
-        X509CertImpl newC = (X509CertImpl)getFromCache(certCache, encoding);
+        X509CertImpl newC = getFromCache(certCache, encoding);
         if (newC != null) {
             return newC;
         }
@@ -181,7 +183,7 @@
         } else {
             encoding = c.getEncoded();
         }
-        X509CRLImpl newC = (X509CRLImpl)getFromCache(crlCache, encoding);
+        X509CRLImpl newC = getFromCache(crlCache, encoding);
         if (newC != null) {
             return newC;
         }
@@ -198,18 +200,17 @@
     /**
      * Get the X509CertImpl or X509CRLImpl from the cache.
      */
-    private static synchronized Object getFromCache(Cache cache,
+    private static synchronized <K,V> V getFromCache(Cache<K,V> cache,
             byte[] encoding) {
         Object key = new Cache.EqualByteArray(encoding);
-        Object value = cache.get(key);
-        return value;
+        return cache.get(key);
     }
 
     /**
      * Add the X509CertImpl or X509CRLImpl to the cache.
      */
-    private static synchronized void addToCache(Cache cache, byte[] encoding,
-            Object value) {
+    private static synchronized <V> void addToCache(Cache<Object, V> cache,
+            byte[] encoding, V value) {
         if (encoding.length > ENC_MAX_LENGTH) {
             return;
         }
@@ -361,7 +362,7 @@
         try {
             byte[] encoding = readOneBlock(is);
             if (encoding != null) {
-                X509CRLImpl crl = (X509CRLImpl)getFromCache(crlCache, encoding);
+                X509CRLImpl crl = getFromCache(crlCache, encoding);
                 if (crl != null) {
                     return crl;
                 }
--- a/src/share/classes/sun/security/provider/certpath/CertStoreHelper.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/provider/certpath/CertStoreHelper.java	Wed Oct 19 10:16:22 2011 -0400
@@ -59,12 +59,13 @@
             "SSLServer",
             "sun.security.provider.certpath.ssl.SSLServerCertStoreHelper");
     };
-    private static Cache cache = Cache.newSoftMemoryCache(NUM_TYPES);
+    private static Cache<String, CertStoreHelper> cache
+        = Cache.newSoftMemoryCache(NUM_TYPES);
 
     public static CertStoreHelper getInstance(final String type)
         throws NoSuchAlgorithmException
     {
-        CertStoreHelper helper = (CertStoreHelper)cache.get(type);
+        CertStoreHelper helper = cache.get(type);
         if (helper != null) {
             return helper;
         }
--- a/src/share/classes/sun/security/provider/certpath/URICertStore.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/provider/certpath/URICertStore.java	Wed Oct 19 10:16:22 2011 -0400
@@ -100,8 +100,7 @@
     private final CertificateFactory factory;
 
     // cached Collection of X509Certificates (may be empty, never null)
-    private Collection<X509Certificate> certs =
-        Collections.<X509Certificate>emptySet();
+    private Collection<X509Certificate> certs = Collections.emptySet();
 
     // cached X509CRL (may be null)
     private X509CRL crl;
@@ -157,14 +156,14 @@
      * Returns a URI CertStore. This method consults a cache of
      * CertStores (shared per JVM) using the URI as a key.
      */
-    private static final Cache certStoreCache =
-        Cache.newSoftMemoryCache(CACHE_SIZE);
+    private static final Cache<URICertStoreParameters, CertStore>
+        certStoreCache = Cache.newSoftMemoryCache(CACHE_SIZE);
     static synchronized CertStore getInstance(URICertStoreParameters params)
         throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
         if (debug != null) {
             debug.println("CertStore URI:" + params.uri);
         }
-        CertStore ucs = (CertStore) certStoreCache.get(params);
+        CertStore ucs = certStoreCache.get(params);
         if (ucs == null) {
             ucs = new UCS(new URICertStore(params), null, "URI", params);
             certStoreCache.put(params, ucs);
@@ -287,7 +286,7 @@
         }
         // exception, forget previous values
         lastModified = 0;
-        certs = Collections.<X509Certificate>emptySet();
+        certs = Collections.emptySet();
         return certs;
     }
 
@@ -394,7 +393,7 @@
         // exception, forget previous values
         lastModified = 0;
         crl = null;
-        return Collections.<X509CRL>emptyList();
+        return Collections.emptyList();
     }
 
     /**
@@ -404,9 +403,9 @@
     private static Collection<X509CRL> getMatchingCRLs
         (X509CRL crl, CRLSelector selector) {
         if (selector == null || (crl != null && selector.match(crl))) {
-            return Collections.<X509CRL>singletonList(crl);
+            return Collections.singletonList(crl);
         } else {
-            return Collections.<X509CRL>emptyList();
+            return Collections.emptyList();
         }
     }
 
--- a/src/share/classes/sun/security/provider/certpath/X509CertificatePair.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/provider/certpath/X509CertificatePair.java	Wed Oct 19 10:16:22 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2002, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -79,7 +79,8 @@
     private X509Certificate reverse;
     private byte[] encoded;
 
-    private static final Cache cache = Cache.newSoftMemoryCache(750);
+    private static final Cache<Object, X509CertificatePair> cache
+        = Cache.newSoftMemoryCache(750);
 
     /**
      * Creates an empty instance of X509CertificatePair.
@@ -114,7 +115,7 @@
      *
      * For internal use only, external code should use generateCertificatePair.
      */
-    private X509CertificatePair(byte[] encoded)throws CertificateException {
+    private X509CertificatePair(byte[] encoded) throws CertificateException {
         try {
             parse(new DerValue(encoded));
             this.encoded = encoded;
@@ -138,7 +139,7 @@
     public static synchronized X509CertificatePair generateCertificatePair
             (byte[] encoded) throws CertificateException {
         Object key = new Cache.EqualByteArray(encoded);
-        X509CertificatePair pair = (X509CertificatePair)cache.get(key);
+        X509CertificatePair pair = cache.get(key);
         if (pair != null) {
             return pair;
         }
--- a/src/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/provider/certpath/ldap/LDAPCertStore.java	Wed Oct 19 10:16:22 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2000, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -160,7 +160,7 @@
      */
     private boolean prefetchCRLs = false;
 
-    private final Cache valueCache;
+    private final Cache<String, byte[][]> valueCache;
 
     private int cacheHits = 0;
     private int cacheMisses = 0;
@@ -207,10 +207,11 @@
      * Returns an LDAP CertStore. This method consults a cache of
      * CertStores (shared per JVM) using the LDAP server/port as a key.
      */
-    private static final Cache certStoreCache = Cache.newSoftMemoryCache(185);
+    private static final Cache<LDAPCertStoreParameters, CertStore>
+        certStoreCache = Cache.newSoftMemoryCache(185);
     static synchronized CertStore getInstance(LDAPCertStoreParameters params)
         throws NoSuchAlgorithmException, InvalidAlgorithmParameterException {
-        CertStore lcs = (CertStore) certStoreCache.get(params);
+        CertStore lcs = certStoreCache.get(params);
         if (lcs == null) {
             lcs = CertStore.getInstance("LDAP", params);
             certStoreCache.put(params, lcs);
@@ -232,7 +233,7 @@
     private void createInitialDirContext(String server, int port)
             throws InvalidAlgorithmParameterException {
         String url = "ldap://" + server + ":" + port;
-        Hashtable<String,Object> env = new Hashtable<String,Object>();
+        Hashtable<String,Object> env = new Hashtable<>();
         env.put(Context.INITIAL_CONTEXT_FACTORY,
                 "com.sun.jndi.ldap.LdapCtxFactory");
         env.put(Context.PROVIDER_URL, url);
@@ -283,7 +284,7 @@
 
         LDAPRequest(String name) {
             this.name = name;
-            requestedAttributes = new ArrayList<String>(5);
+            requestedAttributes = new ArrayList<>(5);
         }
 
         String getName() {
@@ -311,7 +312,7 @@
                         + cacheMisses);
             }
             String cacheKey = name + "|" + attrId;
-            byte[][] values = (byte[][])valueCache.get(cacheKey);
+            byte[][] values = valueCache.get(cacheKey);
             if (values != null) {
                 cacheHits++;
                 return values;
@@ -347,7 +348,7 @@
                     System.out.println("LDAP requests: " + requests);
                 }
             }
-            valueMap = new HashMap<String, byte[][]>(8);
+            valueMap = new HashMap<>(8);
             String[] attrIds = requestedAttributes.toArray(STRING0);
             Attributes attrs;
             try {
@@ -429,10 +430,10 @@
 
         int n = encodedCert.length;
         if (n == 0) {
-            return Collections.<X509Certificate>emptySet();
+            return Collections.emptySet();
         }
 
-        List<X509Certificate> certs = new ArrayList<X509Certificate>(n);
+        List<X509Certificate> certs = new ArrayList<>(n);
         /* decode certs and check if they satisfy selector */
         for (int i = 0; i < n; i++) {
             ByteArrayInputStream bais = new ByteArrayInputStream(encodedCert[i]);
@@ -477,11 +478,10 @@
 
         int n = encodedCertPair.length;
         if (n == 0) {
-            return Collections.<X509CertificatePair>emptySet();
+            return Collections.emptySet();
         }
 
-        List<X509CertificatePair> certPairs =
-                                new ArrayList<X509CertificatePair>(n);
+        List<X509CertificatePair> certPairs = new ArrayList<>(n);
         /* decode each cert pair and add it to the Collection */
         for (int i = 0; i < n; i++) {
             try {
@@ -528,8 +528,7 @@
                                 getCertPairs(request, CROSS_CERT);
 
         // Find Certificates that match and put them in a list
-        ArrayList<X509Certificate> matchingCerts =
-                                        new ArrayList<X509Certificate>();
+        ArrayList<X509Certificate> matchingCerts = new ArrayList<>();
         for (X509CertificatePair certPair : certPairs) {
             X509Certificate cert;
             if (forward != null) {
@@ -587,7 +586,7 @@
         int basicConstraints = xsel.getBasicConstraints();
         String subject = xsel.getSubjectAsString();
         String issuer = xsel.getIssuerAsString();
-        HashSet<X509Certificate> certs = new HashSet<X509Certificate>();
+        HashSet<X509Certificate> certs = new HashSet<>();
         if (debug != null) {
             debug.println("LDAPCertStore.engineGetCertificates() basicConstraints: "
                 + basicConstraints);
@@ -706,10 +705,10 @@
 
         int n = encodedCRL.length;
         if (n == 0) {
-            return Collections.<X509CRL>emptySet();
+            return Collections.emptySet();
         }
 
-        List<X509CRL> crls = new ArrayList<X509CRL>(n);
+        List<X509CRL> crls = new ArrayList<>(n);
         /* decode each crl and check if it matches selector */
         for (int i = 0; i < n; i++) {
             try {
@@ -765,13 +764,13 @@
             throw new CertStoreException("need X509CRLSelector to find CRLs");
         }
         X509CRLSelector xsel = (X509CRLSelector) selector;
-        HashSet<X509CRL> crls = new HashSet<X509CRL>();
+        HashSet<X509CRL> crls = new HashSet<>();
 
         // Look in directory entry for issuer of cert we're checking.
         Collection<Object> issuerNames;
         X509Certificate certChecking = xsel.getCertificateChecking();
         if (certChecking != null) {
-            issuerNames = new HashSet<Object>();
+            issuerNames = new HashSet<>();
             X500Principal issuer = certChecking.getIssuerX500Principal();
             issuerNames.add(issuer.getName(X500Principal.RFC2253));
         } else {
@@ -796,7 +795,7 @@
                 issuerName = (String)nameObject;
             }
             // If all we want is CA certs, try to get the (probably shorter) ARL
-            Collection<X509CRL> entryCRLs = Collections.<X509CRL>emptySet();
+            Collection<X509CRL> entryCRLs = Collections.emptySet();
             if (certChecking == null || certChecking.getBasicConstraints() != -1) {
                 LDAPRequest request = new LDAPRequest(issuerName);
                 request.addRequestedAttribute(CROSS_CERT);
@@ -1028,9 +1027,9 @@
             throws IOException {
             this.selector = selector == null ? new X509CRLSelector() : selector;
             this.certIssuers = certIssuers;
-            issuerNames = new HashSet<Object>();
+            issuerNames = new HashSet<>();
             issuerNames.add(ldapDN);
-            issuers = new HashSet<X500Principal>();
+            issuers = new HashSet<>();
             issuers.add(new X500Name(ldapDN).asX500Principal());
         }
         // we only override the get (accessor methods) since the set methods
--- a/src/share/classes/sun/security/ssl/CipherBox.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/ssl/CipherBox.java	Wed Oct 19 10:16:22 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -305,9 +305,11 @@
                     byte[] buf = null;
                     int limit = bb.limit();
                     if (bb.hasArray()) {
+                        int arrayOffset = bb.arrayOffset();
                         buf = bb.array();
-                        System.arraycopy(buf, pos,
-                                buf, pos + prefix.length, limit - pos);
+                        System.arraycopy(buf, arrayOffset + pos,
+                            buf, arrayOffset + pos + prefix.length,
+                            limit - pos);
                         bb.limit(limit + prefix.length);
                     } else {
                         buf = new byte[limit - pos];
@@ -491,9 +493,10 @@
                     byte[] buf = null;
                     int limit = bb.limit();
                     if (bb.hasArray()) {
+                        int arrayOffset = bb.arrayOffset();
                         buf = bb.array();
-                        System.arraycopy(buf, pos + blockSize,
-                                         buf, pos, limit - pos - blockSize);
+                        System.arraycopy(buf, arrayOffset + pos + blockSize,
+                            buf, arrayOffset + pos, limit - pos - blockSize);
                         bb.limit(limit - blockSize);
                     } else {
                         buf = new byte[limit - pos - blockSize];
--- a/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/ssl/SSLSessionContextImpl.java	Wed Oct 19 10:16:22 2011 -0400
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1999, 2009, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1999, 2011, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -43,11 +43,14 @@
 import javax.net.ssl.SSLSession;
 
 import sun.security.util.Cache;
+import sun.security.util.Cache.CacheVisitor;
 
 
 final class SSLSessionContextImpl implements SSLSessionContext {
-    private Cache sessionCache;         // session cache, session id as key
-    private Cache sessionHostPortCache; // session cache, "host:port" as key
+    private Cache<SessionId, SSLSessionImpl> sessionCache;
+                                        // session cache, session id as key
+    private Cache<String, SSLSessionImpl> sessionHostPortCache;
+                                        // session cache, "host:port" as key
     private int cacheLimit;             // the max cache size
     private int timeout;                // timeout in seconds
 
@@ -71,8 +74,7 @@
             throw new NullPointerException("session id cannot be null");
         }
 
-        SSLSessionImpl sess =
-                (SSLSessionImpl)sessionCache.get(new SessionId(sessionId));
+        SSLSessionImpl sess = sessionCache.get(new SessionId(sessionId));
         if (!isTimedout(sess)) {
             return sess;
         }
@@ -157,8 +159,7 @@
             return null;
         }
 
-        SSLSessionImpl sess =
-            (SSLSessionImpl)sessionHostPortCache.get(getKey(hostname, port));
+        SSLSessionImpl sess = sessionHostPortCache.get(getKey(hostname, port));
         if (!isTimedout(sess)) {
             return sess;
         }
@@ -193,7 +194,7 @@
 
     // package-private method, remove a cached SSLSession
     void remove(SessionId key) {
-        SSLSessionImpl s = (SSLSessionImpl)sessionCache.get(key);
+        SSLSessionImpl s = sessionCache.get(key);
         if (s != null) {
             sessionCache.remove(key);
             sessionHostPortCache.remove(
@@ -233,17 +234,17 @@
     }
 
     final class SessionCacheVisitor
-            implements sun.security.util.Cache.CacheVisitor {
+            implements Cache.CacheVisitor<SessionId, SSLSessionImpl> {
         Vector<byte[]> ids = null;
 
-        // public void visit(java.util.Map<Object, Object> map) {}
-        public void visit(java.util.Map<Object, Object> map) {
-            ids = new Vector<byte[]>(map.size());
+        // public void visit(java.util.Map<K,V> map) {}
+        public void visit(java.util.Map<SessionId, SSLSessionImpl> map) {
+            ids = new Vector<>(map.size());
 
-            for (Object key : map.keySet()) {
-                SSLSessionImpl value = (SSLSessionImpl)map.get(key);
+            for (SessionId key : map.keySet()) {
+                SSLSessionImpl value = map.get(key);
                 if (!isTimedout(value)) {
-                    ids.addElement(((SessionId)key).getId());
+                    ids.addElement(key.getId());
                 }
             }
         }
--- a/src/share/classes/sun/security/util/Cache.java	Wed Oct 19 10:15:23 2011 -0400
+++ b/src/share/classes/sun/security/util/Cache.java	Wed Oct 19 10:16:22 2011 -0400
@@ -43,7 +43,7 @@
  *
  *  . optional lifetime, specified in seconds.
  *
- *  . save for concurrent use by multiple threads
+ *  . safe for concurrent use by multiple threads
  *
  *  . values are held by either standard references or via SoftReferences.
  *    SoftReferences have the advantage that they are automatically cleared
@@ -69,7 +69,7 @@
  *
  * @author Andreas Sterbenz
  */
-public abstract class Cache {
+public abstract class Cache<K,V> {
 
     protected Cache() {
         // empty
@@ -88,12 +88,12 @@
     /**
      * Add an entry to the cache.
      */
-    public abstract void put(Object key, Object value);
+    public abstract void put(K key, V value);
 
     /**
      * Get a value from the cache.
      */
-    public abstract Object get(Object key);
+    public abstract V get(Object key);
 
     /**
      * Remove an entry from the cache.
@@ -113,14 +113,14 @@
     /**
      * accept a visitor
      */
-    public abstract void accept(CacheVisitor visitor);
+    public abstract void accept(CacheVisitor<K,V> visitor);
 
     /**
      * Return a new memory cache with the specified maximum size, unlimited
      * lifetime for entries, with the values held by SoftReferences.
      */
-    public static Cache newSoftMemoryCache(int size) {
-        return new MemoryCache(true, size);
+    public static <K,V> Cache<K,V> newSoftMemoryCache(int size) {
+        return new MemoryCache<>(true, size);
     }
 
     /**
@@ -128,23 +128,24 @@
      * specified maximum lifetime (in seconds), with the values held
      * by SoftReferences.
      */
-    public static Cache newSoftMemoryCache(int size, int timeout) {
-        return new MemoryCache(true, size, timeout);
+    public static <K,V> Cache<K,V> newSoftMemoryCache(int size, int timeout) {
+        return new MemoryCache<>(true, size, timeout);
     }
 
     /**
      * Return a new memory cache with the specified maximum size, unlimited
      * lifetime for entries, with the values held by standard references.
      */
-    public static Cache newHardMemoryCache(int size) {
-        return new MemoryCache(false, size);
+    public static <K,V> Cache<K,V> newHardMemoryCache(int size) {
+        return new MemoryCache<>(false, size);
     }
 
     /**
      * Return a dummy cache that does nothing.
      */
-    public static Cache newNullCache() {
-        return NullCache.INSTANCE;
+    @SuppressWarnings("unchecked")
+    public static <K,V> Cache<K,V> newNullCache() {
+        return (Cache<K,V>) NullCache.INSTANCE;
     }
 
     /**
@@ -152,8 +153,8 @@
      * specified maximum lifetime (in seconds), with the values held
      * by standard references.
      */
-    public static Cache newHardMemoryCache(int size, int timeout) {
-        return new MemoryCache(false, size, timeout);
+    public static <K,V> Cache<K,V> newHardMemoryCache(int size, int timeout) {
+        return new MemoryCache<>(false, size, timeout);
     }
 
     /**
@@ -193,15 +194,15 @@
         }
     }
 
-    public interface CacheVisitor {
-        public void visit(Map<Object, Object> map);
+    public interface CacheVisitor<K,V> {
+        public void visit(Map<K,V> map);
     }
 
 }
 
-class NullCache extends Cache {
+class NullCache<K,V> extends Cache<K,V> {
 
-    final static Cache INSTANCE = new NullCache();
+    final static Cache<Object,Object> INSTANCE = new NullCache<>();
 
     private NullCache() {
         // empty
@@ -215,11 +216,11 @@
         // empty
     }
 
-    public void put(Object key, Object value) {
+    public void put(K key, V value) {
         // empty
     }
 
-    public Object get(Object key) {
+    public V get(Object key) {
         return null;
     }
 
@@ -235,23 +236,26 @@
         // empty
     }
 
-    public void accept(CacheVisitor visitor) {
+    public void accept(CacheVisitor<K,V> visitor) {
         // empty
     }
 
 }
 
-class MemoryCache extends Cache {
+class MemoryCache<K,V> extends Cache<K,V> {
 
     private final static float LOAD_FACTOR = 0.75f;
 
     // XXXX
     private final static boolean DEBUG = false;
 
-    private final Map<Object, CacheEntry> cacheMap;
+    private final Map<K, CacheEntry<K,V>> cacheMap;
     private int maxSize;
     private long lifetime;
-    private final ReferenceQueue<Object> queue;
+
+    // ReferenceQueue is of type V instead of Cache<K,V>
+    // to allow SoftCacheEntry to extend SoftReference<V>
+    private final ReferenceQueue<V> queue;
 
     public MemoryCache(boolean soft, int maxSize) {
         this(soft, maxSize, 0);
@@ -260,10 +264,13 @@
     public MemoryCache(boolean soft, int maxSize, int lifetime) {
         this.maxSize = maxSize;
         this.lifetime = lifetime * 1000;
-        this.queue = soft ? new ReferenceQueue<Object>() : null;
+        if (soft)
+            this.queue = new ReferenceQueue<>();
+        else
+            this.queue = null;
+
         int buckets = (int)(maxSize / LOAD_FACTOR) + 1;
-        cacheMap = new LinkedHashMap<Object, CacheEntry>(buckets,
-                                                        LOAD_FACTOR, true);
+        cacheMap = new LinkedHashMap<>(buckets, LOAD_FACTOR, true);
     }
 
     /**
@@ -279,16 +286,17 @@
         }
         int startSize = cacheMap.size();
         while (true) {
-            CacheEntry entry = (CacheEntry)queue.poll();
+            @SuppressWarnings("unchecked")
+            CacheEntry<K,V> entry = (CacheEntry<K,V>)queue.poll();
             if (entry == null) {
                 break;
             }
-            Object key = entry.getKey();
+            K key = entry.getKey();
             if (key == null) {
                 // key is null, entry has already been removed
                 continue;
             }
-            CacheEntry currentEntry = cacheMap.remove(key);
+            CacheEntry<K,V> currentEntry = cacheMap.remove(key);
             // check if the entry in the map corresponds to the expired
             // entry. If not, readd the entry
             if ((currentEntry != null) && (entry != currentEntry)) {
@@ -314,9 +322,9 @@
         }
         int cnt = 0;
         long time = System.currentTimeMillis();
-        for (Iterator<CacheEntry> t = cacheMap.values().iterator();
+        for (Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
                 t.hasNext(); ) {
-            CacheEntry entry = t.next();
+            CacheEntry<K,V> entry = t.next();
             if (entry.isValid(time) == false) {
                 t.remove();
                 cnt++;
@@ -339,7 +347,7 @@
         if (queue != null) {
             // if this is a SoftReference cache, first invalidate() all
             // entries so that GC does not have to enqueue them
-            for (CacheEntry entry : cacheMap.values()) {
+            for (CacheEntry<K,V> entry : cacheMap.values()) {
                 entry.invalidate();
             }
             while (queue.poll() != null) {
@@ -349,12 +357,12 @@
         cacheMap.clear();
     }
 
-    public synchronized void put(Object key, Object value) {
+    public synchronized void put(K key, V value) {
         emptyQueue();
         long expirationTime = (lifetime == 0) ? 0 :
                                         System.currentTimeMillis() + lifetime;
-        CacheEntry newEntry = newEntry(key, value, expirationTime, queue);
-        CacheEntry oldEntry = cacheMap.put(key, newEntry);
+        CacheEntry<K,V> newEntry = newEntry(key, value, expirationTime, queue);
+        CacheEntry<K,V> oldEntry = cacheMap.put(key, newEntry);
         if (oldEntry != null) {
             oldEntry.invalidate();
             return;
@@ -362,8 +370,8 @@
         if (maxSize > 0 && cacheMap.size() > maxSize) {
             expungeExpiredEntries();
             if (cacheMap.size() > maxSize) { // still too large?
-                Iterator<CacheEntry> t = cacheMap.values().iterator();
-                CacheEntry lruEntry = t.next();
+                Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
+                CacheEntry<K,V> lruEntry = t.next();
                 if (DEBUG) {
                     System.out.println("** Overflow removal "
                         + lruEntry.getKey() + " | " + lruEntry.getValue());
@@ -374,9 +382,9 @@
         }
     }
 
-    public synchronized Object get(Object key) {
+    public synchronized V get(Object key) {
         emptyQueue();
-        CacheEntry entry = cacheMap.get(key);
+        CacheEntry<K,V> entry = cacheMap.get(key);
         if (entry == null) {
             return null;
         }
@@ -393,7 +401,7 @@
 
     public synchronized void remove(Object key) {
         emptyQueue();
-        CacheEntry entry = cacheMap.remove(key);
+        CacheEntry<K,V> entry = cacheMap.remove(key);
         if (entry != null) {
             entry.invalidate();
         }
@@ -402,9 +410,9 @@
     public synchronized void setCapacity(int size) {
         expungeExpiredEntries();
         if (size > 0 && cacheMap.size() > size) {
-            Iterator<CacheEntry> t = cacheMap.values().iterator();
+            Iterator<CacheEntry<K,V>> t = cacheMap.values().iterator();
             for (int i = cacheMap.size() - size; i > 0; i--) {
-                CacheEntry lruEntry = t.next();
+                CacheEntry<K,V> lruEntry = t.next();
                 if (DEBUG) {
                     System.out.println("** capacity reset removal "
                         + lruEntry.getKey() + " | " + lruEntry.getValue());
@@ -431,60 +439,61 @@
     }
 
     // it is a heavyweight method.
-    public synchronized void accept(CacheVisitor visitor) {
+    public synchronized void accept(CacheVisitor<K,V> visitor) {
         expungeExpiredEntries();
-        Map<Object, Object> cached = getCachedEntries();
+        Map<K,V> cached = getCachedEntries();
 
         visitor.visit(cached);
     }
 
-    private Map<Object, Object> getCachedEntries() {
-        Map<Object,Object> kvmap = new HashMap<Object,Object>(cacheMap.size());
+    private Map<K,V> getCachedEntries() {
+        Map<K,V> kvmap = new HashMap<>(cacheMap.size());
 
-        for (CacheEntry entry : cacheMap.values()) {
+        for (CacheEntry<K,V> entry : cacheMap.values()) {
             kvmap.put(entry.getKey(), entry.getValue());
         }
 
         return kvmap;
     }
 
-    protected CacheEntry newEntry(Object key, Object value,
-            long expirationTime, ReferenceQueue<Object> queue) {
+    protected CacheEntry<K,V> newEntry(K key, V value,
+            long expirationTime, ReferenceQueue<V> queue) {
         if (queue != null) {
-            return new SoftCacheEntry(key, value, expirationTime, queue);
+            return new SoftCacheEntry<>(key, value, expirationTime, queue);
         } else {
-            return new HardCacheEntry(key, value, expirationTime);
+            return new HardCacheEntry<>(key, value, expirationTime);
         }
     }
 
-    private static interface CacheEntry {
+    private static interface CacheEntry<K,V> {
 
         boolean isValid(long currentTime);
 
         void invalidate();
 
-        Object getKey();
+        K getKey();
 
-        Object getValue();
+        V getValue();
 
     }
 
-    private static class HardCacheEntry implements CacheEntry {
+    private static class HardCacheEntry<K,V> implements CacheEntry<K,V> {
 
-        private Object key, value;
+        private K key;
+        private V value;
         private long expirationTime;
 
-        HardCacheEntry(Object key, Object value, long expirationTime) {
+        HardCacheEntry(K key, V value, long expirationTime) {
             this.key = key;
             this.value = value;
             this.expirationTime = expirationTime;
         }
 
-        public Object getKey() {
+        public K getKey() {
             return key;
         }
 
-        public Object getValue() {
+        public V getValue() {
             return value;
         }
 
@@ -503,24 +512,25 @@
         }
     }
 
-    private static class SoftCacheEntry
-            extends SoftReference<Object> implements CacheEntry {
+    private static class SoftCacheEntry<K,V>
+            extends SoftReference<V>
+            implements CacheEntry<K,V> {
 
-        private Object key;
+        private K key;
         private long expirationTime;
 
-        SoftCacheEntry(Object key, Object value, long expirationTime,
-                ReferenceQueue<Object> queue) {
+        SoftCacheEntry(K key, V value, long expirationTime,
+                ReferenceQueue<V> queue) {
             super(value, queue);
             this.key = key;
             this.expirationTime = expirationTime;
         }
 
-        public Object getKey() {
+        public K getKey() {
             return key;
         }
 
-        public Object getValue() {
+        public V getValue() {
             return get();
         }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/sun/security/ssl/com/sun/net/ssl/internal/ssl/SSLEngineImpl/SSLEngineBadBufferArrayAccess.java	Wed Oct 19 10:16:22 2011 -0400
@@ -0,0 +1,479 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/*
+ * @test
+ * @bug 7031830
+ * @summary bad_record_mac failure on TLSv1.2 enabled connection with SSLEngine
+ * @run main/othervm SSLEngineBadBufferArrayAccess
+ *
+ *     SunJSSE does not support dynamic system properties, no way to re-use
+ *     system properties in samevm/agentvm mode.
+ */
+
+/**
+ * A SSLSocket/SSLEngine interop test case.  This is not the way to
+ * code SSLEngine-based servers, but works for what we need to do here,
+ * which is to make sure that SSLEngine/SSLSockets can talk to each other.
+ * SSLEngines can use direct or indirect buffers, and different code
+ * is used to get at the buffer contents internally, so we test that here.
+ *
+ * The test creates one SSLSocket (client) and one SSLEngine (server).
+ * The SSLSocket talks to a raw ServerSocket, and the server code
+ * does the translation between byte [] and ByteBuffers that the SSLEngine
+ * can use.  The "transport" layer consists of a Socket Input/OutputStream
+ * and two byte buffers for the SSLEngines:  think of them
+ * as directly connected pipes.
+ *
+ * Again, this is a *very* simple example: real code will be much more
+ * involved.  For example, different threading and I/O models could be
+ * used, transport mechanisms could close unexpectedly, and so on.
+ *
+ * When this application runs, notice that several messages
+ * (wrap/unwrap) pass before any application data is consumed or
+ * produced.  (For more information, please see the SSL/TLS
+ * specifications.)  There may several steps for a successful handshake,
+ * so it's typical to see the following series of operations:
+ *
+ *      client          server          message
+ *      ======          ======          =======
+ *      write()         ...             ClientHello
+ *      ...             unwrap()        ClientHello
+ *      ...             wrap()          ServerHello/Certificate
+ *      read()         ...             ServerHello/Certificate
+ *      write()         ...             ClientKeyExchange
+ *      write()         ...             ChangeCipherSpec
+ *      write()         ...             Finished
+ *      ...             unwrap()        ClientKeyExchange
+ *      ...             unwrap()        ChangeCipherSpec
+ *      ...             unwrap()        Finished
+ *      ...             wrap()          ChangeCipherSpec
+ *      ...             wrap()          Finished
+ *      read()          ...             ChangeCipherSpec
+ *      read()          ...             Finished
+ *
+ * This particular bug had a problem where byte buffers backed by an
+ * array didn't offset correctly, and we got bad MAC errors.
+ */
+import javax.net.ssl.*;
+import javax.net.ssl.SSLEngineResult.*;
+import java.io.*;
+import java.net.*;
+import java.security.*;
+import java.nio.*;
+
+public class SSLEngineBadBufferArrayAccess {
+
+    /*
+     * Enables logging of the SSL/TLS operations.
+     */
+    private static boolean logging = true;
+
+    /*
+     * Enables the JSSE system debugging system property:
+     *
+     *     -Djavax.net.debug=all
+     *
+     * This gives a lot of low-level information about operations underway,
+     * including specific handshake messages, and might be best examined
+     * after gaining some familiarity with this application.
+     */
+    private static boolean debug = false;
+    private SSLContext sslc;
+    private SSLEngine serverEngine;     // server-side SSLEngine
+    private SSLSocket sslSocket;        // client-side socket
+    private ServerSocket serverSocket;  // server-side Socket, generates the...
+    private Socket socket;              // server-side socket that will read
+
+    private final byte[] serverMsg = "Hi there Client, I'm a Server".getBytes();
+    private final byte[] clientMsg = "Hello Server, I'm a Client".getBytes();
+
+    private ByteBuffer serverOut;       // write side of serverEngine
+    private ByteBuffer serverIn;        // read side of serverEngine
+
+    private volatile Exception clientException;
+    private volatile Exception serverException;
+
+    /*
+     * For data transport, this example uses local ByteBuffers.
+     */
+    private ByteBuffer cTOs;            // "reliable" transport client->server
+    private ByteBuffer sTOc;            // "reliable" transport server->client
+
+    /*
+     * The following is to set up the keystores/trust material.
+     */
+    private static final String pathToStores = "../../../../../../../etc/";
+    private static final String keyStoreFile = "keystore";
+    private static final String trustStoreFile = "truststore";
+    private static final String passwd = "passphrase";
+    private static String keyFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores
+            + "/" + keyStoreFile;
+    private static String trustFilename =
+            System.getProperty("test.src", ".") + "/" + pathToStores
+            + "/" + trustStoreFile;
+
+    /*
+     * Main entry point for this test.
+     */
+    public static void main(String args[]) throws Exception {
+        if (debug) {
+            System.setProperty("javax.net.debug", "all");
+        }
+
+        String [] protocols = new String [] {
+            "SSLv3", "TLSv1", "TLSv1.1", "TLSv1.2" };
+
+        for (String protocol : protocols) {
+            log("Testing " + protocol);
+            /*
+             * Run the tests with direct and indirect buffers.
+             */
+            SSLEngineBadBufferArrayAccess test =
+                new SSLEngineBadBufferArrayAccess(protocol);
+            test.runTest(true);
+            test.runTest(false);
+        }
+
+        System.out.println("Test Passed.");
+    }
+
+    /*
+     * Create an initialized SSLContext to use for these tests.
+     */
+    public SSLEngineBadBufferArrayAccess(String protocol) throws Exception {
+
+        KeyStore ks = KeyStore.getInstance("JKS");
+        KeyStore ts = KeyStore.getInstance("JKS");
+
+        char[] passphrase = "passphrase".toCharArray();
+
+        ks.load(new FileInputStream(keyFilename), passphrase);
+        ts.load(new FileInputStream(trustFilename), passphrase);
+
+        KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
+        kmf.init(ks, passphrase);
+
+        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
+        tmf.init(ts);
+
+        SSLContext sslCtx = SSLContext.getInstance(protocol);
+
+        sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
+
+        sslc = sslCtx;
+    }
+
+    /*
+     * Run the test.
+     *
+     * Sit in a tight loop, with the server engine calling wrap/unwrap
+     * regardless of whether data is available or not.  We do this until
+     * we get the application data.  Then we shutdown and go to the next one.
+     *
+     * The main loop handles all of the I/O phases of the SSLEngine's
+     * lifetime:
+     *
+     *     initial handshaking
+     *     application data transfer
+     *     engine closing
+     *
+     * One could easily separate these phases into separate
+     * sections of code.
+     */
+    private void runTest(boolean direct) throws Exception {
+        boolean serverClose = direct;
+
+        serverSocket = new ServerSocket(0);
+        int port = serverSocket.getLocalPort();
+        Thread thread = createClientThread(port, serverClose);
+
+        socket = serverSocket.accept();
+        socket.setSoTimeout(500);
+        serverSocket.close();
+
+        createSSLEngine();
+        createBuffers(direct);
+
+        try {
+            boolean closed = false;
+
+            InputStream is = socket.getInputStream();
+            OutputStream os = socket.getOutputStream();
+
+            SSLEngineResult serverResult;   // results from last operation
+
+            /*
+             * Examining the SSLEngineResults could be much more involved,
+             * and may alter the overall flow of the application.
+             *
+             * For example, if we received a BUFFER_OVERFLOW when trying
+             * to write to the output pipe, we could reallocate a larger
+             * pipe, but instead we wait for the peer to drain it.
+             */
+            byte[] inbound = new byte[8192];
+            byte[] outbound = new byte[8192];
+
+            while (!isEngineClosed(serverEngine)) {
+                int len = 0;
+
+                // Inbound data
+                log("================");
+
+                // Read from the Client side.
+                try {
+                    len = is.read(inbound);
+                    if (len == -1) {
+                        throw new Exception("Unexpected EOF");
+                    }
+                    cTOs.put(inbound, 0, len);
+                } catch (SocketTimeoutException ste) {
+                    // swallow.  Nothing yet, probably waiting on us.
+                }
+
+                cTOs.flip();
+
+                serverResult = serverEngine.unwrap(cTOs, serverIn);
+                log("server unwrap: ", serverResult);
+                runDelegatedTasks(serverResult, serverEngine);
+                cTOs.compact();
+
+                // Outbound data
+                log("----");
+
+                serverResult = serverEngine.wrap(serverOut, sTOc);
+                log("server wrap: ", serverResult);
+                runDelegatedTasks(serverResult, serverEngine);
+
+                sTOc.flip();
+
+                if ((len = sTOc.remaining()) != 0) {
+                    sTOc.get(outbound, 0, len);
+                    os.write(outbound, 0, len);
+                    // Give the other side a chance to process
+                }
+
+                sTOc.compact();
+
+                if (!closed && (serverOut.remaining() == 0)) {
+                    closed = true;
+
+                    /*
+                     * We'll alternate initiatating the shutdown.
+                     * When the server initiates, it will take one more
+                     * loop, but tests the orderly shutdown.
+                     */
+                    if (serverClose) {
+                        serverEngine.closeOutbound();
+                    }
+                    serverIn.flip();
+
+                    /*
+                     * A sanity check to ensure we got what was sent.
+                     */
+                    if (serverIn.remaining() != clientMsg.length) {
+                        throw new Exception("Client:  Data length error");
+                    }
+
+                    for (int i = 0; i < clientMsg.length; i++) {
+                        if (clientMsg[i] != serverIn.get()) {
+                            throw new Exception("Client:  Data content error");
+                        }
+                    }
+                    serverIn.compact();
+                }
+            }
+            return;
+        } catch (Exception e) {
+            serverException = e;
+        } finally {
+            socket.close();
+
+            // Wait for the client to join up with us.
+            thread.join();
+            if (serverException != null) {
+                throw serverException;
+            }
+            if (clientException != null) {
+                throw clientException;
+            }
+        }
+    }
+
+    /*
+     * Create a client thread which does simple SSLSocket operations.
+     * We'll write and read one data packet.
+     */
+    private Thread createClientThread(final int port,
+            final boolean serverClose) throws Exception {
+
+        Thread t = new Thread("ClientThread") {
+
+            @Override
+            public void run() {
+                try {
+                    Thread.sleep(1000);  // Give server time to finish setup.
+
+                    sslSocket = (SSLSocket) sslc.getSocketFactory().
+                            createSocket("localhost", port);
+                    OutputStream os = sslSocket.getOutputStream();
+                    InputStream is = sslSocket.getInputStream();
+
+                    // write(byte[]) goes in one shot.
+                    os.write(clientMsg);
+
+                    byte[] inbound = new byte[2048];
+                    int pos = 0;
+
+                    int len;
+done:
+                    while ((len = is.read(inbound, pos, 2048 - pos)) != -1) {
+                        pos += len;
+                        // Let the client do the closing.
+                        if ((pos == serverMsg.length) && !serverClose) {
+                            sslSocket.close();
+                            break done;
+                        }
+                    }
+
+                    if (pos != serverMsg.length) {
+                        throw new Exception("Client:  Data length error");
+                    }
+
+                    for (int i = 0; i < serverMsg.length; i++) {
+                        if (inbound[i] != serverMsg[i]) {
+                            throw new Exception("Client:  Data content error");
+                        }
+                    }
+                } catch (Exception e) {
+                    clientException = e;
+                }
+            }
+        };
+        t.start();
+        return t;
+    }
+
+    /*
+     * Using the SSLContext created during object creation,
+     * create/configure the SSLEngines we'll use for this test.
+     */
+    private void createSSLEngine() throws Exception {
+        /*
+         * Configure the serverEngine to act as a server in the SSL/TLS
+         * handshake.
+         */
+        serverEngine = sslc.createSSLEngine();
+        serverEngine.setUseClientMode(false);
+        serverEngine.getNeedClientAuth();
+    }
+
+    /*
+     * Create and size the buffers appropriately.
+     */
+    private void createBuffers(boolean direct) {
+
+        SSLSession session = serverEngine.getSession();
+        int appBufferMax = session.getApplicationBufferSize();
+        int netBufferMax = session.getPacketBufferSize();
+
+        /*
+         * We'll make the input buffers a bit bigger than the max needed
+         * size, so that unwrap()s following a successful data transfer
+         * won't generate BUFFER_OVERFLOWS.
+         *
+         * We'll use a mix of direct and indirect ByteBuffers for
+         * tutorial purposes only.  In reality, only use direct
+         * ByteBuffers when they give a clear performance enhancement.
+         */
+        if (direct) {
+            serverIn = ByteBuffer.allocateDirect(appBufferMax + 50);
+            cTOs = ByteBuffer.allocateDirect(netBufferMax);
+            sTOc = ByteBuffer.allocateDirect(netBufferMax);
+        } else {
+            serverIn = ByteBuffer.allocate(appBufferMax + 50);
+            cTOs = ByteBuffer.allocate(netBufferMax);
+            sTOc = ByteBuffer.allocate(netBufferMax);
+        }
+
+        serverOut = ByteBuffer.wrap(serverMsg);
+    }
+
+    /*
+     * If the result indicates that we have outstanding tasks to do,
+     * go ahead and run them in this thread.
+     */
+    private static void runDelegatedTasks(SSLEngineResult result,
+            SSLEngine engine) throws Exception {
+
+        if (result.getHandshakeStatus() == HandshakeStatus.NEED_TASK) {
+            Runnable runnable;
+            while ((runnable = engine.getDelegatedTask()) != null) {
+                log("\trunning delegated task...");
+                runnable.run();
+            }
+            HandshakeStatus hsStatus = engine.getHandshakeStatus();
+            if (hsStatus == HandshakeStatus.NEED_TASK) {
+                throw new Exception(
+                        "handshake shouldn't need additional tasks");
+            }
+            log("\tnew HandshakeStatus: " + hsStatus);
+        }
+    }
+
+    private static boolean isEngineClosed(SSLEngine engine) {
+        return (engine.isOutboundDone() && engine.isInboundDone());
+    }
+
+    /*
+     * Logging code
+     */
+    private static boolean resultOnce = true;
+
+    private static void log(String str, SSLEngineResult result) {
+        if (!logging) {
+            return;
+        }
+        if (resultOnce) {
+            resultOnce = false;
+            System.out.println("The format of the SSLEngineResult is: \n"
+                    + "\t\"getStatus() / getHandshakeStatus()\" +\n"
+                    + "\t\"bytesConsumed() / bytesProduced()\"\n");
+        }
+        HandshakeStatus hsStatus = result.getHandshakeStatus();
+        log(str
+                + result.getStatus() + "/" + hsStatus + ", "
+                + result.bytesConsumed() + "/" + result.bytesProduced()
+                + " bytes");
+        if (hsStatus == HandshakeStatus.FINISHED) {
+            log("\t...ready for application data");
+        }
+    }
+
+    private static void log(String str) {
+        if (logging) {
+            System.out.println(str);
+        }
+    }
+}