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 */
018package org.apache.hadoop.hbase.chaos.factories;
019
020import java.lang.reflect.Constructor;
021import java.util.List;
022import java.util.function.Function;
023import org.apache.hadoop.hbase.TableName;
024import org.apache.hadoop.hbase.chaos.actions.Action;
025import org.slf4j.Logger;
026import org.slf4j.LoggerFactory;
027
028import org.apache.hbase.thirdparty.com.google.common.base.Splitter;
029import org.apache.hbase.thirdparty.com.google.common.collect.Iterables;
030
031public class ConfigurableSlowDeterministicMonkeyFactory extends SlowDeterministicMonkeyFactory {
032
033  private static final Logger LOG =
034    LoggerFactory.getLogger(ConfigurableSlowDeterministicMonkeyFactory.class);
035
036  final static String HEAVY_ACTIONS = "heavy.actions";
037  final static String TABLE_PARAM = "\\$table_name";
038
039  @SuppressWarnings("ImmutableEnumChecker")
040  public enum SupportedTypes {
041    FLOAT(p -> Float.parseFloat(p)),
042    LONG(p -> Long.parseLong(p)),
043    INT(p -> Integer.parseInt(p)),
044    TABLENAME(p -> TableName.valueOf(p));
045
046    final Function<String, Object> converter;
047
048    SupportedTypes(Function<String, Object> converter) {
049      this.converter = converter;
050    }
051
052    Object convert(String param) {
053      return converter.apply(param);
054    }
055  }
056
057  @Override
058  protected Action[] getHeavyWeightedActions() {
059    String actions = this.properties.getProperty(HEAVY_ACTIONS);
060    if (actions == null || actions.isEmpty()) {
061      return super.getHeavyWeightedActions();
062    } else {
063      try {
064        List<String> actionClasses = Splitter.on(';').splitToList(actions);
065        Action[] heavyActions = new Action[actionClasses.size()];
066        int i = 0;
067        for (String action : actionClasses) {
068          heavyActions[i++] = instantiateAction(action);
069        }
070        LOG.info("Created actions {}", (Object[]) heavyActions); // non-varargs call to LOG#info
071        return heavyActions;
072      } catch (Exception e) {
073        LOG.error("Error trying to instantiate heavy actions. Returning null array.", e);
074      }
075      return null;
076    }
077  }
078
079  private Action instantiateAction(String actionString) throws Exception {
080    final String packageName = "org.apache.hadoop.hbase.chaos.actions";
081    Iterable<String> classAndParams =
082      Splitter.on('(').split(Iterables.get(Splitter.on(')').split(actionString), 0));
083    String className = packageName + "." + Iterables.get(classAndParams, 0);
084    String[] params = Splitter.on(',')
085      .splitToStream(
086        Iterables.get(classAndParams, 1).replaceAll(TABLE_PARAM, tableName.getNameAsString()))
087      .toArray(String[]::new);
088    LOG.info("About to instantiate action class: {}; With constructor params: {}", className,
089      params);
090    Class<? extends Action> actionClass = (Class<? extends Action>) Class.forName(className);
091    Constructor<? extends Action>[] constructors =
092      (Constructor<? extends Action>[]) actionClass.getDeclaredConstructors();
093    for (Constructor<? extends Action> c : constructors) {
094      if (c.getParameterCount() != params.length) {
095        continue;
096      }
097      Class[] paramTypes = c.getParameterTypes();
098      Object[] constructorParams = new Object[paramTypes.length];
099      for (int i = 0; i < paramTypes.length; i++) {
100        constructorParams[i] =
101          SupportedTypes.valueOf(paramTypes[i].getSimpleName().toUpperCase()).convert(params[i]);
102      }
103      return c.newInstance(constructorParams);
104    }
105    throw new IllegalArgumentException(
106      "Couldn't find any matching constructor for: " + actionString);
107  }
108}