|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
*/ |
|
|
|
package jdk.jfr.internal; |
|
|
|
import java.io.IOException; |
|
import java.io.ObjectInputStream; |
|
import java.io.ObjectOutputStream; |
|
import java.security.AccessControlContext; |
|
import java.security.AccessController; |
|
import java.security.PrivilegedAction; |
|
import java.util.Collections; |
|
import java.util.HashSet; |
|
import java.util.Objects; |
|
import java.util.Set; |
|
|
|
// User must never be able to subclass directly. |
|
// |
|
// Never put Control or Setting Control in a collections |
|
// so overridable versions of hashCode or equals are |
|
// executed in the wrong context. TODO: wrap this class |
|
// in SsecureControl directly when it is instantiated and |
|
|
|
abstract public class Control { |
|
private final AccessControlContext context; |
|
private final static int CACHE_SIZE = 5; |
|
private final Set<?>[] cachedUnions = new HashSet<?>[CACHE_SIZE]; |
|
private final String[] cachedValues = new String[CACHE_SIZE]; |
|
private String defaultValue; |
|
private String lastValue; |
|
|
|
|
|
public Control(AccessControlContext acc) { |
|
Objects.requireNonNull(acc); |
|
this.context = acc; |
|
|
|
} |
|
|
|
|
|
public Control(String defaultValue) { |
|
this.defaultValue = defaultValue; |
|
this.context = null; |
|
} |
|
|
|
// For user code to override, must never be called from jdk.jfr.internal |
|
|
|
public abstract String combine(Set<String> values); |
|
|
|
// For user code to override, must never be called from jdk.jfr.internal |
|
|
|
public abstract void setValue(String value); |
|
|
|
// For user code to override, must never be called from jdk.jfr.internal |
|
|
|
public abstract String getValue(); |
|
|
|
|
|
final void apply(Set<String> values) { |
|
setValueSafe(findCombineSafe(values)); |
|
} |
|
|
|
// Package private, user code should not have access to this method. |
|
|
|
final void setDefault() { |
|
if (defaultValue == null) { |
|
defaultValue = getValueSafe(); |
|
} |
|
apply(defaultValue); |
|
} |
|
|
|
final String getValueSafe() { |
|
if (context == null) { |
|
|
|
return getValue(); |
|
} else { |
|
return AccessController.doPrivileged(new PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
try { |
|
return getValue(); |
|
} catch (Throwable t) { |
|
|
|
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when trying to get value for " + getClass()); |
|
} |
|
return defaultValue != null ? defaultValue : ""; |
|
} |
|
}, context); |
|
} |
|
} |
|
|
|
private void apply(String value) { |
|
if (lastValue != null && Objects.equals(value, lastValue)) { |
|
return; |
|
} |
|
setValueSafe(value); |
|
} |
|
|
|
final void setValueSafe(String value) { |
|
if (context == null) { |
|
|
|
try { |
|
setValue(value); |
|
} catch (Throwable t) { |
|
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass()); |
|
} |
|
} else { |
|
AccessController.doPrivileged(new PrivilegedAction<Void>() { |
|
@Override |
|
public Void run() { |
|
try { |
|
setValue(value); |
|
} catch (Throwable t) { |
|
|
|
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when setting value \"" + value + "\" for " + getClass()); |
|
} |
|
return null; |
|
} |
|
}, context); |
|
} |
|
lastValue = value; |
|
} |
|
|
|
|
|
private String combineSafe(Set<String> values) { |
|
if (context == null) { |
|
|
|
return combine(values); |
|
} |
|
return AccessController.doPrivileged(new PrivilegedAction<String>() { |
|
@Override |
|
public String run() { |
|
try { |
|
combine(Collections.unmodifiableSet(values)); |
|
} catch (Throwable t) { |
|
|
|
Logger.log(LogTag.JFR_SETTING, LogLevel.WARN, "Exception occured when combining " + values + " for " + getClass()); |
|
} |
|
return null; |
|
} |
|
}, context); |
|
} |
|
|
|
private final String findCombineSafe(Set<String> values) { |
|
if (values.size() == 1) { |
|
return values.iterator().next(); |
|
} |
|
for (int i = 0; i < CACHE_SIZE; i++) { |
|
if (Objects.equals(cachedUnions[i], values)) { |
|
return cachedValues[i]; |
|
} |
|
} |
|
String result = combineSafe(values); |
|
for (int i = 0; i < CACHE_SIZE - 1; i++) { |
|
cachedUnions[i + 1] = cachedUnions[i]; |
|
cachedValues[i + 1] = cachedValues[i]; |
|
} |
|
cachedValues[0] = result; |
|
cachedUnions[0] = values; |
|
return result; |
|
} |
|
|
|
|
|
|
|
final String getDefaultValue() { |
|
return defaultValue; |
|
} |
|
|
|
|
|
final String getLastValue() { |
|
return lastValue; |
|
} |
|
|
|
// Precaution to prevent a malicious user from instantiating instances |
|
|
|
@Override |
|
public final Object clone() throws java.lang.CloneNotSupportedException { |
|
throw new CloneNotSupportedException(); |
|
} |
|
|
|
private final void writeObject(ObjectOutputStream out) throws IOException { |
|
throw new IOException("Object cannot be serialized"); |
|
} |
|
|
|
private final void readObject(ObjectInputStream in) throws IOException { |
|
throw new IOException("Class cannot be deserialized"); |
|
} |
|
} |