blob: 2a483a377ce310542f5bd542164cc6ed9d1c969b [file] [log] [blame]
/*
* 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.jackrabbit.oak.plugins.document.util;
import java.util.Objects;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.event.Level;
/**
* Utility class for consistent handling of system properties.
* <p>
* It provides for:
* <ul>
* <li>TRACE level logging of getting the system property
* <li>ERROR level logging when value does not parse or is invalid (where
* validity can be checked by a {@link Predicate})
* <li>(default) INFO level logging when value differs from default (log level
* and message format can be overridden)
* </ul>
* <p>
* The supported types are: {@link Boolean}, {@link Integer}, {@link Long}, {@link String}
*/
public class SystemPropertySupplier<T> implements Supplier<T> {
private static final Logger LOG = LoggerFactory.getLogger(SystemPropertySupplier.class);
private final String propName;
private final T defaultValue;
private final Function<String, T> parser;
private Logger log = LOG;
private Level successLogLevel = Level.INFO;
private Predicate<T> validator = (a) -> true;
private Function<String, String> sysPropReader = System::getProperty;
private BiFunction<String, T, String> setMessageFormatter = (a, b) -> {
return String.format("System property %s found to be '%s'", a, b);
};
private SystemPropertySupplier(@NotNull String propName, @NotNull T defaultValue) throws IllegalArgumentException {
this.propName = Objects.requireNonNull(propName, "propertyName must be non-null");
this.defaultValue = Objects.requireNonNull(defaultValue, "defaultValue must be non-null");
this.parser = getValueParser(defaultValue);
}
/**
* Create it for a given property name and default value.
*/
public static <U> SystemPropertySupplier<U> create(@NotNull String propName, @NotNull U defaultValue)
throws IllegalArgumentException {
return new SystemPropertySupplier<U>(propName, defaultValue);
}
/**
* Specify the {@link Logger} to log to (defaults to this classes logger otherwise).
*/
public SystemPropertySupplier<T> loggingTo(@NotNull Logger log) {
this.log = Objects.requireNonNull(log);
return this;
}
/**
* Specify a validation expression.
*/
public SystemPropertySupplier<T> validateWith(@NotNull Predicate<T> validator) {
this.validator = Objects.requireNonNull(validator);
return this;
}
/**
* Specify a formatter for the "success" log message to be used when the
* returned property value differs from the default.
*/
public SystemPropertySupplier<T> formatSetMessage(@NotNull BiFunction<String, T, String> setMessageFormatter) {
this.setMessageFormatter = Objects.requireNonNull(setMessageFormatter);
return this;
}
/**
* Specify {@link Level} to use for "success" message.
*/
public SystemPropertySupplier<T> logSuccessAs(Level successLogLevel) {
this.successLogLevel = Objects.requireNonNull(successLogLevel);
return this;
}
/**
* <em>For unit testing</em>: specify a function to read system properties
* (overriding default of {@code System.getProperty(String}).
*/
protected SystemPropertySupplier<T> usingSystemPropertyReader(@NotNull Function<String, String> sysPropReader) {
this.sysPropReader = Objects.requireNonNull(sysPropReader);
return this;
}
/**
* Obtains the value of a system property, optionally generating
* diagnostics.
*
* @return value of system property
*/
public T get() {
T ret = defaultValue;
String value = sysPropReader.apply(propName);
if (value == null) {
log.trace("System property {} not set", propName);
} else {
log.trace("System property {} set to '{}'", propName, value);
try {
T v = parser.apply(value);
if (!validator.test(v)) {
log.error("Ignoring invalid value '{}' for system property {}", value, propName);
} else {
ret = v;
}
} catch (NumberFormatException ex) {
log.error("Ignoring malformed value '{}' for system property {}", value, propName);
}
if (!ret.equals(defaultValue)) {
String msg = setMessageFormatter.apply(propName, ret);
switch (successLogLevel) {
case INFO:
log.info(msg);
break;
case DEBUG:
log.debug(msg);
break;
case ERROR:
log.error(msg);
break;
case TRACE:
log.trace(msg);
break;
case WARN:
log.warn(msg);
break;
default:
break;
}
}
}
return ret;
}
@SuppressWarnings("unchecked")
private static <T> Function<String, T> getValueParser(T defaultValue) {
if (defaultValue instanceof Boolean) {
return v -> (T) Boolean.valueOf(v);
} else if (defaultValue instanceof Integer) {
return v -> (T) Integer.valueOf(v);
} else if (defaultValue instanceof Long) {
return v -> (T) Long.valueOf(v);
} else if (defaultValue instanceof String) {
return v -> (T) v;
} else {
throw new IllegalArgumentException(
String.format("expects a defaultValue of Boolean, Integer, Long, or String, but got: %s", defaultValue.getClass()));
}
}
}