8029755: Enhance subject class
authorjbachorik
Fri, 07 Mar 2014 10:15:36 +0100
changeset 25534 27caad43f846
parent 25533 95f7f4b0bb93
child 25535 70b793c8273e
8029755: Enhance subject class Reviewed-by: sla, dfuchs, hawtin
jdk/src/share/classes/com/sun/jmx/remote/security/SubjectDelegator.java
jdk/src/share/classes/com/sun/jmx/remote/util/CacheMap.java
jdk/test/javax/management/remote/mandatory/util/CacheMapTest.java
--- a/jdk/src/share/classes/com/sun/jmx/remote/security/SubjectDelegator.java	Mon Mar 03 14:14:10 2014 -0800
+++ b/jdk/src/share/classes/com/sun/jmx/remote/security/SubjectDelegator.java	Fri Mar 07 10:15:36 2014 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2006, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2014, 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
@@ -34,22 +34,14 @@
 
 import javax.management.remote.SubjectDelegationPermission;
 
-import com.sun.jmx.remote.util.CacheMap;
-import java.util.ArrayList;
-import java.util.Collection;
+import java.util.*;
 
 public class SubjectDelegator {
-    private static final int PRINCIPALS_CACHE_SIZE = 10;
-    private static final int ACC_CACHE_SIZE = 10;
-
-    private CacheMap<Subject, Principal[]> principalsCache;
-    private CacheMap<Subject, AccessControlContext> accCache;
-
     /* Return the AccessControlContext appropriate to execute an
        operation on behalf of the delegatedSubject.  If the
        authenticatedAccessControlContext does not have permission to
        delegate to that subject, throw SecurityException.  */
-    public synchronized AccessControlContext
+    public AccessControlContext
         delegatedContext(AccessControlContext authenticatedACC,
                          Subject delegatedSubject,
                          boolean removeCallerContext)
@@ -58,56 +50,14 @@
         if (System.getSecurityManager() != null && authenticatedACC == null) {
             throw new SecurityException("Illegal AccessControlContext: null");
         }
-        if (principalsCache == null || accCache == null) {
-            principalsCache =
-                    new CacheMap<>(PRINCIPALS_CACHE_SIZE);
-            accCache =
-                    new CacheMap<>(ACC_CACHE_SIZE);
-        }
-
-        // Retrieve the principals for the given
-        // delegated subject from the cache
-        //
-        Principal[] delegatedPrincipals = principalsCache.get(delegatedSubject);
-
-        // Convert the set of principals stored in the
-        // delegated subject into an array of principals
-        // and store it in the cache
-        //
-        if (delegatedPrincipals == null) {
-            delegatedPrincipals =
-                delegatedSubject.getPrincipals().toArray(new Principal[0]);
-            principalsCache.put(delegatedSubject, delegatedPrincipals);
-        }
-
-        // Retrieve the access control context for the
-        // given delegated subject from the cache
-        //
-        AccessControlContext delegatedACC = accCache.get(delegatedSubject);
-
-        // Build the access control context to be used
-        // when executing code as the delegated subject
-        // and store it in the cache
-        //
-        if (delegatedACC == null) {
-            if (removeCallerContext) {
-                delegatedACC =
-                    JMXSubjectDomainCombiner.getDomainCombinerContext(
-                                                              delegatedSubject);
-            } else {
-                delegatedACC =
-                    JMXSubjectDomainCombiner.getContext(delegatedSubject);
-            }
-            accCache.put(delegatedSubject, delegatedACC);
-        }
 
         // Check if the subject delegation permission allows the
         // authenticated subject to assume the identity of each
         // principal in the delegated subject
         //
-        final Principal[] dp = delegatedPrincipals;
-        final Collection<Permission> permissions = new ArrayList<>(dp.length);
-        for(Principal p : dp) {
+        Collection<Principal> ps = getSubjectPrincipals(delegatedSubject);
+        final Collection<Permission> permissions = new ArrayList<>(ps.size());
+        for(Principal p : ps) {
             final String pname = p.getClass().getName() + "." + p.getName();
             permissions.add(new SubjectDelegationPermission(pname));
         }
@@ -122,7 +72,15 @@
             };
         AccessController.doPrivileged(action, authenticatedACC);
 
-        return delegatedACC;
+        return getDelegatedAcc(delegatedSubject, removeCallerContext);
+    }
+
+    private AccessControlContext getDelegatedAcc(Subject delegatedSubject, boolean removeCallerContext) {
+        if (removeCallerContext) {
+            return JMXSubjectDomainCombiner.getDomainCombinerContext(delegatedSubject);
+        } else {
+            return JMXSubjectDomainCombiner.getContext(delegatedSubject);
+        }
     }
 
     /**
@@ -137,11 +95,9 @@
     public static synchronized boolean
         checkRemoveCallerContext(Subject subject) {
         try {
-            final Principal[] dp =
-                subject.getPrincipals().toArray(new Principal[0]);
-            for (int i = 0 ; i < dp.length ; i++) {
+            for (Principal p : getSubjectPrincipals(subject)) {
                 final String pname =
-                    dp[i].getClass().getName() + "." + dp[i].getName();
+                    p.getClass().getName() + "." + p.getName();
                 final Permission sdp =
                     new SubjectDelegationPermission(pname);
                 AccessController.checkPermission(sdp);
@@ -151,4 +107,19 @@
         }
         return true;
     }
+
+    /**
+     * Retrieves the {@linkplain Subject} principals
+     * @param subject The subject
+     * @return If the {@code Subject} is immutable it will return the principals directly.
+     *         If the {@code Subject} is mutable it will create an unmodifiable copy.
+     */
+    private static Collection<Principal> getSubjectPrincipals(Subject subject) {
+        if (subject.isReadOnly()) {
+            return subject.getPrincipals();
+        }
+
+        List<Principal> principals = Arrays.asList(subject.getPrincipals().toArray(new Principal[0]));
+        return Collections.unmodifiableList(principals);
+    }
 }
--- a/jdk/src/share/classes/com/sun/jmx/remote/util/CacheMap.java	Mon Mar 03 14:14:10 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,121 +0,0 @@
-/*
- * Copyright (c) 2003, 2006, 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.  Oracle designates this
- * particular file as subject to the "Classpath" exception as provided
- * by Oracle in the LICENSE file that accompanied this code.
- *
- * 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.
- */
-
-package com.sun.jmx.remote.util;
-
-import java.lang.ref.SoftReference;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.WeakHashMap;
-
-import com.sun.jmx.mbeanserver.Util;
-
-/**
- * <p>Like WeakHashMap, except that the keys of the <em>n</em> most
- * recently-accessed entries are kept as {@link SoftReference soft
- * references}.  Accessing an element means creating it, or retrieving
- * it with {@link #get(Object) get}.  Because these entries are kept
- * with soft references, they will tend to remain even if their keys
- * are not referenced elsewhere.  But if memory is short, they will
- * be removed.</p>
- */
-public class CacheMap<K, V> extends WeakHashMap<K, V> {
-    /**
-     * <p>Create a <code>CacheMap</code> that can keep up to
-     * <code>nSoftReferences</code> as soft references.</p>
-     *
-     * @param nSoftReferences Maximum number of keys to keep as soft
-     * references.  Access times for {@link #get(Object) get} and
-     * {@link #put(Object, Object) put} have a component that scales
-     * linearly with <code>nSoftReferences</code>, so this value
-     * should not be too great.
-     *
-     * @throws IllegalArgumentException if
-     * <code>nSoftReferences</code> is negative.
-     */
-    public CacheMap(int nSoftReferences) {
-        if (nSoftReferences < 0) {
-            throw new IllegalArgumentException("nSoftReferences = " +
-                                               nSoftReferences);
-        }
-        this.nSoftReferences = nSoftReferences;
-    }
-
-    public V put(K key, V value) {
-        cache(key);
-        return super.put(key, value);
-    }
-
-    public V get(Object key) {
-        cache(Util.<K>cast(key));
-        return super.get(key);
-    }
-
-    /* We don't override remove(Object) or try to do something with
-       the map's iterators to detect removal.  So we may keep useless
-       entries in the soft reference list for keys that have since
-       been removed.  The assumption is that entries are added to the
-       cache but never removed.  But the behavior is not wrong if
-       they are in fact removed -- the caching is just less
-       performant.  */
-
-    private void cache(K key) {
-        Iterator<SoftReference<K>> it = cache.iterator();
-        while (it.hasNext()) {
-            SoftReference<K> sref = it.next();
-            K key1 = sref.get();
-            if (key1 == null)
-                it.remove();
-            else if (key.equals(key1)) {
-                // Move this element to the head of the LRU list
-                it.remove();
-                cache.add(0, sref);
-                return;
-            }
-        }
-
-        int size = cache.size();
-        if (size == nSoftReferences) {
-            if (size == 0)
-                return;  // degenerate case, equivalent to WeakHashMap
-            it.remove();
-        }
-
-        cache.add(0, new SoftReference<K>(key));
-    }
-
-    /* List of soft references for the most-recently referenced keys.
-       The list is in most-recently-used order, i.e. the first element
-       is the most-recently referenced key.  There are never more than
-       nSoftReferences elements of this list.
-
-       If we didn't care about J2SE 1.3 compatibility, we could use
-       LinkedHashSet in conjunction with a subclass of SoftReference
-       whose equals and hashCode reflect the referent.  */
-    private final LinkedList<SoftReference<K>> cache =
-            new LinkedList<SoftReference<K>>();
-    private final int nSoftReferences;
-}
--- a/jdk/test/javax/management/remote/mandatory/util/CacheMapTest.java	Mon Mar 03 14:14:10 2014 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,110 +0,0 @@
-/*
- * Copyright (c) 2003, 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 7654321
- * @summary Tests the CacheMap class.
- * @author Eamonn McManus
- * @run clean CacheMapTest
- * @run build CacheMapTest
- * @run main CacheMapTest
- */
-
-import java.util.Iterator;
-import java.util.Map;
-
-import com.sun.jmx.remote.util.CacheMap;
-
-public class CacheMapTest {
-    public static void main(String[] args) {
-        try {
-            boolean ok = test(5) && test(100);
-            if (ok) {
-                System.out.println("Test completed");
-                return;
-            } else {
-                System.out.println("Test failed!");
-                System.exit(1);
-            }
-        } catch (Exception e) {
-            System.err.println("Unexpected exception: " + e);
-            e.printStackTrace();
-            System.exit(1);
-        }
-    }
-
-    private static boolean test(int cacheSize) throws Exception {
-        System.out.println("CacheMap test with cache size " + cacheSize);
-        CacheMap map = new CacheMap(cacheSize);
-        int size = 0;
-        int maxIterations = cacheSize * 10;
-        while (map.size() == size && size < maxIterations) {
-            Integer key = new Integer(size);
-            Object x = map.put(key, "x");
-            if (x != null) {
-                System.out.println("Map already had entry " + key + "!");
-                return false;
-            }
-            x = map.get(key);
-            if (!"x".equals(x)) {
-                System.out.println("Got back surprising value: " + x);
-                return false;
-            }
-            size++;
-        }
-        System.out.println("Map size is " + map.size() + " after inserting " +
-                           size + " elements");
-        do {
-            System.gc();
-            Thread.sleep(1);
-            System.out.println("Map size is " + map.size() + " after GC");
-        } while (map.size() > cacheSize);
-        if (map.size() < cacheSize) {
-            System.out.println("Map shrank to less than cache size: " +
-                               map.size() + " (surprising but not wrong)");
-        } else
-            System.out.println("Map shrank to cache size as expected");
-        int lowest = size - cacheSize;
-        // lowest value that can still be in cache if LRU is respected
-        for (Iterator it = map.entrySet().iterator(); it.hasNext(); ) {
-            Map.Entry entry = (Map.Entry) it.next();
-            Integer x = (Integer) entry.getKey();
-            int xx = x.intValue();
-            if (xx < lowest || xx >= size) {
-                System.out.println("Old value remained (" + x + "), " +
-                                   "expected none earlier than " + lowest);
-                return false;
-            }
-            Object xxx = entry.getValue();
-            if (!"x".equals(xxx)) {
-                System.out.println("Got back surprising value: " + xxx);
-                return false;
-            }
-        }
-        if (map.size() > 0)
-            System.out.println("Remaining elements are the most recent ones");
-        System.out.println("Test passed");
-        return true;
-    }
-}