001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018 019package org.apache.oozie.util.db; 020 021import com.google.common.base.Preconditions; 022import org.apache.oozie.util.XLog; 023 024import java.lang.reflect.InvocationTargetException; 025import java.util.concurrent.ThreadLocalRandom; 026import java.util.concurrent.atomic.AtomicLong; 027 028public class RuntimeExceptionInjector<E extends RuntimeException> { 029 private static final XLog LOG = XLog.getLog(RuntimeExceptionInjector.class); 030 private static final AtomicLong failureCounter = new AtomicLong(0); 031 032 private final Class<E> runtimeExceptionClass; 033 private final int failurePercent; 034 035 public RuntimeExceptionInjector(final Class<E> runtimeExceptionClass, final int failurePercent) { 036 Preconditions.checkArgument(failurePercent <= 100 && failurePercent >= 0, 037 "illegal value for failure %: " + failurePercent); 038 039 this.runtimeExceptionClass = runtimeExceptionClass; 040 this.failurePercent = failurePercent; 041 } 042 043 public void inject(final String errorMessage) { 044 LOG.trace("Trying to inject random failure. [errorMessage={0}]", errorMessage); 045 046 final ThreadLocalRandom random = ThreadLocalRandom.current(); 047 final int randomVal = random.nextInt(0, 100); // range: [0..99] 048 049 if (randomVal < failurePercent) { 050 final long count = failureCounter.incrementAndGet(); 051 LOG.warn("Injecting random failure. [runtimeExceptionClass.name={0};count={1};errorMessage={2}]", 052 runtimeExceptionClass.getName(), count, errorMessage); 053 E injected; 054 055 try { 056 injected = runtimeExceptionClass.getConstructor(String.class).newInstance( 057 "injected random failure #" + count + " ." + errorMessage); 058 } catch (final InstantiationException | IllegalAccessException | InvocationTargetException 059 | NoSuchMethodException outer) { 060 try { 061 LOG.warn("Instantiating without error message. [runtimeExceptionClass.name={0};outer.message={1}]", 062 runtimeExceptionClass.getName(), outer.getMessage()); 063 injected = runtimeExceptionClass.newInstance(); 064 } catch (final InstantiationException | IllegalAccessException inner) { 065 LOG.error("Could not instantiate. [runtimeExceptionClass.name={0};inner.message={1}]", 066 runtimeExceptionClass.getName(), inner.getMessage()); 067 throw new RuntimeException(inner); 068 } 069 070 } 071 072 throw injected; 073 } 074 075 LOG.trace("Did not inject random failure."); 076 } 077}