/*
 * 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.solr.handler.admin;

import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;

import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.CoreAdminParams;
import org.apache.solr.common.params.CoreAdminParams.CoreAdminAction;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.response.SolrQueryResponse;
import org.junit.BeforeClass;
import org.junit.Test;

public class StatsReloadRaceTest extends SolrTestCaseJ4 {

  // to support many times repeating
  static AtomicInteger taskNum = new AtomicInteger();

  @BeforeClass
  public static void beforeTests() throws Exception {
    initCore("solrconfig.xml", "schema.xml");

    XmlDoc docs = new XmlDoc();
    for (int i = 0; i < atLeast(10); i++) {
      docs.xml += doc("id", "" + i,
          "name_s", "" + i);
    }
    assertU(add(docs));
    assertU(commit());
  }

  @Test
  public void testParallelReloadAndStats() throws Exception {

    Random random = random();
    
    for (int i = 0; i < atLeast(random, 2); i++) {

      int asyncId = taskNum.incrementAndGet();

     
      h.getCoreContainer().getMultiCoreHandler().handleRequest(req(
          CommonParams.QT, "/admin/cores",
          CoreAdminParams.ACTION,
          CoreAdminParams.CoreAdminAction.RELOAD.toString(),
          CoreAdminParams.CORE, DEFAULT_TEST_CORENAME,
          "async", "" + asyncId), new SolrQueryResponse());

      boolean isCompleted;
      do {
        if (random.nextBoolean()) {
          requestMetrics(true);
        } else {
          requestCoreStatus();
        }

        isCompleted = checkReloadComlpetion(asyncId);
      } while (!isCompleted);
      requestMetrics(false);
    }
  }

  private void requestCoreStatus() throws Exception {
    SolrQueryResponse rsp = new SolrQueryResponse();
    h.getCoreContainer().getMultiCoreHandler().handleRequest(req(
        CoreAdminParams.ACTION,
        CoreAdminParams.CoreAdminAction.STATUS.toString(),
        "core", DEFAULT_TEST_CORENAME), rsp);
    assertNull(""+rsp.getException(),rsp.getException());

  }

  private boolean checkReloadComlpetion(int asyncId) {
    boolean isCompleted;
    SolrQueryResponse rsp = new SolrQueryResponse();
    h.getCoreContainer().getMultiCoreHandler().handleRequest(req(
        CoreAdminParams.ACTION,
        CoreAdminParams.CoreAdminAction.REQUESTSTATUS.toString(),
        CoreAdminParams.REQUESTID, "" + asyncId), rsp);
    
    @SuppressWarnings("unchecked")
    List<Object> statusLog = rsp.getValues().getAll(CoreAdminAction.STATUS.name());

    assertFalse("expect status check w/o error, got:" + statusLog,
                              statusLog.contains(CoreAdminHandler.FAILED));

    isCompleted = statusLog.contains(CoreAdminHandler.COMPLETED);
    return isCompleted;
  }

  private void requestMetrics(boolean softFail) throws Exception {
    SolrQueryResponse rsp = new SolrQueryResponse();
    String registry = "solr.core." + h.coreName;
    String key = "SEARCHER.searcher.indexVersion";
    boolean found = false;
    int count = 10;
    while (!found && count-- > 0) {
      h.getCoreContainer().getRequestHandler("/admin/metrics").handleRequest(
          req("prefix", "SEARCHER", "registry", registry, "compact", "true"), rsp);

      NamedList values = rsp.getValues();
      NamedList metrics = (NamedList)values.get("metrics");
      metrics = (NamedList)metrics.get(registry);
      // this is not guaranteed to exist right away after core reload - there's a
      // small window between core load and before searcher metrics are registered
      // so we may have to check a few times
      if (metrics.get(key) != null) {
        found = true;
        assertTrue(metrics.get(key) instanceof Long);
        break;
      } else {
        Thread.sleep(500);
      }
    }
    if (softFail && !found) {
      return;
    }
    assertTrue("Key " + key + " not found in registry " + registry, found);
  }

}

@@ -4191,118 +4191,8 @@
 ();%0A
-      NamedList metrics = (NamedList)values.get(%22metrics%22);%0A      metrics = (NamedList)metrics.get(registry);%0A
     
@@ -4396,16 +4396,380 @@
 ew times
+, and then fail softly if reload is not complete yet%0A      NamedList metrics = (NamedList)values.get(%22metrics%22);%0A      if (metrics == null) %7B%0A        if (softFail) %7B%0A          return;%0A        %7D else %7B%0A          fail(%22missing 'metrics' element in handler's output: %22 + values.asMap(5).toString());%0A        %7D%0A      %7D%0A      metrics = (NamedList)metrics.get(registry);
 %0A      i

Fix an occasional NPE.