Repository: cassandra
Updated Branches:
  refs/heads/cassandra-3.11 d2248f206 -> bd0804065
  refs/heads/trunk 39bcdcd32 -> 2ad06d65b


Allow logging implementation to be interchanged for embedded testing

patch by Eric Hubert; reviewed by jasobrown for CASSANDRA-13396


Project: http://git-wip-us.apache.org/repos/asf/cassandra/repo
Commit: http://git-wip-us.apache.org/repos/asf/cassandra/commit/bd080406
Tree: http://git-wip-us.apache.org/repos/asf/cassandra/tree/bd080406
Diff: http://git-wip-us.apache.org/repos/asf/cassandra/diff/bd080406

Branch: refs/heads/cassandra-3.11
Commit: bd0804065daaa01ba478c0ed97f7411f1180eef9
Parents: d2248f2
Author: Eric Hubert <eric.hub...@optivo.com>
Authored: Fri Mar 23 18:24:17 2018 -0700
Committer: Jason Brown <jasedbr...@gmail.com>
Committed: Mon Mar 26 04:08:54 2018 -0700

----------------------------------------------------------------------
 CHANGES.txt                                     |   1 +
 .../cql3/functions/JavaBasedUDFunction.java     |   3 +
 .../cql3/functions/ScriptBasedUDFunction.java   |   2 +
 .../cql3/functions/SecurityThreadGroup.java     |  53 ----
 .../functions/ThreadAwareSecurityManager.java   | 270 -------------------
 .../cassandra/security/SecurityThreadGroup.java |  53 ++++
 .../security/ThreadAwareSecurityManager.java    | 214 +++++++++++++++
 .../cassandra/service/CassandraDaemon.java      |   2 +-
 .../cassandra/service/StorageService.java       |  51 +---
 .../utils/logging/LogbackLoggingSupport.java    | 146 ++++++++++
 .../cassandra/utils/logging/LoggingSupport.java |  34 +++
 .../utils/logging/LoggingSupportFactory.java    |  42 +++
 .../logging/NoOpFallbackLoggingSupport.java     |  30 +++
 .../org/apache/cassandra/cql3/CQLTester.java    |   2 +-
 14 files changed, 532 insertions(+), 371 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/CHANGES.txt
----------------------------------------------------------------------
diff --git a/CHANGES.txt b/CHANGES.txt
index 987b9f7..5646081 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,4 +1,5 @@
 3.11.3
+ * Allow logging implementation to be interchanged for embedded testing 
(CASSANDRA-13396)
  * SASI tokenizer for simple delimiter based entries (CASSANDRA-14247)
  * Fix Loss of digits when doing CAST from varint/bigint to decimal 
(CASSANDRA-14170)
  * RateBasedBackPressure unnecessarily invokes a lock on the Guava RateLimiter 
(CASSANDRA-14163)

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java 
b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
index 8f12899..feb17e3 100644
--- a/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/JavaBasedUDFunction.java
@@ -40,6 +40,7 @@ import java.util.regex.Pattern;
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.io.ByteStreams;
 import com.google.common.reflect.TypeToken;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -50,6 +51,8 @@ import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.exceptions.InvalidRequestException;
 import org.apache.cassandra.transport.ProtocolVersion;
 import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.security.SecurityThreadGroup;
+import org.apache.cassandra.security.ThreadAwareSecurityManager;
 import org.eclipse.jdt.core.compiler.IProblem;
 import org.eclipse.jdt.internal.compiler.*;
 import org.eclipse.jdt.internal.compiler.Compiler;

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java 
b/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
index c568972..41035a4 100644
--- a/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
+++ b/src/java/org/apache/cassandra/cql3/functions/ScriptBasedUDFunction.java
@@ -35,6 +35,8 @@ import org.apache.cassandra.concurrent.NamedThreadFactory;
 import org.apache.cassandra.cql3.ColumnIdentifier;
 import org.apache.cassandra.db.marshal.AbstractType;
 import org.apache.cassandra.exceptions.InvalidRequestException;
+import org.apache.cassandra.security.SecurityThreadGroup;
+import org.apache.cassandra.security.ThreadAwareSecurityManager;
 import org.apache.cassandra.transport.ProtocolVersion;
 
 final class ScriptBasedUDFunction extends UDFunction

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/cql3/functions/SecurityThreadGroup.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/SecurityThreadGroup.java 
b/src/java/org/apache/cassandra/cql3/functions/SecurityThreadGroup.java
deleted file mode 100644
index 8f50dc8..0000000
--- a/src/java/org/apache/cassandra/cql3/functions/SecurityThreadGroup.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.cassandra.cql3.functions;
-
-import java.util.Set;
-
-/**
- * Used by {@link ThreadAwareSecurityManager} to determine whether 
access-control checks needs to be performed.
- */
-public final class SecurityThreadGroup extends ThreadGroup
-{
-    private final Set<String> allowedPackages;
-    private final ThreadInitializer threadInitializer;
-
-    public SecurityThreadGroup(String name, Set<String> allowedPackages, 
ThreadInitializer threadInitializer)
-    {
-        super(name);
-        this.allowedPackages = allowedPackages;
-        this.threadInitializer = threadInitializer;
-    }
-
-    public void initializeThread()
-    {
-        threadInitializer.initializeThread();
-    }
-
-    public boolean isPackageAllowed(String pkg)
-    {
-        return allowedPackages == null || allowedPackages.contains(pkg);
-    }
-
-    @FunctionalInterface
-    interface ThreadInitializer
-    {
-        void initializeThread();
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java 
b/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java
deleted file mode 100644
index 9c5b95b..0000000
--- 
a/src/java/org/apache/cassandra/cql3/functions/ThreadAwareSecurityManager.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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.cassandra.cql3.functions;
-
-import java.security.AccessControlException;
-import java.security.AllPermission;
-import java.security.CodeSource;
-import java.security.Permission;
-import java.security.PermissionCollection;
-import java.security.Permissions;
-import java.security.Policy;
-import java.security.ProtectionDomain;
-import java.util.Collections;
-import java.util.Enumeration;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.spi.TurboFilterList;
-import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter;
-import ch.qos.logback.classic.turbo.TurboFilter;
-import io.netty.util.concurrent.FastThreadLocal;
-
-/**
- * Custom {@link SecurityManager} and {@link Policy} implementation that only 
performs access checks
- * if explicitly enabled.
- * <p>
- * This implementation gives no measurable performance panalty
- * (see <a 
href="http://cstar.datastax.com/tests/id/1d461628-12ba-11e5-918f-42010af0688f";>see
 cstar test</a>).
- * This is better than the penalty of 1 to 3 percent using a standard {@code 
SecurityManager} with an <i>allow all</i> policy.
- * </p>
- */
-public final class ThreadAwareSecurityManager extends SecurityManager
-{
-    static final PermissionCollection noPermissions = new 
PermissionCollection()
-    {
-        public void add(Permission permission)
-        {
-            throw new UnsupportedOperationException();
-        }
-
-        public boolean implies(Permission permission)
-        {
-            return false;
-        }
-
-        public Enumeration<Permission> elements()
-        {
-            return Collections.emptyEnumeration();
-        }
-    };
-
-    private static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = 
new RuntimePermission("accessDeclaredMembers");
-    private static final RuntimePermission MODIFY_THREAD_PERMISSION = new 
RuntimePermission("modifyThread");
-    private static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = new 
RuntimePermission("modifyThreadGroup");
-
-    private static volatile boolean installed;
-
-    public static void install()
-    {
-        if (installed)
-            return;
-        System.setSecurityManager(new ThreadAwareSecurityManager());
-
-        // The default logback configuration in conf/logback.xml allows 
reloading the
-        // configuration when the configuration file has changed (every 60 
seconds by default).
-        // This requires logback to use file I/O APIs. But file I/O is not 
allowed from UDFs.
-        // I.e. if logback decides to check for a modification of the config 
file while
-        // executiing a sandbox thread, the UDF execution and therefore the 
whole request
-        // execution will fail with an AccessControlException.
-        // To work around this, a custom ReconfigureOnChangeFilter is 
installed, that simply
-        // prevents this configuration file check and possible reload of the 
configration,
-        // while executing sandboxed UDF code.
-        Logger l = LoggerFactory.getLogger(ThreadAwareSecurityManager.class);
-        ch.qos.logback.classic.Logger logbackLogger = 
(ch.qos.logback.classic.Logger) l;
-        LoggerContext ctx = logbackLogger.getLoggerContext();
-
-        TurboFilterList turboFilterList = ctx.getTurboFilterList();
-        for (int i = 0; i < turboFilterList.size(); i++)
-        {
-            TurboFilter turboFilter = turboFilterList.get(i);
-            if (turboFilter instanceof ReconfigureOnChangeFilter)
-            {
-                ReconfigureOnChangeFilter reconfigureOnChangeFilter = 
(ReconfigureOnChangeFilter) turboFilter;
-                turboFilterList.set(i, new 
SMAwareReconfigureOnChangeFilter(reconfigureOnChangeFilter));
-                break;
-            }
-        }
-
-        installed = true;
-    }
-
-    /**
-     * The purpose of this class is to prevent logback from checking for 
config file change,
-     * if the current thread is executing a sandboxed thread to avoid {@link 
AccessControlException}s.
-     */
-    private static class SMAwareReconfigureOnChangeFilter extends 
ReconfigureOnChangeFilter
-    {
-        SMAwareReconfigureOnChangeFilter(ReconfigureOnChangeFilter 
reconfigureOnChangeFilter)
-        {
-            setRefreshPeriod(reconfigureOnChangeFilter.getRefreshPeriod());
-            setName(reconfigureOnChangeFilter.getName());
-            setContext(reconfigureOnChangeFilter.getContext());
-            if (reconfigureOnChangeFilter.isStarted())
-            {
-                reconfigureOnChangeFilter.stop();
-                start();
-            }
-        }
-
-        protected boolean changeDetected(long now)
-        {
-            if (isSecuredThread())
-                return false;
-            return super.changeDetected(now);
-        }
-    }
-
-    static
-    {
-        //
-        // Use own security policy to be easier (and faster) since the C* has 
no fine grained permissions.
-        // Either code has access to everything or code has access to nothing 
(UDFs).
-        // This also removes the burden to maintain and configure policy files 
for production, unit tests etc.
-        //
-        // Note: a permission is only granted, if there is no objector. This 
means that
-        // AccessController/AccessControlContext collect all applicable 
ProtectionDomains - only if none of these
-        // applicable ProtectionDomains denies access, the permission is 
granted.
-        // A ProtectionDomain can have its origin at an oridinary code-source 
or provided via a
-        // AccessController.doPrivileded() call.
-        //
-        Policy.setPolicy(new Policy()
-        {
-            public PermissionCollection getPermissions(CodeSource codesource)
-            {
-                // contract of getPermissions() methods is to return a 
_mutable_ PermissionCollection
-
-                Permissions perms = new Permissions();
-
-                if (codesource == null || codesource.getLocation() == null)
-                    return perms;
-
-                switch (codesource.getLocation().getProtocol())
-                {
-                    case "file":
-                        // All JARs and class files reside on the file system 
- we can safely
-                        // assume that these classes are "good".
-                        perms.add(new AllPermission());
-                        return perms;
-                }
-
-                return perms;
-            }
-
-            public PermissionCollection getPermissions(ProtectionDomain domain)
-            {
-                return getPermissions(domain.getCodeSource());
-            }
-
-            public boolean implies(ProtectionDomain domain, Permission 
permission)
-            {
-                CodeSource codesource = domain.getCodeSource();
-                if (codesource == null || codesource.getLocation() == null)
-                    return false;
-
-                switch (codesource.getLocation().getProtocol())
-                {
-                    case "file":
-                        // All JARs and class files reside on the file system 
- we can safely
-                        // assume that these classes are "good".
-                        return true;
-                }
-
-                return false;
-            }
-        });
-    }
-
-    private static final FastThreadLocal<Boolean> initializedThread = new 
FastThreadLocal<>();
-
-    private ThreadAwareSecurityManager()
-    {
-    }
-
-    private static boolean isSecuredThread()
-    {
-        ThreadGroup tg = Thread.currentThread().getThreadGroup();
-        if (!(tg instanceof SecurityThreadGroup))
-            return false;
-        Boolean threadInitialized = initializedThread.get();
-        if (threadInitialized == null)
-        {
-            initializedThread.set(false);
-            ((SecurityThreadGroup) tg).initializeThread();
-            initializedThread.set(true);
-            threadInitialized = true;
-        }
-        return threadInitialized;
-    }
-
-    public void checkAccess(Thread t)
-    {
-        // need to override since the default implementation only checks the 
permission if the current thread's
-        // in the root-thread-group
-
-        if (isSecuredThread())
-            throw new AccessControlException("access denied: " + 
MODIFY_THREAD_PERMISSION, MODIFY_THREAD_PERMISSION);
-        super.checkAccess(t);
-    }
-
-    public void checkAccess(ThreadGroup g)
-    {
-        // need to override since the default implementation only checks the 
permission if the current thread's
-        // in the root-thread-group
-
-        if (isSecuredThread())
-            throw new AccessControlException("access denied: " + 
MODIFY_THREADGROUP_PERMISSION, MODIFY_THREADGROUP_PERMISSION);
-        super.checkAccess(g);
-    }
-
-    public void checkPermission(Permission perm)
-    {
-        if (!isSecuredThread())
-            return;
-
-        // required by JavaDriver 2.2.0-rc3 and 3.0.0-a2 or newer
-        // code in com.datastax.driver.core.CodecUtils uses Guava stuff, which 
in turns requires this permission
-        if (CHECK_MEMBER_ACCESS_PERMISSION.equals(perm))
-            return;
-
-        super.checkPermission(perm);
-    }
-
-    public void checkPermission(Permission perm, Object context)
-    {
-        if (isSecuredThread())
-            super.checkPermission(perm, context);
-    }
-
-    public void checkPackageAccess(String pkg)
-    {
-        if (!isSecuredThread())
-            return;
-
-        if (!((SecurityThreadGroup) 
Thread.currentThread().getThreadGroup()).isPackageAllowed(pkg))
-        {
-            RuntimePermission perm = new 
RuntimePermission("accessClassInPackage." + pkg);
-            throw new AccessControlException("access denied: " + perm, perm);
-        }
-
-        super.checkPackageAccess(pkg);
-    }
-}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/security/SecurityThreadGroup.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/security/SecurityThreadGroup.java 
b/src/java/org/apache/cassandra/security/SecurityThreadGroup.java
new file mode 100644
index 0000000..c57a7b7
--- /dev/null
+++ b/src/java/org/apache/cassandra/security/SecurityThreadGroup.java
@@ -0,0 +1,53 @@
+/*
+ * 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.cassandra.security;
+
+import java.util.Set;
+
+/**
+ * Used by {@link ThreadAwareSecurityManager} to determine whether 
access-control checks needs to be performed.
+ */
+public final class SecurityThreadGroup extends ThreadGroup
+{
+    private final Set<String> allowedPackages;
+    private final ThreadInitializer threadInitializer;
+
+    public SecurityThreadGroup(String name, Set<String> allowedPackages, 
ThreadInitializer threadInitializer)
+    {
+        super(name);
+        this.allowedPackages = allowedPackages;
+        this.threadInitializer = threadInitializer;
+    }
+
+    public void initializeThread()
+    {
+        threadInitializer.initializeThread();
+    }
+
+    public boolean isPackageAllowed(String pkg)
+    {
+        return allowedPackages == null || allowedPackages.contains(pkg);
+    }
+
+    @FunctionalInterface
+    public interface ThreadInitializer
+    {
+        void initializeThread();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java 
b/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
new file mode 100644
index 0000000..c9402f1
--- /dev/null
+++ b/src/java/org/apache/cassandra/security/ThreadAwareSecurityManager.java
@@ -0,0 +1,214 @@
+/*
+ * 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.cassandra.security;
+
+import java.security.AccessControlException;
+import java.security.AllPermission;
+import java.security.CodeSource;
+import java.security.Permission;
+import java.security.PermissionCollection;
+import java.security.Permissions;
+import java.security.Policy;
+import java.security.ProtectionDomain;
+import java.util.Collections;
+import java.util.Enumeration;
+
+import io.netty.util.concurrent.FastThreadLocal;
+
+import org.apache.cassandra.utils.logging.LoggingSupportFactory;
+
+/**
+ * Custom {@link SecurityManager} and {@link Policy} implementation that only 
performs access checks
+ * if explicitly enabled.
+ * <p>
+ * This implementation gives no measurable performance penalty
+ * (see <a 
href="http://cstar.datastax.com/tests/id/1d461628-12ba-11e5-918f-42010af0688f";>see
 cstar test</a>).
+ * This is better than the penalty of 1 to 3 percent using a standard {@code 
SecurityManager} with an <i>allow all</i> policy.
+ * </p>
+ */
+public final class ThreadAwareSecurityManager extends SecurityManager
+{
+    public static final PermissionCollection noPermissions = new 
PermissionCollection()
+    {
+        public void add(Permission permission)
+        {
+            throw new UnsupportedOperationException();
+        }
+
+        public boolean implies(Permission permission)
+        {
+            return false;
+        }
+
+        public Enumeration<Permission> elements()
+        {
+            return Collections.emptyEnumeration();
+        }
+    };
+
+    private static final RuntimePermission CHECK_MEMBER_ACCESS_PERMISSION = 
new RuntimePermission("accessDeclaredMembers");
+    private static final RuntimePermission MODIFY_THREAD_PERMISSION = new 
RuntimePermission("modifyThread");
+    private static final RuntimePermission MODIFY_THREADGROUP_PERMISSION = new 
RuntimePermission("modifyThreadGroup");
+
+    private static volatile boolean installed;
+
+    public static void install()
+    {
+        if (installed)
+            return;
+        System.setSecurityManager(new ThreadAwareSecurityManager());
+        LoggingSupportFactory.getLoggingSupport().onStartup();
+        installed = true;
+    }
+
+    static
+    {
+        //
+        // Use own security policy to be easier (and faster) since the C* has 
no fine grained permissions.
+        // Either code has access to everything or code has access to nothing 
(UDFs).
+        // This also removes the burden to maintain and configure policy files 
for production, unit tests etc.
+        //
+        // Note: a permission is only granted, if there is no objector. This 
means that
+        // AccessController/AccessControlContext collect all applicable 
ProtectionDomains - only if none of these
+        // applicable ProtectionDomains denies access, the permission is 
granted.
+        // A ProtectionDomain can have its origin at an oridinary code-source 
or provided via a
+        // AccessController.doPrivileded() call.
+        //
+        Policy.setPolicy(new Policy()
+        {
+            public PermissionCollection getPermissions(CodeSource codesource)
+            {
+                // contract of getPermissions() methods is to return a 
_mutable_ PermissionCollection
+
+                Permissions perms = new Permissions();
+
+                if (codesource == null || codesource.getLocation() == null)
+                    return perms;
+
+                switch (codesource.getLocation().getProtocol())
+                {
+                    case "file":
+                        // All JARs and class files reside on the file system 
- we can safely
+                        // assume that these classes are "good".
+                        perms.add(new AllPermission());
+                        return perms;
+                }
+
+                return perms;
+            }
+
+            public PermissionCollection getPermissions(ProtectionDomain domain)
+            {
+                return getPermissions(domain.getCodeSource());
+            }
+
+            public boolean implies(ProtectionDomain domain, Permission 
permission)
+            {
+                CodeSource codesource = domain.getCodeSource();
+                if (codesource == null || codesource.getLocation() == null)
+                    return false;
+
+                switch (codesource.getLocation().getProtocol())
+                {
+                    case "file":
+                        // All JARs and class files reside on the file system 
- we can safely
+                        // assume that these classes are "good".
+                        return true;
+                }
+
+                return false;
+            }
+        });
+    }
+
+    private static final FastThreadLocal<Boolean> initializedThread = new 
FastThreadLocal<>();
+
+    private ThreadAwareSecurityManager()
+    {
+    }
+
+    public static boolean isSecuredThread()
+    {
+        ThreadGroup tg = Thread.currentThread().getThreadGroup();
+        if (!(tg instanceof SecurityThreadGroup))
+            return false;
+        Boolean threadInitialized = initializedThread.get();
+        if (threadInitialized == null)
+        {
+            initializedThread.set(false);
+            ((SecurityThreadGroup) tg).initializeThread();
+            initializedThread.set(true);
+            threadInitialized = true;
+        }
+        return threadInitialized;
+    }
+
+    public void checkAccess(Thread t)
+    {
+        // need to override since the default implementation only checks the 
permission if the current thread's
+        // in the root-thread-group
+
+        if (isSecuredThread())
+            throw new AccessControlException("access denied: " + 
MODIFY_THREAD_PERMISSION, MODIFY_THREAD_PERMISSION);
+        super.checkAccess(t);
+    }
+
+    public void checkAccess(ThreadGroup g)
+    {
+        // need to override since the default implementation only checks the 
permission if the current thread's
+        // in the root-thread-group
+
+        if (isSecuredThread())
+            throw new AccessControlException("access denied: " + 
MODIFY_THREADGROUP_PERMISSION, MODIFY_THREADGROUP_PERMISSION);
+        super.checkAccess(g);
+    }
+
+    public void checkPermission(Permission perm)
+    {
+        if (!isSecuredThread())
+            return;
+
+        // required by JavaDriver 2.2.0-rc3 and 3.0.0-a2 or newer
+        // code in com.datastax.driver.core.CodecUtils uses Guava stuff, which 
in turns requires this permission
+        if (CHECK_MEMBER_ACCESS_PERMISSION.equals(perm))
+            return;
+
+        super.checkPermission(perm);
+    }
+
+    public void checkPermission(Permission perm, Object context)
+    {
+        if (isSecuredThread())
+            super.checkPermission(perm, context);
+    }
+
+    public void checkPackageAccess(String pkg)
+    {
+        if (!isSecuredThread())
+            return;
+
+        if (!((SecurityThreadGroup) 
Thread.currentThread().getThreadGroup()).isPackageAllowed(pkg))
+        {
+            RuntimePermission perm = new 
RuntimePermission("accessClassInPackage." + pkg);
+            throw new AccessControlException("access denied: " + perm, perm);
+        }
+
+        super.checkPackageAccess(pkg);
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/service/CassandraDaemon.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/CassandraDaemon.java 
b/src/java/org/apache/cassandra/service/CassandraDaemon.java
index 3dbf3d8..d9bd5c3 100644
--- a/src/java/org/apache/cassandra/service/CassandraDaemon.java
+++ b/src/java/org/apache/cassandra/service/CassandraDaemon.java
@@ -51,7 +51,6 @@ import org.apache.cassandra.config.CFMetaData;
 import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.config.SchemaConstants;
-import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager;
 import org.apache.cassandra.cql3.QueryProcessor;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.commitlog.CommitLog;
@@ -69,6 +68,7 @@ import org.apache.cassandra.schema.LegacySchemaMigrator;
 import org.apache.cassandra.thrift.ThriftServer;
 import org.apache.cassandra.tracing.Tracing;
 import org.apache.cassandra.utils.*;
+import org.apache.cassandra.security.ThreadAwareSecurityManager;
 
 /**
  * The <code>CassandraDaemon</code> is an abstraction for a Cassandra daemon

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/service/StorageService.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/service/StorageService.java 
b/src/java/org/apache/cassandra/service/StorageService.java
index b743bf3..373493c 100644
--- a/src/java/org/apache/cassandra/service/StorageService.java
+++ b/src/java/org/apache/cassandra/service/StorageService.java
@@ -46,11 +46,6 @@ import org.apache.commons.lang3.StringUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import ch.qos.logback.classic.LoggerContext;
-import ch.qos.logback.classic.jmx.JMXConfiguratorMBean;
-import ch.qos.logback.classic.spi.ILoggingEvent;
-import ch.qos.logback.core.Appender;
-import ch.qos.logback.core.hook.DelayingShutdownHook;
 import org.apache.cassandra.auth.AuthKeyspace;
 import org.apache.cassandra.auth.AuthMigrationListener;
 import org.apache.cassandra.batchlog.BatchRemoveVerbHandler;
@@ -96,6 +91,7 @@ import org.apache.cassandra.thrift.cassandraConstants;
 import org.apache.cassandra.tracing.TraceKeyspace;
 import org.apache.cassandra.transport.ProtocolVersion;
 import org.apache.cassandra.utils.*;
+import org.apache.cassandra.utils.logging.LoggingSupportFactory;
 import org.apache.cassandra.utils.progress.ProgressEvent;
 import org.apache.cassandra.utils.progress.ProgressEventType;
 import org.apache.cassandra.utils.progress.jmx.JMXProgressSupport;
@@ -645,10 +641,7 @@ public class StorageService extends 
NotificationBroadcasterSupport implements IE
                 if (FBUtilities.isWindows)
                     
WindowsTimer.endTimerPeriod(DatabaseDescriptor.getWindowsTimerInterval());
 
-                // Cleanup logback
-                DelayingShutdownHook logbackHook = new DelayingShutdownHook();
-                
logbackHook.setContext((LoggerContext)LoggerFactory.getILoggerFactory());
-                logbackHook.run();
+                LoggingSupportFactory.getLoggingSupport().onShutdown();
             }
         }, "StorageServiceShutdownHook");
         Runtime.getRuntime().addShutdownHook(drainOnShutdown);
@@ -3811,50 +3804,16 @@ public class StorageService extends 
NotificationBroadcasterSupport implements IE
 
     public void setLoggingLevel(String classQualifier, String rawLevel) throws 
Exception
     {
-        ch.qos.logback.classic.Logger logBackLogger = 
(ch.qos.logback.classic.Logger) LoggerFactory.getLogger(classQualifier);
-
-        // if both classQualifer and rawLevel are empty, reload from 
configuration
-        if (StringUtils.isBlank(classQualifier) && 
StringUtils.isBlank(rawLevel) )
-        {
-            JMXConfiguratorMBean jmxConfiguratorMBean = 
JMX.newMBeanProxy(ManagementFactory.getPlatformMBeanServer(),
-                    new 
ObjectName("ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator"),
-                    JMXConfiguratorMBean.class);
-            jmxConfiguratorMBean.reloadDefaultConfiguration();
-            return;
-        }
-        // classQualifer is set, but blank level given
-        else if (StringUtils.isNotBlank(classQualifier) && 
StringUtils.isBlank(rawLevel) )
-        {
-            if (logBackLogger.getLevel() != null || 
hasAppenders(logBackLogger))
-                logBackLogger.setLevel(null);
-            return;
-        }
-
-        ch.qos.logback.classic.Level level = 
ch.qos.logback.classic.Level.toLevel(rawLevel);
-        logBackLogger.setLevel(level);
-        logger.info("set log level to {} for classes under '{}' (if the level 
doesn't look like '{}' then the logger couldn't parse '{}')", level, 
classQualifier, rawLevel, rawLevel);
+        
LoggingSupportFactory.getLoggingSupport().setLoggingLevel(classQualifier, 
rawLevel);
     }
 
     /**
      * @return the runtime logging levels for all the configured loggers
      */
     @Override
-    public Map<String,String>getLoggingLevels()
-    {
-        Map<String, String> logLevelMaps = Maps.newLinkedHashMap();
-        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
-        for (ch.qos.logback.classic.Logger logger : lc.getLoggerList())
-        {
-            if(logger.getLevel() != null || hasAppenders(logger))
-                logLevelMaps.put(logger.getName(), 
logger.getLevel().toString());
-        }
-        return logLevelMaps;
-    }
-
-    private boolean hasAppenders(ch.qos.logback.classic.Logger logger)
+    public Map<String,String> getLoggingLevels()
     {
-        Iterator<Appender<ILoggingEvent>> it = logger.iteratorForAppenders();
-        return it.hasNext();
+        return LoggingSupportFactory.getLoggingSupport().getLoggingLevels();
     }
 
     /**

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/utils/logging/LogbackLoggingSupport.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/utils/logging/LogbackLoggingSupport.java 
b/src/java/org/apache/cassandra/utils/logging/LogbackLoggingSupport.java
new file mode 100644
index 0000000..3229460
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/logging/LogbackLoggingSupport.java
@@ -0,0 +1,146 @@
+package org.apache.cassandra.utils.logging;
+
+import java.lang.management.ManagementFactory;
+import java.security.AccessControlException;
+import java.util.Iterator;
+import java.util.Map;
+
+import javax.management.JMX;
+import javax.management.ObjectName;
+
+import org.apache.cassandra.security.ThreadAwareSecurityManager;
+import org.apache.commons.lang3.StringUtils;
+import org.slf4j.LoggerFactory;
+
+import com.google.common.collect.Maps;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.LoggerContext;
+import ch.qos.logback.classic.jmx.JMXConfiguratorMBean;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.classic.spi.TurboFilterList;
+import ch.qos.logback.classic.turbo.ReconfigureOnChangeFilter;
+import ch.qos.logback.classic.turbo.TurboFilter;
+import ch.qos.logback.core.Appender;
+import ch.qos.logback.core.hook.DelayingShutdownHook;
+
+/**
+ * Encapsulates all logback-specific implementations in a central place.
+ * Generally, the Cassandra code-base should be logging-backend agnostic and 
only use slf4j-api.
+ * This class MUST NOT be used directly, but only via {@link 
LoggingSupportFactory} which dynamically loads and
+ * instantiates an appropriate implementation according to the used slf4j 
binding.
+ */
+public class LogbackLoggingSupport implements LoggingSupport
+{
+
+    private static final org.slf4j.Logger logger = 
LoggerFactory.getLogger(LogbackLoggingSupport.class);
+
+    @Override
+    public void onStartup()
+    {
+        // The default logback configuration in conf/logback.xml allows 
reloading the
+        // configuration when the configuration file has changed (every 60 
seconds by default).
+        // This requires logback to use file I/O APIs. But file I/O is not 
allowed from UDFs.
+        // I.e. if logback decides to check for a modification of the config 
file while
+        // executing a sandbox thread, the UDF execution and therefore the 
whole request
+        // execution will fail with an AccessControlException.
+        // To work around this, a custom ReconfigureOnChangeFilter is 
installed, that simply
+        // prevents this configuration file check and possible reload of the 
configuration,
+        // while executing sandboxed UDF code.
+        Logger logbackLogger = (Logger) 
LoggerFactory.getLogger(ThreadAwareSecurityManager.class);
+        LoggerContext ctx = logbackLogger.getLoggerContext();
+
+        TurboFilterList turboFilterList = ctx.getTurboFilterList();
+        for (int i = 0; i < turboFilterList.size(); i++)
+        {
+            TurboFilter turboFilter = turboFilterList.get(i);
+            if (turboFilter instanceof ReconfigureOnChangeFilter)
+            {
+                ReconfigureOnChangeFilter reconfigureOnChangeFilter = 
(ReconfigureOnChangeFilter) turboFilter;
+                turboFilterList.set(i, new 
SMAwareReconfigureOnChangeFilter(reconfigureOnChangeFilter));
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void onShutdown()
+    {
+        DelayingShutdownHook logbackHook = new DelayingShutdownHook();
+        logbackHook.setContext((LoggerContext) 
LoggerFactory.getILoggerFactory());
+        logbackHook.run();
+    }
+
+    @Override
+    public void setLoggingLevel(String classQualifier, String rawLevel) throws 
Exception
+    {
+        Logger logBackLogger = (Logger) 
LoggerFactory.getLogger(classQualifier);
+
+        // if both classQualifier and rawLevel are empty, reload from 
configuration
+        if (StringUtils.isBlank(classQualifier) && 
StringUtils.isBlank(rawLevel))
+        {
+            JMXConfiguratorMBean jmxConfiguratorMBean = 
JMX.newMBeanProxy(ManagementFactory.getPlatformMBeanServer(),
+                                                                          new 
ObjectName("ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator"),
+                                                                          
JMXConfiguratorMBean.class);
+            jmxConfiguratorMBean.reloadDefaultConfiguration();
+            return;
+        }
+        // classQualifier is set, but blank level given
+        else if (StringUtils.isNotBlank(classQualifier) && 
StringUtils.isBlank(rawLevel))
+        {
+            if (logBackLogger.getLevel() != null || 
hasAppenders(logBackLogger))
+                logBackLogger.setLevel(null);
+            return;
+        }
+
+        Level level = Level.toLevel(rawLevel);
+        logBackLogger.setLevel(level);
+        logger.info("set log level to {} for classes under '{}' (if the level 
doesn't look like '{}' then the logger couldn't parse '{}')", level, 
classQualifier, rawLevel, rawLevel);
+    }
+
+    @Override
+    public Map<String, String> getLoggingLevels()
+    {
+        Map<String, String> logLevelMaps = Maps.newLinkedHashMap();
+        LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
+        for (Logger logBackLogger : lc.getLoggerList())
+        {
+            if (logBackLogger.getLevel() != null || 
hasAppenders(logBackLogger))
+                logLevelMaps.put(logBackLogger.getName(), 
logBackLogger.getLevel().toString());
+        }
+        return logLevelMaps;
+    }
+
+    private boolean hasAppenders(Logger logBackLogger)
+    {
+        Iterator<Appender<ILoggingEvent>> it = 
logBackLogger.iteratorForAppenders();
+        return it.hasNext();
+    }
+
+    /**
+     * The purpose of this class is to prevent logback from checking for 
config file change,
+     * if the current thread is executing a sandboxed thread to avoid {@link 
AccessControlException}s.
+     */
+    private static class SMAwareReconfigureOnChangeFilter extends 
ReconfigureOnChangeFilter
+    {
+        SMAwareReconfigureOnChangeFilter(ReconfigureOnChangeFilter 
reconfigureOnChangeFilter)
+        {
+            setRefreshPeriod(reconfigureOnChangeFilter.getRefreshPeriod());
+            setName(reconfigureOnChangeFilter.getName());
+            setContext(reconfigureOnChangeFilter.getContext());
+            if (reconfigureOnChangeFilter.isStarted())
+            {
+                reconfigureOnChangeFilter.stop();
+                start();
+            }
+        }
+
+        protected boolean changeDetected(long now)
+        {
+            if (ThreadAwareSecurityManager.isSecuredThread())
+                return false;
+            return super.changeDetected(now);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/utils/logging/LoggingSupport.java
----------------------------------------------------------------------
diff --git a/src/java/org/apache/cassandra/utils/logging/LoggingSupport.java 
b/src/java/org/apache/cassandra/utils/logging/LoggingSupport.java
new file mode 100644
index 0000000..3855ca7
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/logging/LoggingSupport.java
@@ -0,0 +1,34 @@
+package org.apache.cassandra.utils.logging;
+
+import java.util.Map;
+
+/**
+ * Common abstraction of functionality which can be implemented for different 
logging backend implementations (slf4j bindings).
+ * Concrete implementations are dynamically loaded and instantiated by {@link 
LoggingSupportFactory#getLoggingSupport()}.
+ */
+public interface LoggingSupport
+{
+    /**
+     * Hook used to execute logging implementation specific customization at 
Cassandra startup time.
+     */
+    default void onStartup() {}
+
+    /**
+     * Hook used to execute logging implementation specific customization at 
Cassandra shutdown time.
+     */
+    default void onShutdown() {}
+
+    /**
+     * Changes the given logger to the given log level.
+     *
+     * @param classQualifier the class qualifier or logger name
+     * @param rawLevel the string representation of a log level
+     * @throws Exception an exception which may occur while changing the given 
logger to the given log level.
+     */
+    void setLoggingLevel(String classQualifier, String rawLevel) throws 
Exception;
+
+    /**
+     * @return a map of logger names and their associated log level as string 
representations.
+     */
+    Map<String, String> getLoggingLevels();
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/utils/logging/LoggingSupportFactory.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/utils/logging/LoggingSupportFactory.java 
b/src/java/org/apache/cassandra/utils/logging/LoggingSupportFactory.java
new file mode 100644
index 0000000..3e7adab
--- /dev/null
+++ b/src/java/org/apache/cassandra/utils/logging/LoggingSupportFactory.java
@@ -0,0 +1,42 @@
+package org.apache.cassandra.utils.logging;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import org.apache.cassandra.utils.FBUtilities;
+
+/**
+ * Dynamically loads and instantiates an appropriate {@link LoggingSupport} 
implementation according to the used slf4j binding.
+ * For production use, this should always be {@link LogbackLoggingSupport}.
+ */
+public class LoggingSupportFactory
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(LoggingSupportFactory.class);
+
+    private static volatile LoggingSupport loggingSupport;
+
+    private LoggingSupportFactory() {}
+
+    /**
+     * @return An appropriate {@link LoggingSupport} implementation according 
to the used slf4j binding.
+     */
+    public static LoggingSupport getLoggingSupport()
+    {
+        if (loggingSupport == null)
+        {
+            // unfortunately, this is the best way to determine if logback is 
being used for logger
+            String loggerFactoryClass = 
LoggerFactory.getILoggerFactory().getClass().getName();
+            if (loggerFactoryClass.contains("logback"))
+            {
+                loggingSupport = 
FBUtilities.instanceOrConstruct("org.apache.cassandra.utils.logging.LogbackLoggingSupport",
 "LogbackLoggingSupport");
+            }
+            else
+            {
+                loggingSupport = new NoOpFallbackLoggingSupport();
+                logger.warn("You are using Cassandra with an unsupported 
deployment. The intended logging implementation library logback is not used by 
slf4j. Detected slf4j logger factory: {}. "
+                        + "You will not be able to dynamically manage log 
levels via JMX and may have performance or other issues.", loggerFactoryClass);
+            }
+        }
+        return loggingSupport;
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/src/java/org/apache/cassandra/utils/logging/NoOpFallbackLoggingSupport.java
----------------------------------------------------------------------
diff --git 
a/src/java/org/apache/cassandra/utils/logging/NoOpFallbackLoggingSupport.java 
b/src/java/org/apache/cassandra/utils/logging/NoOpFallbackLoggingSupport.java
new file mode 100644
index 0000000..4865e09
--- /dev/null
+++ 
b/src/java/org/apache/cassandra/utils/logging/NoOpFallbackLoggingSupport.java
@@ -0,0 +1,30 @@
+package org.apache.cassandra.utils.logging;
+
+import java.util.Collections;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * A fallback implementation with empty implementations which ensures other 
slf4j bindings (logging implementations)
+ * than the default supported framework can be used. This loses functionality, 
but is perfectly fine for most
+ * integration test requirements of applications using an embedded cassandra 
server.
+ */
+public class NoOpFallbackLoggingSupport implements LoggingSupport
+{
+    private static final Logger logger = 
LoggerFactory.getLogger(NoOpFallbackLoggingSupport.class);
+
+    @Override
+    public void setLoggingLevel(String classQualifier, String rawLevel) throws 
Exception
+    {
+        logger.warn("The log level was not changed, because you are using an 
unsupported slf4j logging implementation for which this functionality was not 
implemented.");
+    }
+
+    @Override
+    public Map<String, String> getLoggingLevels()
+    {
+        logger.warn("An empty map of logger names and their logging levels was 
returned, because you are using an unsupported slf4j logging implementation for 
which this functionality was not implemented.");
+        return Collections.emptyMap();
+    }
+}

http://git-wip-us.apache.org/repos/asf/cassandra/blob/bd080406/test/unit/org/apache/cassandra/cql3/CQLTester.java
----------------------------------------------------------------------
diff --git a/test/unit/org/apache/cassandra/cql3/CQLTester.java 
b/test/unit/org/apache/cassandra/cql3/CQLTester.java
index d150dac..192cbbc 100644
--- a/test/unit/org/apache/cassandra/cql3/CQLTester.java
+++ b/test/unit/org/apache/cassandra/cql3/CQLTester.java
@@ -48,7 +48,6 @@ import org.apache.cassandra.config.DatabaseDescriptor;
 import org.apache.cassandra.config.Schema;
 import org.apache.cassandra.config.SchemaConstants;
 import org.apache.cassandra.cql3.functions.FunctionName;
-import org.apache.cassandra.cql3.functions.ThreadAwareSecurityManager;
 import org.apache.cassandra.cql3.statements.ParsedStatement;
 import org.apache.cassandra.db.*;
 import org.apache.cassandra.db.commitlog.CommitLog;
@@ -67,6 +66,7 @@ import org.apache.cassandra.transport.ProtocolVersion;
 import org.apache.cassandra.transport.messages.ResultMessage;
 import org.apache.cassandra.utils.ByteBufferUtil;
 import org.apache.cassandra.utils.FBUtilities;
+import org.apache.cassandra.security.ThreadAwareSecurityManager;
 
 import static junit.framework.Assert.assertNotNull;
 


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscr...@cassandra.apache.org
For additional commands, e-mail: commits-h...@cassandra.apache.org

Reply via email to