/* * Copyright 2014 OCTO Technology * * Licensed 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 com.octo.reactive.audit.lib; /** * Exception thrown by the JVM agent if a blocking API is used. * This exception is thrown only if the reactiveAudit_throwExceptions parameter is set to <b>true</b>. * * @author Philippe PRADOS * @since 1.0 */ public class ReactiveAuditException extends AssertionError { /* This variable was set by the javaagent, via introspection. Thus it is not declared public. */ @SuppressWarnings("StaticNonFinalField") /*!final !!!*/static boolean debug = false; /* Calculate the package name of the project. */ private static final String auditPackageName = ReactiveAuditException.class.getPackage().getName().replaceFirst("\\.[^.]+$", ""); /* If debug, use a limited stack trace. */ private static final int LIMIT_STACK_SIZE_IF_DEBUG = 10; /* The threadName with the exception is created. */ private final String threadName; /* The latency of this exception. */ private Latency latency; /** * Create an {@link AssertionError} with {@link Latency}, thread name and message. * * @param latency The latency for this exception. * @param message The message associated with this exception. */ protected ReactiveAuditException(Latency latency, String message) { super(message); this.threadName = Thread.currentThread().getName(); this.latency = latency; updateStackTraceElements(); } /** * Create an {@link AssertionError} with {@link Latency}, thread name and formatted message. * * @param latency The latency for this exception. * @param format The format message associated with this exception. * @param args The arguments to generate the message with the format. */ protected ReactiveAuditException(Latency latency, String format, Object... args) { super(String.format(format, args)); this.threadName = Thread.currentThread().getName(); updateStackTraceElements(); } /** * @return The latency associated with this exception. */ public Latency getLatency() { return this.latency; } /** * @return a String with "<message> at thread <thread name>" */ @Override public String toString() { return super.toString() + System.getProperty("line.separator")+ "\tat thread \"" + this.threadName + '"'; } /** * If not debug, remove all the audit layout in the stack trace. */ @SuppressWarnings("PointlessBooleanExpression") private void updateStackTraceElements() { if (!debug) // Warning: the debug value can be updated via introspection. { // Filter stack trace final StackTraceElement[] stack = getStackTrace(); int pos = 0; for (final StackTraceElement traceElement : stack) { if (!traceElement.getClassName().startsWith(auditPackageName) || traceElement.getClassName().endsWith("Test")) // For inner unit test { break; } ++pos; } final StackTraceElement[] newStack = new StackTraceElement[stack.length - pos]; System.arraycopy(stack, pos, newStack, 0, newStack.length); setStackTrace(newStack); } else { final StackTraceElement[] stack = getStackTrace(); final int newSize = Math.min(stack.length, LIMIT_STACK_SIZE_IF_DEBUG); final StackTraceElement[] newStack = new StackTraceElement[newSize]; System.arraycopy(stack, 0, newStack, 0, newSize); setStackTrace(newStack); } } }