1 /* 2 * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.jfr.internal; 27 28 import java.io.IOException; 29 import java.io.ObjectInputStream; 30 import java.io.ObjectOutputStream; 31 import java.security.AccessControlContext; 32 import java.security.AccessController; 33 import java.security.PrivilegedAction; 34 import java.util.Collections; 35 import java.util.HashSet; 36 import java.util.Objects; 37 import java.util.Set; 38 39 // User must never be able to subclass directly. 40 // 41 // Never put Control or Setting Control in a collections 42 // so overridable versions of hashCode or equals are 43 // executed in the wrong context. TODO: wrap this class 44 // in SsecureControl directly when it is instantiated and 45 // forward calls using AccessControlContext 46 abstract public class Control { 47 private final AccessControlContext context; 48 private final static int CACHE_SIZE = 5; 49 private final Set<?>[] cachedUnions = new HashSet<?>[CACHE_SIZE]; 50 private final String[] cachedValues = new String[CACHE_SIZE]; 51 private String defaultValue; 52 private String lastValue; 53 54 // called by exposed subclass in external API Control(AccessControlContext acc)55 public Control(AccessControlContext acc) { 56 Objects.requireNonNull(acc); 57 this.context = acc; 58 59 } 60 61 // only to be called by trusted VM code Control(String defaultValue)62 public Control(String defaultValue) { 63 this.defaultValue = defaultValue; 64 this.context = null; 65 } 66 67 // For user code to override, must never be called from jdk.jfr.internal 68 // for user defined settings combine(Set<String> values)69 public abstract String combine(Set<String> values); 70 71 // For user code to override, must never be called from jdk.jfr.internal 72 // for user defined settings setValue(String value)73 public abstract void setValue(String value); 74 75 // For user code to override, must never be called from jdk.jfr.internal 76 // for user defined settings getValue()77 public abstract String getValue(); 78 79 // Package private, user code should not have access to this method apply(Set<String> values)80 final void apply(Set<String> values) { 81 setValueSafe(findCombineSafe(values)); 82 } 83 84 // Package private, user code should not have access to this method. 85 // Only called during event registration setDefault()86 final void setDefault() { 87 if (defaultValue == null) { 88 defaultValue = getValueSafe(); 89 } 90 apply(defaultValue); 91 } 92 getValueSafe()93 final String getValueSafe() { 94 if (context == null) { 95 // VM events requires no access control context 96 return getValue(); 97 } else { 98 return AccessController.doPrivileged(new PrivilegedAction<String>() { 99 @Override 100 public String run() { 101 try { 102 return getValue(); 103 } catch (Throwable t) { 104 // Prevent malicious user to propagate exception callback in the wrong context 105 Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when trying to get value for " + getClass()); 106 } 107 return defaultValue != null ? defaultValue : ""; // Need to return something 108 } 109 }, context); 110 } 111 } 112 113 private void apply(String value) { 114 if (lastValue != null && Objects.equals(value, lastValue)) { 115 return; 116 } 117 setValueSafe(value); 118 } 119 120 final void setValueSafe(String value) { 121 if (context == null) { 122 // VM events requires no access control context 123 try { 124 setValue(value); 125 } catch (Throwable t) { 126 Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass()); 127 } 128 } else { 129 AccessController.doPrivileged(new PrivilegedAction<Void>() { 130 @Override 131 public Void run() { 132 try { 133 setValue(value); 134 } catch (Throwable t) { 135 // Prevent malicious user to propagate exception callback in the wrong context 136 Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass()); 137 } 138 return null; 139 } 140 }, context); 141 } 142 lastValue = value; 143 } 144 145 146 private String combineSafe(Set<String> values) { 147 if (context == null) { 148 // VM events requires no access control context 149 return combine(values); 150 } 151 return AccessController.doPrivileged(new PrivilegedAction<String>() { 152 @Override 153 public String run() { 154 try { 155 combine(Collections.unmodifiableSet(values)); 156 } catch (Throwable t) { 157 // Prevent malicious user to propagate exception callback in the wrong context 158 Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when combining " + values + " for " + getClass()); 159 } 160 return null; 161 } 162 }, context); 163 } 164 165 private final String findCombineSafe(Set<String> values) { 166 if (values.size() == 1) { 167 return values.iterator().next(); 168 } 169 for (int i = 0; i < CACHE_SIZE; i++) { 170 if (Objects.equals(cachedUnions[i], values)) { 171 return cachedValues[i]; 172 } 173 } 174 String result = combineSafe(values); 175 for (int i = 0; i < CACHE_SIZE - 1; i++) { 176 cachedUnions[i + 1] = cachedUnions[i]; 177 cachedValues[i + 1] = cachedValues[i]; 178 } 179 cachedValues[0] = result; 180 cachedUnions[0] = values; 181 return result; 182 } 183 184 185 // package private, user code should not have access to this method 186 final String getDefaultValue() { 187 return defaultValue; 188 } 189 190 // package private, user code should not have access to this method 191 final String getLastValue() { 192 return lastValue; 193 } 194 195 // Precaution to prevent a malicious user from instantiating instances 196 // of a control where the context has not been set up. 197 @Override 198 public final Object clone() throws java.lang.CloneNotSupportedException { 199 throw new CloneNotSupportedException(); 200 } 201 202 private final void writeObject(ObjectOutputStream out) throws IOException { 203 throw new IOException("Object cannot be serialized"); 204 } 205 206 private final void readObject(ObjectInputStream in) throws IOException { 207 throw new IOException("Class cannot be deserialized"); 208 } 209 } 210