Coverage Report - org.perfidix.element.BenchmarkExecutor
 
Classes in this File Line Coverage Branch Coverage Complexity
BenchmarkExecutor
86%
93/108
90%
38/42
4.778
 
 1  
 /**
 2  
  * Copyright (c) 2012, University of Konstanz, Distributed Systems Group
 3  
  * All rights reserved.
 4  
  * 
 5  
  * Redistribution and use in source and binary forms, with or without
 6  
  * modification, are permitted provided that the following conditions are met:
 7  
  * * Redistributions of source code must retain the above copyright
 8  
  * notice, this list of conditions and the following disclaimer.
 9  
  * * Redistributions in binary form must reproduce the above copyright
 10  
  * notice, this list of conditions and the following disclaimer in the
 11  
  * documentation and/or other materials provided with the distribution.
 12  
  * * Neither the name of the University of Konstanz nor the
 13  
  * names of its contributors may be used to endorse or promote products
 14  
  * derived from this software without specific prior written permission.
 15  
  * 
 16  
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 17  
  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 18  
  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 19  
  * DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
 20  
  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 21  
  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 22  
  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 23  
  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 24  
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 25  
  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 26  
  */
 27  
 package org.perfidix.element;
 28  
 
 29  
 import java.lang.annotation.Annotation;
 30  
 import java.lang.reflect.InvocationTargetException;
 31  
 import java.lang.reflect.Method;
 32  
 import java.util.Arrays;
 33  
 import java.util.Hashtable;
 34  
 import java.util.LinkedHashSet;
 35  
 import java.util.Map;
 36  
 import java.util.Set;
 37  
 
 38  
 import org.perfidix.AbstractConfig;
 39  
 import org.perfidix.annotation.AfterEachRun;
 40  
 import org.perfidix.annotation.AfterLastRun;
 41  
 import org.perfidix.annotation.BeforeEachRun;
 42  
 import org.perfidix.annotation.BeforeFirstRun;
 43  
 import org.perfidix.annotation.Bench;
 44  
 import org.perfidix.exceptions.PerfidixMethodCheckException;
 45  
 import org.perfidix.exceptions.PerfidixMethodInvocationException;
 46  
 import org.perfidix.meter.AbstractMeter;
 47  
 import org.perfidix.result.BenchmarkResult;
 48  
 
 49  
 /**
 50  
  * Corresponding to each method, an executor is launched to execute {@link BeforeFirstRun},
 51  
  * {@link BeforeEachRun}, {@link AfterEachRun} and {@link AfterLastRun} classes. To store the data if the
 52  
  * single-execute before
 53  
  * classes have been executed, this class is implemented as a singleton to store
 54  
  * this information related to the method. All the data comes from the {@link BenchmarkMethod} class.
 55  
  * 
 56  
  * @author Sebastian Graf, University of Konstanz
 57  
  */
 58  
 public final class BenchmarkExecutor {
 59  
 
 60  
     /**
 61  
      * Static mapping for all methods to be executed because of the single-runs
 62  
      * before/after methods.
 63  
      */
 64  5
     private static final Map<BenchmarkMethod, BenchmarkExecutor> EXECUTOR =
 65  
         new Hashtable<BenchmarkMethod, BenchmarkExecutor>();
 66  
 
 67  
     /** Static Mapping of runs to occur for each BenchmarkMethod.} */
 68  5
     private static final Map<BenchmarkMethod, Integer> RUNS = new Hashtable<BenchmarkMethod, Integer>();
 69  
 
 70  
     /** Set with all meters to be benched automatically. */
 71  5
     private static final Set<AbstractMeter> METERS_TO_BENCH = new LinkedHashSet<AbstractMeter>();
 72  
 
 73  
     /** Result for all Benchmarks. */
 74  
     private static BenchmarkResult BENCHRES;
 75  
 
 76  
     /** Config for all Benchmarks. */
 77  
     private static AbstractConfig CONFIG;
 78  
 
 79  
     /** Boolean to be sure that the beforeFirstRun was not executed yet. */
 80  
     private transient boolean beforeFirstRun;
 81  
 
 82  
     /**
 83  
      * Corresponding BenchmarkElement of this executor to get the before/after
 84  
      * methods.
 85  
      */
 86  
     private transient final BenchmarkMethod element;
 87  
 
 88  
     /**
 89  
      * Private constructor, just setting the booleans and one element to get the
 90  
      * before/after methods.
 91  
      * 
 92  
      * @param paramElement
 93  
      *            BenchmarkElement to provide easy access to the before/after
 94  
      *            methods.
 95  
      */
 96  110
     private BenchmarkExecutor(final BenchmarkMethod paramElement) {
 97  110
         beforeFirstRun = false;
 98  110
         element = paramElement;
 99  110
     }
 100  
 
 101  
     /**
 102  
      * Getting the executor corresponding to a BenchmarkElement.
 103  
      * 
 104  
      * @param meth
 105  
      *            for the executor. If the underlaying {@link Method} was not
 106  
      *            registered, a new mapping-entry will be created.
 107  
      * @return the BenchmarkExecutor corresponding to the Method of the
 108  
      *         BenchmarkElement
 109  
      */
 110  
     public static BenchmarkExecutor getExecutor(final BenchmarkElement meth) {
 111  4000
         if (BENCHRES == null) {
 112  0
             throw new IllegalStateException("Call initialize method first!");
 113  
         }
 114  
 
 115  
         // check if new instance needs to be created
 116  4000
         if (!EXECUTOR.containsKey(meth.getMeth())) {
 117  110
             EXECUTOR.put(meth.getMeth(), new BenchmarkExecutor(meth.getMeth()));
 118  110
             int runsOnAnno = BenchmarkMethod.getNumberOfAnnotatedRuns(meth.getMeth().getMethodToBench());
 119  110
             if (runsOnAnno < 0) {
 120  50
                 runsOnAnno = CONFIG.getRuns();
 121  
             }
 122  110
             RUNS.put(meth.getMeth(), runsOnAnno);
 123  
         }
 124  
 
 125  
         // returning the executor
 126  4000
         return EXECUTOR.get(meth.getMeth());
 127  
 
 128  
     }
 129  
 
 130  
     /**
 131  
      * Initializing the executor.
 132  
      * 
 133  
      * @param config
 134  
      *            to be benched
 135  
      * @param result
 136  
      *            to be stored to
 137  
      */
 138  
     public static void initialize(final AbstractConfig config, final BenchmarkResult result) {
 139  65
         METERS_TO_BENCH.clear();
 140  65
         METERS_TO_BENCH.addAll(Arrays.asList(config.getMeters()));
 141  65
         EXECUTOR.clear();
 142  65
         BENCHRES = result;
 143  65
         CONFIG = config;
 144  
 
 145  65
     }
 146  
 
 147  
     /**
 148  
      * Executing the {@link BeforeFirstRun}-annotated methods (if still wasn't)
 149  
      * and the {@link BeforeEachRun} methods.
 150  
      * 
 151  
      * @param obj
 152  
      *            the object of the class where the bench runs currently in.
 153  
      */
 154  
     public void executeBeforeMethods(final Object obj) {
 155  
 
 156  
         // invoking once the beforeFirstRun-method
 157  3935
         if (!beforeFirstRun) {
 158  90
             beforeFirstRun = true;
 159  
 
 160  90
             Method[] beforeFirst = null;
 161  
             try {
 162  90
                 beforeFirst = element.findBeforeFirstRun();
 163  0
             } catch (final PerfidixMethodCheckException e) {
 164  0
                 BENCHRES.addException(e);
 165  90
             }
 166  90
             if (beforeFirst.length != 0) {
 167  55
                 checkAndExectuteBeforeAfters(obj, BeforeFirstRun.class, beforeFirst);
 168  
             }
 169  
         }
 170  
 
 171  
         // invoking the beforeEachRun-method
 172  3935
         Method[] beforeEach = null;
 173  
         try {
 174  3935
             beforeEach = element.findBeforeEachRun();
 175  0
         } catch (final PerfidixMethodCheckException e) {
 176  0
             BENCHRES.addException(e);
 177  3935
         }
 178  3935
         if (beforeEach.length != 0) {
 179  2385
             checkAndExectuteBeforeAfters(obj, BeforeEachRun.class, beforeEach);
 180  
         }
 181  
 
 182  3935
     }
 183  
 
 184  
     /**
 185  
      * Execution of bench method. All data is stored corresponding to the
 186  
      * meters.
 187  
      * 
 188  
      * @param objToExecute
 189  
      *            the instance of the benchclass where the method should be
 190  
      *            executed with.
 191  
      */
 192  
     public void executeBench(final Object objToExecute) {
 193  
 
 194  3930
         final double[] meterResults = new double[METERS_TO_BENCH.size()];
 195  
 
 196  3930
         final Method meth = element.getMethodToBench();
 197  
 
 198  3930
         int meterIndex1 = 0;
 199  3930
         int meterIndex2 = 0;
 200  3930
         for (final AbstractMeter meter : METERS_TO_BENCH) {
 201  6985
             meterResults[meterIndex1] = meter.getValue();
 202  6985
             meterIndex1++;
 203  6985
         }
 204  
 
 205  3930
         final PerfidixMethodInvocationException res = invokeMethod(objToExecute, Bench.class, meth);
 206  
 
 207  3930
         for (final AbstractMeter meter : METERS_TO_BENCH) {
 208  6985
             meterResults[meterIndex2] = meter.getValue() - meterResults[meterIndex2];
 209  6985
             meterIndex2++;
 210  6985
         }
 211  
 
 212  3930
         if (res == null) {
 213  3909
             meterIndex1 = 0;
 214  3909
             for (final AbstractMeter meter : METERS_TO_BENCH) {
 215  6943
                 BENCHRES.addData(element.getMethodToBench(), meter, meterResults[meterIndex1]);
 216  6943
                 meterIndex1++;
 217  6943
             }
 218  
         } else {
 219  21
             BENCHRES.addException(res);
 220  
         }
 221  
 
 222  3930
     }
 223  
 
 224  
     /**
 225  
      * Executing the {@link AfterLastRun}-annotated methods (if still wasn't)
 226  
      * and the {@link AfterEachRun} methods.
 227  
      * 
 228  
      * @param obj
 229  
      *            the object of the class where the bench runs currently in.
 230  
      */
 231  
     public void executeAfterMethods(final Object obj) {
 232  3935
         int runs = RUNS.get(element);
 233  3935
         runs--;
 234  3935
         RUNS.put(element, runs);
 235  
 
 236  
         // invoking once the beforeFirstRun-method
 237  3935
         if (RUNS.get(element) == 0) {
 238  90
             Method[] afterLast = null;
 239  
             try {
 240  90
                 afterLast = element.findAfterLastRun();
 241  0
             } catch (final PerfidixMethodCheckException e) {
 242  0
                 BENCHRES.addException(e);
 243  90
             }
 244  90
             if (afterLast.length != 0) {
 245  55
                 checkAndExectuteBeforeAfters(obj, AfterLastRun.class, afterLast);
 246  
             }
 247  
 
 248  
         }
 249  
 
 250  
         // invoking the beforeEachRun-method
 251  3935
         Method[] afterEach = null;
 252  
         try {
 253  3935
             afterEach = element.findAfterEachRun();
 254  0
         } catch (final PerfidixMethodCheckException e) {
 255  0
             BENCHRES.addException(e);
 256  3935
         }
 257  3935
         if (afterEach != null) {
 258  3935
             checkAndExectuteBeforeAfters(obj, AfterEachRun.class, afterEach);
 259  
         }
 260  
 
 261  3935
     }
 262  
 
 263  
     /**
 264  
      * Checking and executing several before/after methods.
 265  
      * 
 266  
      * @param obj
 267  
      *            on which the execution should take place
 268  
      * @param meth
 269  
      *            to be executed
 270  
      * @param anno
 271  
      *            the related annotation
 272  
      */
 273  
     private void checkAndExectuteBeforeAfters(final Object obj, final Class<? extends Annotation> anno,
 274  
         Method... meths) {
 275  6430
         final PerfidixMethodCheckException checkExc = checkMethod(obj, anno, meths);
 276  6430
         if (checkExc == null) {
 277  6430
             final PerfidixMethodInvocationException invoExc = invokeMethod(obj, anno, meths);
 278  6430
             if (invoExc != null) {
 279  0
                 BENCHRES.addException(invoExc);
 280  
             }
 281  
 
 282  6430
         } else {
 283  0
             BENCHRES.addException(checkExc);
 284  
         }
 285  6430
     }
 286  
 
 287  
     /**
 288  
      * Method to invoke a reflective invokable method.
 289  
      * 
 290  
      * @param obj
 291  
      *            on which the execution takes place
 292  
      * @param meth
 293  
      *            to be executed
 294  
      * @param relatedAnno
 295  
      *            related annotation for the execution
 296  
      * @return {@link PerfidixMethodInvocationException} if invocation fails,
 297  
      *         null otherwise.
 298  
      */
 299  
     public static PerfidixMethodInvocationException invokeMethod(final Object obj,
 300  
         final Class<? extends Annotation> relatedAnno, Method... meths) {
 301  10425
         final Object[] args = {};
 302  17774
         for (Method meth : meths) {
 303  
             try {
 304  7375
                 meth.invoke(obj, args);
 305  0
             } catch (final IllegalArgumentException e) {
 306  0
                 return new PerfidixMethodInvocationException(e, meth, relatedAnno);
 307  0
             } catch (final IllegalAccessException e) {
 308  0
                 return new PerfidixMethodInvocationException(e, meth, relatedAnno);
 309  26
             } catch (final InvocationTargetException e) {
 310  26
                 return new PerfidixMethodInvocationException(e.getCause(), meth, relatedAnno);
 311  7349
             }
 312  
         }
 313  10399
         return null;
 314  
     }
 315  
 
 316  
     /**
 317  
      * Checking a method if it is reflective executable and if the mapping to
 318  
      * the object fits.
 319  
      * 
 320  
      * @param obj
 321  
      *            on which the execution takes place
 322  
      * @param meths
 323  
      *            to be checked
 324  
      * @param anno
 325  
      *            the related annotation for the check
 326  
      * @return {@link PerfidixMethodCheckException} if something is wrong in the
 327  
      *         mapping, null otherwise.
 328  
      */
 329  
     public static PerfidixMethodCheckException checkMethod(final Object obj,
 330  
         final Class<? extends Annotation> anno, Method... meths) {
 331  
 
 332  9950
         for (Method meth : meths) {
 333  
             // check if the class of the object to be executed has the given method
 334  3455
             boolean classMethodCorr = false;
 335  47975
             for (final Method methodOfClass : obj.getClass().getDeclaredMethods()) {
 336  44520
                 if (methodOfClass.equals(meth)) {
 337  3450
                     classMethodCorr = true;
 338  
                 }
 339  
             }
 340  
 
 341  3455
             if (!classMethodCorr) {
 342  5
                 return new PerfidixMethodCheckException(new IllegalStateException(new StringBuilder(
 343  
                     "Object to execute ").append(obj).append(" is not having a Method named ").append(meth)
 344  
                     .append(".").toString()), meth, anno);
 345  
             }
 346  
 
 347  
             // check if the method is reflected executable
 348  3450
             if (!BenchmarkMethod.isReflectedExecutable(meth, anno)) {
 349  5
                 return new PerfidixMethodCheckException(new IllegalAccessException(new StringBuilder(
 350  
                     "Method to execute ").append(meth).append(" is not reflected executable.").toString()),
 351  
                     meth, anno);
 352  
             }
 353  
         }
 354  6495
         return null;
 355  
     }
 356  
 
 357  
 }