Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
* Both {@link org.javasimon.Stopwatch} and {@link org.javasimon.Counter} provide human readable
* {@code toString} outputs. All nanosecond values are converted into few valid digits with
* proper unit (ns, us, ms, s) - this is done via method {@link #presentNanoTime(long)}.
* Max/min counter values are checked for undefined state (max/min long value is converted
* to string "undef") - via method {@link #presentMinMaxCount(long)}.
*
*
Aggregation utilities
* It is possible to sum up (aggregate) values for a subtree for a particular Simon type using
* {@link #calculateCounterAggregate(org.javasimon.Simon)} or {@link #calculateStopwatchAggregate(org.javasimon.Simon)}.
* Methods come also in versions allowing to filter by {@link org.javasimon.SimonFilter}.
*
*
Simon tree operations
* For various debug purposes there is a method that creates string displaying the whole Simon sub-tree.
* Here is example code that initializes two random Simons and prints the whole Simon hierarchy
* (note that the method can be used to obtain any sub-tree of the hierarchy):
*
* (+): Unknown Simon: [ ENABLED]
* com(+): Unknown Simon: [com INHERIT]
* my(+): Unknown Simon: [com.my INHERIT]
* other(+): Unknown Simon: [com.my.other INHERIT]
* stopwatch(+): Simon Stopwatch: total 24.2 ms, counter 1, max 24.2 ms, min 24.2 ms, mean 24.2 ms [com.my.other.stopwatch INHERIT]
* counter(-): Simon Counter: counter=0, max=undef, min=undef [com.my.counter DISABLED]
* Notice +/- signs in parenthesis that displays effective Simon state (enabled/disabled), further
* details are printed via each Simon's {@code toString} method.
*
*
Other utilities
* It is possible to obtain "local name" of the Simon (behind the last dot) via {@link #localName(String)}
* or check if the name is valid Simon name via {@link #checkName(String)}.
*
* @author Richard "Virgo" Richter
* @author Radovan Sninsky
* @since 1.0
*/
@SuppressWarnings({"UnusedDeclaration"})
public final class SimonUtils {
/** Regex character class content for {@link #NAME_PATTERN}. */
public static final String NAME_PATTERN_CHAR_CLASS_CONTENT = "-_\\[\\]A-Za-z0-9.,@$%)(<>";
/** Regex pattern for Simon names. */
public static final Pattern NAME_PATTERN = Pattern.compile("[" + NAME_PATTERN_CHAR_CLASS_CONTENT + "]+");
/**
* Allowed Simon name characters.
*
* @since 2.3
*/
public static final String ALLOWED_CHARS = "-_[]ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.,@$%()<>";
/**
* Name of the attribute where manager is searched for in an appropriate context - used for Spring/JavaEE/console integration.
* While the name can be used in any context supporting named attributes, it is primarily aimed for ServletContext. Manager can
* be shared in ServletContext by {@code SimonWebConfigurationBean} (Spring module) and then picked up by {@code SimonServletFilter}
* (JavaEE module) and {@code SimonConsoleFilter} (Embeddable console). If no manager is found in the attribute of the context,
* it is expected that components will use default {@link org.javasimon.SimonManager} instead.
*
* @since 3.2
*/
public static final String MANAGER_SERVLET_CTX_ATTRIBUTE = "manager-servlet-ctx-attribute";
private static final SimpleDateFormat TIMESTAMP_FORMAT = new SimpleDateFormat("yyMMdd-HHmmss.SSS");
private static final int UNIT_PREFIX_FACTOR = 1000;
private static final DecimalFormatSymbols DECIMAL_FORMAT_SYMBOLS = new DecimalFormatSymbols(Locale.US);
private static final int TEN = 10;
private static final DecimalFormat UNDER_TEN_FORMAT = new DecimalFormat("0.00", DECIMAL_FORMAT_SYMBOLS);
private static final int HUNDRED = 100;
private static final DecimalFormat UNDER_HUNDRED_FORMAT = new DecimalFormat("00.0", DECIMAL_FORMAT_SYMBOLS);
private static final DecimalFormat DEFAULT_FORMAT = new DecimalFormat("000", DECIMAL_FORMAT_SYMBOLS);
private static final String UNDEF_STRING = "undef";
private static final int CLIENT_CODE_STACK_INDEX;
static {
// Finds out the index of "this code" in the returned stack trace - funny but it differs in JDK 1.5 and 1.6
int i = 1;
for (StackTraceElement ste : Thread.currentThread().getStackTrace()) {
i++;
if (ste.getClassName().equals(SimonUtils.class.getName())) {
break;
}
}
CLIENT_CODE_STACK_INDEX = i;
}
private SimonUtils() {
throw new AssertionError();
}
/**
* Returns nano-time in human readable form with unit. Number is always from 10 to 9999
* except for seconds that are the biggest unit used. In case of undefined value ({@link Long#MAX_VALUE} or
* {@link Long#MIN_VALUE}) string {@link #UNDEF_STRING} is returned, so it can be used for min/max values as well.
*
* @param nanos time in nanoseconds
* @return human readable time string
*/
public static String presentNanoTime(long nanos) {
if (nanos == Long.MAX_VALUE || nanos == Long.MIN_VALUE) {
return UNDEF_STRING;
}
return presentNanoTimePrivate((double) nanos);
}
/**
* Returns nano-time in human readable form with unit. Number is always from 10 to 9999
* except for seconds that are the biggest unit used.
*
* @param nanos time in nanoseconds
* @return human readable time string
*/
public static String presentNanoTime(double nanos) {
if (nanos == Double.MAX_VALUE) {
return UNDEF_STRING;
}
return presentNanoTimePrivate(nanos);
}
private static String presentNanoTimePrivate(double time) {
if (Math.abs(time) < 1d) {
return "0";
}
if (time < UNIT_PREFIX_FACTOR) {
return ((long) time) + " ns";
}
time /= UNIT_PREFIX_FACTOR;
if (time < UNIT_PREFIX_FACTOR) {
return formatTime(time, " us");
}
time /= UNIT_PREFIX_FACTOR;
if (time < UNIT_PREFIX_FACTOR) {
return formatTime(time, " ms");
}
time /= UNIT_PREFIX_FACTOR;
return formatTime(time, " s");
}
private static synchronized String formatTime(double time, String unit) {
if (time < TEN) {
return UNDER_TEN_FORMAT.format(time) + unit;
}
if (time < HUNDRED) {
return UNDER_HUNDRED_FORMAT.format(time) + unit;
}
return DEFAULT_FORMAT.format(time) + unit;
}
/**
* Returns timestamp in human readable (yet condensed) form "yyMMdd-HHmmss.SSS".
*
* @param timestamp timestamp in millis
* @return timestamp as a human readable string
*/
public static String presentTimestamp(long timestamp) {
if (timestamp == 0) {
return UNDEF_STRING;
}
synchronized (TIMESTAMP_FORMAT) {
return TIMESTAMP_FORMAT.format(new Date(timestamp));
}
}
/**
* Returns min/max counter values in human readable form - if the value is max or min long value
* it is considered unused and string "undef" is returned.
*
* @param minmax counter extreme value
* @return counter value or "undef" if counter contains {@code Long.MIN_VALUE} or {@code Long.MAX_VALUE}
*/
public static String presentMinMaxCount(long minmax) {
if (minmax == Long.MAX_VALUE || minmax == Long.MIN_VALUE) {
return UNDEF_STRING;
}
return String.valueOf(minmax);
}
/**
* Returns multi-line string containing Simon tree starting with the specified Simon.
* Root Simon can be used to obtain tree with all Simons. Returns {@code null} for
* input value of null or for NullSimon or any Simon with name equal to null (anonymous
* Simons) - this is also the case when the Manager is disabled and tree for its root
* Simon is requested.
*
* @param simon root Simon of the output tree
* @return string containing the tree or null if the Simon is null Simon
*/
public static String simonTreeString(Simon simon) {
if (simon == null || simon.getName() == null) {
return null;
}
StringBuilder sb = new StringBuilder();
printSimonTree(0, simon, sb);
return sb.toString();
}
private static void printSimonTree(int level, Simon simon, StringBuilder sb) {
printSimon(level, simon, sb);
for (Simon child : simon.getChildren()) {
printSimonTree(level + 1, child, sb);
}
}
private static void printSimon(int level, Simon simon, StringBuilder sb) {
for (int i = 0; i < level; i++) {
sb.append(" ");
}
sb.append(localName(simon.getName()))
.append('(')
.append(simon.isEnabled() ? '+' : '-')
.append("): ")
.append(simon.toString())
.append('\n');
}
/**
* Returns last part of Simon name - local name.
*
* @param name full Simon name
* @return string containing local name
*/
public static String localName(String name) {
int ix = name.lastIndexOf(Manager.HIERARCHY_DELIMITER);
if (ix == -1) {
return name;
}
return name.substring(ix + 1);
}
/**
* Checks if the input string is correct Simon name. Simon name is checked against
* public {@link #NAME_PATTERN}.
*
* @param name checked Simon name
* @return true if the string is proper Simon name
*/
public static boolean checkName(String name) {
return NAME_PATTERN.matcher(name).matches();
}
/**
* Checks if the input string is correct Simon name and throws {@link SimonException} otherwise.
*
* @param name checked Simon name
* @throws SimonException if the string is not proper Simon name
*/
public static void validateSimonName(String name) {
if (!checkName(name)) {
throw new SimonException("Simon name must match following pattern: '" + SimonUtils.NAME_PATTERN.pattern() + "', used name: " + name);
}
}
/**
* Autogenerates name for the Simon using the fully-qualified class name.
* This method has inherent performance penalty (getting the stack-trace) so it is recommended to store
* pre-generated name. (Whole get-start-stop cycle is roughly 30-times slower with this method included.)
*
* @param suffix name suffix for eventual Simon discrimination
* @return autogenerated name for Simon
* @since 3.1
*/
public static String generateNameForClass(String suffix) {
return generatePrivate(suffix, false);
}
/**
* Autogenerates name for the Simon using the fully qualified class name and the method name.
* This method has inherent performance penalty (getting the stack-trace) so it is recommended to store
* pre-generated name. (Whole get-start-stop cycle is roughly 30-times slower with this method included.)
*
* @param suffix name suffix for eventual Simon discrimination
* @return autogenerated name for Simon
* @since 3.1
*/
public static String generateNameForClassAndMethod(String suffix) {
return generatePrivate(suffix, true);
}
// method is extracted, so the stack trace index is always right
private static String generatePrivate(String suffix, boolean includeMethodName) {
StackTraceElement stackElement = Thread.currentThread().getStackTrace()[CLIENT_CODE_STACK_INDEX];
StringBuilder nameBuilder = new StringBuilder(stackElement.getClassName());
if (includeMethodName) {
nameBuilder.append('.').append(stackElement.getMethodName());
}
if (suffix != null) {
nameBuilder.append(suffix);
}
return nameBuilder.toString();
}
/**
* Autogenerates name for the Simon using the class name and the method name.
*
* @return autogenerated name for Simon
* @see #generateNameForClassAndMethod(String)
*/
public static String generateName() {
return generatePrivate(null, true);
}
/**
* Calls a block of code with stopwatch around and returns result.
*
* @param name name of the Stopwatch
* @param callable callable block of code
* @param return type
* @return whatever block of code returns
* @throws Exception whatever block of code throws
* @since 3.0
*/
public static T doWithStopwatch(String name, Callable callable) throws Exception {
try (Split split = SimonManager.getStopwatch(name).start()) {
return callable.call();
}
}
/**
* Calls a block of code with stopwatch around, can not return any result or throw an exception
* (use {@link #doWithStopwatch(String, java.util.concurrent.Callable)} instead).
*
* @param name name of the Stopwatch
* @param runnable wrapped block of code
* @since 3.0
*/
public static void doWithStopwatch(String name, Runnable runnable) {
try (Split split = SimonManager.getStopwatch(name).start()) {
runnable.run();
}
}
private static final String SHRINKED_STRING = "...";
/**
* Shrinks the middle of the input string if it is too long, so it does not exceed limitTo.
*
* @since 3.2
*/
public static String compact(String input, int limitTo) {
if (input == null || input.length() <= limitTo) {
return input;
}
int headLength = limitTo / 2;
int tailLength = limitTo - SHRINKED_STRING.length() - headLength;
if (tailLength < 0) {
tailLength = 1;
}
return input.substring(0, headLength) + SHRINKED_STRING + input.substring(input.length() - tailLength);
}
/**
* Aggregate statistics from all stopwatches in hierarchy of simons.
*
* @param simon root of the hierarchy of simons for which statistics will be aggregated
* @return aggregated statistics
* @since 3.5
*/
public static StopwatchAggregate calculateStopwatchAggregate(Simon simon) {
return calculateStopwatchAggregate(simon, null);
}
/**
* Aggregate statistics from all stopwatches in hierarchy that pass specified filter. Filter is applied
* to all simons in the hierarchy of all types. If a simon is rejected by filter its children are not considered.
* Simons are aggregated in the top-bottom fashion, i.e. parent simons are aggregated before children.
*
* @param simon root of the hierarchy of simons for which statistics will be aggregated
* @param filter filter to select subsets of simons to aggregate
* @return aggregates statistics
* @since 3.5
*/
public static StopwatchAggregate calculateStopwatchAggregate(Simon simon, SimonFilter filter) {
StopwatchAggregate stopwatchAggregate = new StopwatchAggregate();
aggregateStopwatches(stopwatchAggregate, simon,
filter != null ? filter : SimonFilter.ACCEPT_ALL_FILTER);
return stopwatchAggregate;
}
private static void aggregateStopwatches(StopwatchAggregate aggregate, Simon simon, SimonFilter filter) {
if (filter.accept(simon)) {
if (simon instanceof Stopwatch) {
Stopwatch stopwatch = (Stopwatch) simon;
aggregate.addSample(stopwatch.sample());
}
for (Simon child : simon.getChildren()) {
aggregateStopwatches(aggregate, child, filter);
}
}
}
/**
* Aggregate statistics from all counters in hierarchy of simons.
*
* @param simon root of the hierarchy of simons for which statistics will be aggregated
* @return aggregated statistics
* @since 3.5
*/
public static CounterAggregate calculateCounterAggregate(Simon simon) {
return calculateCounterAggregate(simon, null);
}
/**
* Aggregate statistics from all counters in hierarchy that pass specified filter. Filter is applied
* to all simons in the hierarchy of all types. If a simon is rejected by filter its children are not considered.
* Simons are aggregated in the top-bottom fashion, i.e. parent simons are aggregated before children.
*
* @param simon root of the hierarchy of simons for which statistics will be aggregated
* @param filter filter to select subsets of simons to aggregate
* @return aggregates statistics
* @since 3.5
*/
public static CounterAggregate calculateCounterAggregate(Simon simon, SimonFilter filter) {
CounterAggregate aggregate = new CounterAggregate();
aggregateCounters(aggregate, simon,
filter != null ? filter : SimonFilter.ACCEPT_ALL_FILTER);
return aggregate;
}
private static void aggregateCounters(CounterAggregate aggregate, Simon simon, SimonFilter filter) {
if (filter.accept(simon)) {
if (simon instanceof Counter) {
Counter counter = (Counter) simon;
aggregate.addSample(counter.sample());
}
for (Simon child : simon.getChildren()) {
aggregateCounters(aggregate, child, filter);
}
}
}
}