/*
 * 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.logging.log4j.perf.jmh;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Random;

import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.openjdk.jmh.annotations.Benchmark;
import org.apache.logging.log4j.util.ReflectionUtil;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

import sun.reflect.Reflection;

/**
 * <p>
 * Benchmarks the different ways the caller class can be obtained. To run this in sampling mode (latency test):
 * </p>
 * 
 * <pre>
 *     java -jar benchmarks.jar ".*ReflectionBenchmark.*" -i 5 -f 1 -wi 5 -bm sample -tu ns
 * </pre>
 * <p>
 * To run this in throughput testing mode:
 * </p>
 * 
 * <pre>
 *     java -jar benchmarks.jar ".*ReflectionBenchmark.*" -i 5 -f 1 -wi 5 -bm Throughput -tu ms
 * </pre>
 */
public class ReflectionBenchmark {

    @State(Scope.Thread)
    public static class RandomInteger {

        private final Random r = new Random();

        int random;

        @Setup(Level.Iteration)
        public void setup() {
            random = r.nextInt();
        }
    }

    @Benchmark
    public void baseline() {
    }

    @Benchmark
    public String test01_getCallerClassNameFromStackTrace() {
        return new Throwable().getStackTrace()[3].getClassName();
    }

    @Benchmark
    public String test02_getCallerClassNameFromThreadStackTrace() {
        return Thread.currentThread().getStackTrace()[3].getClassName();
    }

    @Benchmark
    public String test03_getCallerClassNameReflectively() {
        return ReflectionUtil.getCallerClass(3).getName();
    }

    @Benchmark
    public String test04_getCallerClassNameSunReflection() {
        return Reflection.getCallerClass(3).getName();
    }

    @Benchmark
    public Class<?> test05_getStackTraceClassForClassName() throws ClassNotFoundException {
        return Class.forName(new Throwable().getStackTrace()[3].getClassName());
    }

    @Benchmark
    public Class<?> test06_getThreadStackTraceClassForClassName() throws ClassNotFoundException {
        return Class.forName(Thread.currentThread().getStackTrace()[3].getClassName());
    }

    @Benchmark
    public Class<?> test07_getReflectiveCallerClassUtility() {
        return ReflectionUtil.getCallerClass(3);
    }

    @Benchmark
    public Class<?> test08_getDirectSunReflection() {
        return Reflection.getCallerClass(3);
    }

    @Benchmark
    public Message test09_getMessageUsingNew(final RandomInteger rng) {
        return new StringFormattedMessage("Hello %i", rng.random);
    }

    @Benchmark
    public Message test10_getMessageUsingReflection(final RandomInteger rng) throws NoSuchMethodException,
            IllegalAccessException, InvocationTargetException, InstantiationException {
        final Constructor<? extends Message> constructor = StringFormattedMessage.class.getConstructor(String.class,
                Object[].class);
        return constructor.newInstance("Hello %i", new Object[] { rng.random });
    }
}

@@ -1082,94 +1082,94 @@
 org.
-openjdk.jmh.annotations.Benchmark;%0Aimport org.apache.logging.log4j.util.ReflectionUtil
+apache.logging.log4j.util.ReflectionUtil;%0Aimport org.openjdk.jmh.annotations.Benchmark
 ;%0Aim
@@ -1334,17 +1334,16 @@
 .State;%0A
-%0A
 import s
@@ -1491,33 +1491,32 @@
 est):%0A * %3C/p%3E%0A *
- 
 %0A * %3Cpre%3E%0A *    
@@ -1671,17 +1671,16 @@
  %3C/p%3E%0A *
- 
 %0A * %3Cpre
@@ -2020,24 +2020,24 @@
 d setup() %7B%0A
-
             
@@ -2067,32 +2067,255 @@
        %7D%0A    %7D%0A%0A
+    @State(Scope.Benchmark)%0A    public static class ClassContextManager extends SecurityManager %7B%0A        @Override%0A        protected Class%5B%5D getClassContext() %7B%0A            return super.getClassContext();%0A        %7D%0A    %7D%0A%0A
     @Benchmark%0A 
@@ -4207,10 +4207,570 @@
 ;%0A    %7D%0A
+%0A    @Benchmark%0A    public Class%3C?%3E%5B%5D test11_getClassContextViaCallerClass() %7B%0A        // let's not benchmark LinkedList or anything here%0A        final Class%3C?%3E%5B%5D classes = new Class%3C?%3E%5B100%5D;%0A        Class%3C?%3E clazz;%0A        for (int i = 0; null != (clazz = ReflectionUtil.getCallerClass(i)); i++) %7B%0A            classes%5Bi%5D = clazz;%0A        %7D%0A        return classes;%0A    %7D%0A%0A    @Benchmark%0A    public Class%3C?%3E%5B%5D test12_getClassContextViaSecurityManager(final ClassContextManager classContextManager) %7B%0A        return classContextManager.getClassContext();%0A    %7D%0A%0A
 %7D%0A

Add benchmarks for obtaining stack trace class array.