/**
 * 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.hadoop.hive.metastore;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;

import org.apache.hadoop.hive.conf.HiveConf;
import org.apache.hadoop.hive.conf.HiveConf.ConfVars;
import org.apache.hive.service.server.HiveServer2;
import org.junit.Before;
import org.junit.Test;

public class TestServerSpecificConfig {

  private static URL oldDefaultHiveSite = HiveConf.getHiveSiteLocation();

  /**
   * Verify if appropriate server configuration (metastore, hiveserver2) get
   * loaded when the embedded clients are loaded
   *
   * Checks values used in the configs used for testing.
   *
   * @throws IOException
   * @throws Throwable
   */
  @Test
  public void testServerConfigsEmbeddedMetastore() throws IOException, Throwable {

    // set hive-site.xml to default hive-site.xml that has embedded metastore
    HiveConf.setHiveSiteLocation(oldDefaultHiveSite);

    HiveConf conf = new HiveConf();

    // check config properties expected with embedded metastore client
    assertTrue(HiveConf.isLoadMetastoreConfig());
    assertEquals("from.hivemetastore-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.override"));

    assertEquals("from.hivemetastore-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.metastoresite"));

    assertEquals("from.hive-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.hivesite"));

    // verify that hiveserver2 config is not loaded
    assertFalse(HiveConf.isLoadHiveServer2Config());
    assertNull(conf.get("hive.dummyparam.test.server.specific.config.hiveserver2site"));

    // check if hiveserver2 config gets loaded when HS2 is started
    new HiveServer2();
    conf = new HiveConf();
    verifyHS2ConfParams(conf);

    assertEquals("from.hivemetastore-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.metastoresite"));
  }

  private void verifyHS2ConfParams(HiveConf conf) {
    assertTrue(HiveConf.isLoadHiveServer2Config());
    assertEquals("from.hiveserver2-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.override"));

    assertEquals("from.hiveserver2-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.hiveserver2site"));

    assertEquals("from.hive-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.hivesite"));
  }

  /**
   * Ensure that system properties still get precedence. Config params set as
   * -hiveconf on commandline get set as system properties They should have the
   * final say
   */
  @Test
  public void testSystemPropertyPrecedence() {
    // Using property defined in HiveConf.ConfVars to test System property
    // overriding
    final String OVERRIDE_KEY = "hive.conf.restricted.list";
    try {
      HiveConf.setHiveSiteLocation(oldDefaultHiveSite);
      System.setProperty(OVERRIDE_KEY, "from.sysprop");
      HiveConf conf = new HiveConf();
      // ensure metatore site.xml does not get to override this
      assertEquals("from.sysprop", conf.get(OVERRIDE_KEY));

      // get HS2 site.xml loaded
      new HiveServer2();
      conf = new HiveConf();
      assertTrue(HiveConf.isLoadHiveServer2Config());
      // ensure hiveserver2 site.xml does not get to override this
      assertEquals("from.sysprop", conf.get(OVERRIDE_KEY));

    } finally {
      System.getProperties().remove(OVERRIDE_KEY);
    }
  }

  @Before
  public void resetDefaults() throws SecurityException, IllegalArgumentException,
      NoSuchFieldException, IllegalAccessException {
    // re-set the static variables in HiveConf to default values

    // set load server conf booleans to false
    HiveConf.setLoadMetastoreConfig(false);
    HiveConf.setLoadHiveServer2Config(false);

  }

  /**
   * Test to ensure that HiveConf does not try to load hivemetastore-site.xml,
   * when remote metastore is used.
   *
   * @throws IOException
   * @throws Throwable
   */
  @Test
  public void testHiveMetastoreRemoteConfig() throws IOException, Throwable {
    // switch to hive-site.xml with remote metastore
    setHiveSiteWithRemoteMetastore();

    // Set HiveConf statics to default values
    resetDefaults();

    // create hiveconf again to run initialization code, to see if value changes
    HiveConf conf = new HiveConf();

    // check the properties expected in hive client without metastore
    verifyMetastoreConfNotLoaded(conf);
    assertEquals("from.hive-site.xml",
        conf.get("hive.dummyparam.test.server.specific.config.override"));

    // get HS2 site.xml loaded
    new HiveServer2();
    conf = new HiveConf();
    verifyHS2ConfParams(conf);
    verifyMetastoreConfNotLoaded(conf);
  }

  private void verifyMetastoreConfNotLoaded(HiveConf conf) {
    assertFalse(HiveConf.isLoadMetastoreConfig());
    assertNull(conf.get("hive.dummyparam.test.server.specific.config.metastoresite"));
  }

  /**
   * Set new hive-site.xml file location that has remote metastore config
   *
   * @throws IOException
   */
  private void setHiveSiteWithRemoteMetastore() throws IOException {
    // new *hive-site.xml file
    String newConfFile = System.getProperty("test.tmp.dir") + File.separator
        + this.getClass().getSimpleName() + "hive-site.xml";

    // create a new conf file, using contents from current one
    // modifying the meastore.uri property
    File hiveSite = new File(newConfFile);
    FileOutputStream out = new FileOutputStream(hiveSite);
    HiveConf.setHiveSiteLocation(oldDefaultHiveSite);
    HiveConf defaultHiveConf = new HiveConf();
    defaultHiveConf.setVar(ConfVars.METASTOREURIS, "dummyvalue");
    // reset to the hive-site.xml values for following param
    defaultHiveConf.set("hive.dummyparam.test.server.specific.config.override",
        "from.hive-site.xml");
    defaultHiveConf.unset("hive.dummyparam.test.server.specific.config.metastoresite");
    defaultHiveConf.writeXml(out);

    // set the new hive-site.xml
    HiveConf.setHiveSiteLocation(hiveSite.toURI().toURL());
  }

}