HdfsCompatCommand.java

/**
 * 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
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * 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.hadoop.fs.compat.common;

import org.apache.hadoop.classification.VisibleForTesting;
import org.apache.hadoop.fs.compat.suites.HdfsCompatSuiteForAll;
import org.apache.hadoop.fs.compat.suites.HdfsCompatSuiteForShell;
import org.apache.hadoop.fs.compat.suites.HdfsCompatSuiteForTpcds;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.Path;

import java.io.IOException;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;

public class HdfsCompatCommand {
  private final Path uri;
  private final String suiteName;
  private final Configuration conf;
  private HdfsCompatSuite suite;
  private HdfsCompatApiScope api;
  private HdfsCompatShellScope shell;

  public HdfsCompatCommand(String uri, String suiteName, Configuration conf) {
    this.uri = new Path(uri);
    this.suiteName = suiteName.toLowerCase();
    this.conf = conf;
  }

  public void initialize() throws ReflectiveOperationException, IOException {
    initSuite();
    HdfsCompatEnvironment env = new HdfsCompatEnvironment(uri, conf);
    env.init();
    if (hasApiCase()) {
      api = new HdfsCompatApiScope(env, suite);
    }
    if (hasShellCase()) {
      shell = new HdfsCompatShellScope(env, suite);
    }
  }

  public HdfsCompatReport apply() throws Exception {
    HdfsCompatReport report = new HdfsCompatReport(uri.toString(), suite);
    if (api != null) {
      report.merge(api.apply());
    }
    if (shell != null) {
      report.merge(shell.apply());
    }
    return report;
  }

  private void initSuite() throws ReflectiveOperationException {
    Map<String, HdfsCompatSuite> defaultSuites = getDefaultSuites();
    this.suite = defaultSuites.getOrDefault(this.suiteName, null);
    if (this.suite != null) {
      return;
    }
    String key = "hadoop.compatibility.suite." + this.suiteName + ".classname";
    final String suiteClassName = conf.get(key, null);
    if ((suiteClassName == null) || suiteClassName.isEmpty()) {
      throw new HdfsCompatIllegalArgumentException(
          "cannot get class name for suite " + this.suiteName +
              ", configuration " + key + " is not properly set.");
    }
    Constructor<?> ctor = suiteClassName.getClass().getConstructor();
    ctor.setAccessible(true);
    Object suiteObj = ctor.newInstance();
    if (suiteObj instanceof HdfsCompatSuite) {
      this.suite = (HdfsCompatSuite) suiteObj;
    } else {
      throw new HdfsCompatIllegalArgumentException(
          "class name " + suiteClassName + " must be an" +
              " implementation of " + HdfsCompatSuite.class.getName());
    }
    if (suite.getSuiteName() == null || suite.getSuiteName().isEmpty()) {
      throw new HdfsCompatIllegalArgumentException(
          "suite " + suiteClassName + " suiteName is empty");
    }
    for (HdfsCompatSuite defaultSuite : defaultSuites.values()) {
      if (suite.getSuiteName().equalsIgnoreCase(defaultSuite.getSuiteName())) {
        throw new HdfsCompatIllegalArgumentException(
            "suite " + suiteClassName + " suiteName" +
                " conflicts with default suite " + defaultSuite.getSuiteName());
      }
    }
    if (!hasApiCase() && !hasShellCase()) {
      throw new HdfsCompatIllegalArgumentException(
          "suite " + suiteClassName + " is empty for both API and SHELL");
    }
  }

  private boolean hasApiCase() {
    return (suite.getApiCases() != null) &&
        (suite.getApiCases().length > 0);
  }

  private boolean hasShellCase() {
    return (suite.getShellCases() != null) &&
        (suite.getShellCases().length > 0);
  }

  @VisibleForTesting
  protected Map<String, HdfsCompatSuite> getDefaultSuites() {
    Map<String, HdfsCompatSuite> defaultSuites = new HashMap<>();
    defaultSuites.put("all", new HdfsCompatSuiteForAll());
    defaultSuites.put("shell", new HdfsCompatSuiteForShell());
    defaultSuites.put("tpcds", new HdfsCompatSuiteForTpcds());
    return defaultSuites;
  }
}