ONOS-3521: SM-ONOS performance improvement

Change-Id: I8643187f2ceb35f8e0701d9e7ddb10098f05b244
diff --git a/core/api/src/main/java/org/onosproject/security/AppGuard.java b/core/api/src/main/java/org/onosproject/security/AppGuard.java
index 4b80dfc..788fbcd 100644
--- a/core/api/src/main/java/org/onosproject/security/AppGuard.java
+++ b/core/api/src/main/java/org/onosproject/security/AppGuard.java
@@ -16,26 +16,102 @@
 
 package org.onosproject.security;
 
+import java.security.AccessController;
+import java.security.AccessControlContext;
+import java.security.PrivilegedAction;
+import java.security.ProtectionDomain;
 
 import com.google.common.annotations.Beta;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
+
+import java.lang.reflect.Field;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.TimeUnit;
 
 /**
  * Aids SM-ONOS to perform API-level permission checking.
  */
 @Beta
 public final class AppGuard {
-
     private AppGuard() {
     }
 
     /**
      * Checks if the caller has the required permission only when security-mode is enabled.
+     *
      * @param permission permission to be checked
      */
     public static void checkPermission(AppPermission.Type permission) {
+
         SecurityManager sm = System.getSecurityManager();
-        if (sm != null) {
-            System.getSecurityManager().checkPermission(new AppPermission(permission));
+        if (sm == null) {
+            return;
+        }
+
+        Object result = AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
+            int contextHash = 0;
+            AccessControlContext context = AccessController.getContext();
+            Field f = null;
+            try {
+                f = context.getClass().getDeclaredField("context");
+
+                f.setAccessible(true);
+                ProtectionDomain[] domain = (ProtectionDomain[]) f.get(context);
+                for (ProtectionDomain pd : domain) {
+                    if (pd.getCodeSource() != null) {
+                        contextHash = contextHash ^ pd.getCodeSource().getLocation().hashCode();
+                    } else {
+                        return null;
+                    }
+                }
+                return contextHash;
+            } catch (NoSuchFieldException e) {
+                return null;
+            } catch (IllegalAccessException e) {
+                return null;
+            }
+        });
+
+        if (result == null) {
+            sm.checkPermission(new AppPermission(permission));
+        } else {
+            AppPermission perm = new AppPermission(permission);
+            int hash = ((int) result) ^ perm.hashCode();
+            PermissionCheckCache.getInstance().checkCache(hash, perm);
         }
     }
+
+
+    private static final class PermissionCheckCache {
+
+        private static final Cache<Integer, Boolean> CACHE = CacheBuilder.newBuilder()
+                .maximumSize(1000)
+                .expireAfterAccess(10, TimeUnit.MINUTES)
+                .build();
+
+        private PermissionCheckCache() {
+        }
+
+        private static class SingletonHelper {
+            private static final PermissionCheckCache INSTANCE = new PermissionCheckCache();
+        }
+
+        public static PermissionCheckCache getInstance() {
+            return SingletonHelper.INSTANCE;
+        }
+
+        public static void checkCache(int key, AppPermission perm) {
+            try {
+                CACHE.get(key, () -> {
+                    System.getSecurityManager().checkPermission(perm);
+                    return true;
+                });
+            } catch (ExecutionException e) {
+                System.getSecurityManager().checkPermission(perm);
+            }
+        }
+    }
+
 }
+