/*
 * 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.core.impl.ReflectiveCallerClassUtility;
import org.apache.logging.log4j.message.Message;
import org.apache.logging.log4j.message.StringFormattedMessage;
import org.openjdk.jmh.annotations.GenerateMicroBenchmark;
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 microbenchmarks.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 microbenchmarks.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();
        }
    }

    @GenerateMicroBenchmark
    public void testBaseline() {
    }

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

    @GenerateMicroBenchmark
    public String getCallerClassNameReflectively() {
        return ReflectiveCallerClassUtility.getCaller(3).getName();
    }

    @GenerateMicroBenchmark
    public String getCallerClassNameSunReflection() {
        return Reflection.getCallerClass(3).getName();
    }

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

    @GenerateMicroBenchmark
    public Class<?> getReflectiveCallerClassUtility() {
        return ReflectiveCallerClassUtility.getCaller(3);
    }

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

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

    @GenerateMicroBenchmark
    public Message 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});
    }
}

@@ -2146,13 +2146,9 @@
 oid 
-testB
+b
 asel
@@ -2200,32 +2200,39 @@
   public String 
+test01_
 getCallerClassNa
@@ -2363,32 +2363,215 @@
   public String 
+test02_getCallerClassNameFromThreadStackTrace() %7B%0A        return Thread.currentThread().getStackTrace()%5B3%5D.getClassName();%0A    %7D%0A%0A    @GenerateMicroBenchmark%0A    public String test03_
 getCallerClassNa
@@ -2710,16 +2710,23 @@
  String 
+test04_
 getCalle
@@ -2863,16 +2863,23 @@
 lass%3C?%3E 
+test05_
 getStack
@@ -3071,16 +3071,244 @@
 lass%3C?%3E 
+test06_getThreadStackTraceClassForClassName() throws ClassNotFoundException %7B%0A        return Class.forName(Thread.currentThread().getStackTrace()%5B3%5D.getClassName());%0A    %7D%0A%0A    @GenerateMicroBenchmark%0A    public Class%3C?%3E test07_
 getRefle
@@ -3448,16 +3448,23 @@
 lass%3C?%3E 
+test08_
 getDirec
@@ -3573,32 +3573,39 @@
  public Message 
+test09_
 getMessageUsingN
@@ -3728,32 +3728,32 @@
 eMicroBenchmark%0A
-
     public Messa
@@ -3755,16 +3755,23 @@
 Message 
+test10_
 getMessa

Rename benchmarks to preserve order.