blob: e91e21933a3ccd9b8895f5088eb02701420c97ec [file] [log] [blame]
/*
* 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.core;
import java.io.File;
import java.io.FileOutputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarOutputStream;
import java.util.regex.Pattern;
import com.google.common.base.Throwables;
import com.google.common.collect.ImmutableMap;
import org.apache.commons.exec.OS;
import org.apache.commons.io.FileUtils;
import org.apache.lucene.util.IOUtils;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
import org.apache.solr.handler.admin.CollectionsHandler;
import org.apache.solr.handler.admin.ConfigSetsHandler;
import org.apache.solr.handler.admin.CoreAdminHandler;
import org.apache.solr.handler.admin.InfoHandler;
import org.junit.AfterClass;
import org.junit.Assume;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xml.sax.SAXParseException;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsInstanceOf.instanceOf;
import static org.junit.matchers.JUnitMatchers.containsString;
public class TestCoreContainer extends SolrTestCaseJ4 {
private static String oldSolrHome;
private static final String SOLR_HOME_PROP = "solr.solr.home";
@BeforeClass
public static void beforeClass() throws Exception {
oldSolrHome = System.getProperty(SOLR_HOME_PROP);
System.setProperty("configsets", getFile("solr/configsets").getAbsolutePath());
}
@AfterClass
public static void afterClass() {
if (oldSolrHome != null) {
System.setProperty(SOLR_HOME_PROP, oldSolrHome);
} else {
System.clearProperty(SOLR_HOME_PROP);
}
}
private CoreContainer init(String xml) throws Exception {
Path solrHomeDirectory = createTempDir();
return init(solrHomeDirectory, xml);
}
private CoreContainer init(Path homeDirectory, String xml) throws Exception {
CoreContainer ret = new CoreContainer(SolrXmlConfig.fromString(homeDirectory, xml));
ret.load();
return ret;
}
public void testSolrHomeAndResourceLoader() throws Exception {
// regardless of what sys prop may be set, the CoreContainer's init arg should be the definitive
// solr home -- and nothing i nthe call stack should be "setting" the sys prop to make that work...
final Path fakeSolrHome = createTempDir().toAbsolutePath();
System.setProperty(SOLR_HOME_PROP, fakeSolrHome.toString());
final Path realSolrHome = createTempDir().toAbsolutePath();
final CoreContainer cc = init(realSolrHome, CONFIGSETS_SOLR_XML);
try {
// instance dir & resource loader for the CC
assertEquals(realSolrHome.toString(), cc.getSolrHome());
assertEquals(realSolrHome, cc.getResourceLoader().getInstancePath());
} finally {
cc.shutdown();
}
assertEquals("Nothing in solr should be overriding the solr home sys prop in order to work!",
fakeSolrHome.toString(),
System.getProperty(SOLR_HOME_PROP));
}
@Test
public void testShareSchema() throws Exception {
System.setProperty("shareSchema", "true");
CoreContainer cores = init(CONFIGSETS_SOLR_XML);
try {
SolrCore core1 = cores.create("core1", ImmutableMap.of("configSet", "minimal"));
SolrCore core2 = cores.create("core2", ImmutableMap.of("configSet", "minimal"));
assertSame(core1.getLatestSchema(), core2.getLatestSchema());
} finally {
cores.shutdown();
System.clearProperty("shareSchema");
}
}
@Test
public void testReloadSequential() throws Exception {
final CoreContainer cc = init(CONFIGSETS_SOLR_XML);
try {
cc.create("core1", ImmutableMap.of("configSet", "minimal"));
cc.reload("core1");
cc.reload("core1");
cc.reload("core1");
cc.reload("core1");
} finally {
cc.shutdown();
}
}
private static class TestReloadThread extends Thread {
private final CoreContainer cc;
TestReloadThread(CoreContainer cc) {
this.cc = cc;
}
@Override
public void run() {
cc.reload("core1");
}
}
@Test
public void testReloadThreaded() throws Exception {
final CoreContainer cc = init(CONFIGSETS_SOLR_XML);
cc.create("core1", ImmutableMap.of("configSet", "minimal"));
List<Thread> threads = new ArrayList<>();
int numThreads = 4;
for (int i = 0; i < numThreads; i++) {
threads.add(new TestReloadThread(cc));
}
for (Thread thread : threads) {
thread.start();
}
for (Thread thread : threads) {
thread.join();
}
cc.shutdown();
}
private static class TestCreateThread extends Thread {
final CoreContainer cc;
final String coreName;
boolean foundExpectedError = false;
SolrCore core = null;
TestCreateThread(CoreContainer cc, String coreName) {
this.cc = cc;
this.coreName = coreName;
}
@Override
public void run() {
try {
core = cc.create(coreName, ImmutableMap.of("configSet", "minimal"));
} catch (SolrException e) {
String msg = e.getMessage();
foundExpectedError = msg.contains("Already creating a core with name") || msg.contains("already exists");
}
}
int verifyMe() {
if (foundExpectedError) {
assertNull("failed create should have returned null for core", core);
return 0;
} else {
assertNotNull("Core should not be null if there was no error", core);
return 1;
}
}
}
@Test
public void testCreateThreaded() throws Exception {
final CoreContainer cc = init(CONFIGSETS_SOLR_XML);
final int NUM_THREADS = 3;
// Try this a few times to increase the chances of failure.
for (int idx = 0; idx < 3; ++idx) {
TestCreateThread[] threads = new TestCreateThread[NUM_THREADS];
// Let's add a little stress in here by using the same core name each
// time around. The final unload in the loop should delete the core and
// allow the next time around to succeed.
// This also checks the bookkeeping in CoreContainer.create
// that prevents multiple simultaneous creations,
// currently "inFlightCreations"
String testName = "coreToTest";
for (int thread = 0; thread < NUM_THREADS; ++thread) {
threads[thread] = new TestCreateThread(cc, testName);
}
// only one of these should succeed.
for (int thread = 0; thread < NUM_THREADS; ++thread) {
threads[thread].start();
}
for (int thread = 0; thread < NUM_THREADS; ++thread) {
threads[thread].join();
}
// We can't guarantee that which thread failed, so go find it
int goodCount = 0;
for (int thread = 0; thread < NUM_THREADS; ++thread) {
goodCount += threads[thread].verifyMe();
}
assertEquals("Only one create should have succeeded", 1, goodCount);
// Check bookkeeping by removing and creating the core again, making sure
// we didn't leave the record of trying to create this core around.
// NOTE: unloading the core closes it too.
cc.unload(testName, true, true, true);
cc.create(testName, ImmutableMap.of("configSet", "minimal"));
// This call should fail with a different error because the core was
// created successfully.
SolrException thrown = expectThrows(SolrException.class, () ->
cc.create(testName, ImmutableMap.of("configSet", "minimal")));
assertTrue("Should have 'already exists' error", thrown.getMessage().contains("already exists"));
cc.unload(testName, true, true, true);
}
cc.shutdown();
}
@Test
public void testNoCores() throws Exception {
CoreContainer cores = init(CONFIGSETS_SOLR_XML);
try {
//assert zero cores
assertEquals("There should not be cores", 0, cores.getCores().size());
//add a new core
cores.create("core1", ImmutableMap.of("configSet", "minimal"));
//assert one registered core
assertEquals("There core registered", 1, cores.getCores().size());
cores.unload("core1");
//assert cero cores
assertEquals("There should not be cores", 0, cores.getCores().size());
// try and remove a core that does not exist
SolrException thrown = expectThrows(SolrException.class, () -> {
cores.unload("non_existent_core");
});
assertThat(thrown.getMessage(), containsString("Cannot unload non-existent core [non_existent_core]"));
// try and remove a null core
thrown = expectThrows(SolrException.class, () -> {
cores.unload(null);
});
assertThat(thrown.getMessage(), containsString("Cannot unload non-existent core [null]"));
} finally {
cores.shutdown();
}
}
@Test
public void testLogWatcherEnabledByDefault() throws Exception {
CoreContainer cc = init("<solr></solr>");
try {
assertNotNull(cc.getLogging());
}
finally {
cc.shutdown();
}
}
@Test
public void testDeleteBadCores() throws Exception {
MockCoresLocator cl = new MockCoresLocator();
Path solrHome = createTempDir();
System.setProperty("configsets", getFile("solr/configsets").getAbsolutePath());
final CoreContainer cc = new CoreContainer(SolrXmlConfig.fromString(solrHome, CONFIGSETS_SOLR_XML), cl);
Path corePath = solrHome.resolve("badcore");
CoreDescriptor badcore = new CoreDescriptor("badcore", corePath, cc,
"configSet", "nosuchconfigset");
cl.add(badcore);
try {
cc.load();
assertThat(cc.getCoreInitFailures().size(), is(1));
assertThat(cc.getCoreInitFailures().get("badcore").exception.getMessage(), containsString("nosuchconfigset"));
cc.unload("badcore", true, true, true);
assertThat(cc.getCoreInitFailures().size(), is(0));
// can we create the core now with a good config?
SolrCore core = cc.create("badcore", ImmutableMap.of("configSet", "minimal"));
assertThat(core, not(nullValue()));
}
finally {
cc.shutdown();
}
}
@Test
public void testClassLoaderHierarchy() throws Exception {
final CoreContainer cc = init(CONFIGSETS_SOLR_XML);
try {
ClassLoader sharedLoader = cc.loader.getClassLoader();
ClassLoader baseLoader = SolrResourceLoader.class.getClassLoader();
assertSame(baseLoader, sharedLoader.getParent());
SolrCore core1 = cc.create("core1", ImmutableMap.of("configSet", "minimal"));
ClassLoader coreLoader = core1.getResourceLoader().getClassLoader();
assertSame(sharedLoader, coreLoader.getParent());
} finally {
cc.shutdown();
}
}
@Test
public void testSharedLib() throws Exception {
Path tmpRoot = createTempDir("testSharedLib");
File lib = new File(tmpRoot.toFile(), "lib");
lib.mkdirs();
try (JarOutputStream jar1 = new JarOutputStream(new FileOutputStream(new File(lib, "jar1.jar")))) {
jar1.putNextEntry(new JarEntry("defaultSharedLibFile"));
jar1.closeEntry();
}
File customLib = new File(tmpRoot.toFile(), "customLib");
customLib.mkdirs();
try (JarOutputStream jar2 = new JarOutputStream(new FileOutputStream(new File(customLib, "jar2.jar")))) {
jar2.putNextEntry(new JarEntry("customSharedLibFile"));
jar2.closeEntry();
}
File customLib2 = new File(tmpRoot.toFile(), "customLib2");
customLib2.mkdirs();
try (JarOutputStream jar3 = new JarOutputStream(new FileOutputStream(new File(customLib2, "jar3.jar")))) {
jar3.putNextEntry(new JarEntry("jar3File"));
jar3.closeEntry();
}
final CoreContainer cc1 = init(tmpRoot, "<solr></solr>");
try {
cc1.loader.openResource("defaultSharedLibFile").close();
} finally {
cc1.shutdown();
}
// Explicitly declaring 'lib' makes no change compared to the default
final CoreContainer cc2 = init(tmpRoot, "<solr><str name=\"sharedLib\">lib</str></solr>");
try {
cc2.loader.openResource("defaultSharedLibFile").close();
} finally {
cc2.shutdown();
}
// custom lib folder, added to path in addition to default 'lib' folder
final CoreContainer cc3 = init(tmpRoot, "<solr><str name=\"sharedLib\">customLib</str></solr>");
try {
cc3.loader.openResource("defaultSharedLibFile").close();
cc3.loader.openResource("customSharedLibFile").close();
} finally {
cc3.shutdown();
}
// Comma separated list of lib folders
final CoreContainer cc4 = init(tmpRoot, "<solr><str name=\"sharedLib\">customLib, customLib2</str></solr>");
try {
cc4.loader.openResource("defaultSharedLibFile").close();
cc4.loader.openResource("customSharedLibFile").close();
cc4.loader.openResource("jar3File").close();
} finally {
cc4.shutdown();
}
}
private static final String CONFIGSETS_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<solr>\n" +
"<str name=\"configSetBaseDir\">${configsets:configsets}</str>\n" +
"<str name=\"shareSchema\">${shareSchema:false}</str>\n" +
"</solr>";
private static final String ALLOW_PATHS_SOLR_XML ="<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<solr>\n" +
"<str name=\"allowPaths\">${solr.allowPaths:}</str>\n" +
"</solr>";
private static final String CUSTOM_HANDLERS_SOLR_XML = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" +
"<solr>" +
" <str name=\"collectionsHandler\">" + CustomCollectionsHandler.class.getName() + "</str>" +
" <str name=\"infoHandler\">" + CustomInfoHandler.class.getName() + "</str>" +
" <str name=\"adminHandler\">" + CustomCoreAdminHandler.class.getName() + "</str>" +
" <str name=\"configSetsHandler\">" + CustomConfigSetsHandler.class.getName() + "</str>" +
"</solr>";
public static class CustomCollectionsHandler extends CollectionsHandler {
public CustomCollectionsHandler(CoreContainer cc) {
super(cc);
}
}
public static class CustomInfoHandler extends InfoHandler {
public CustomInfoHandler(CoreContainer cc) {
super(cc);
}
}
public static class CustomCoreAdminHandler extends CoreAdminHandler {
public CustomCoreAdminHandler(CoreContainer cc) {
super(cc);
}
}
public static class CustomConfigSetsHandler extends ConfigSetsHandler {
public CustomConfigSetsHandler(CoreContainer cc) {
super(cc);
}
}
@Test
public void assertAllowPathFromSolrXml() throws Exception {
Assume.assumeFalse(OS.isFamilyWindows());
System.setProperty("solr.allowPaths", "/var/solr");
CoreContainer cc = init(ALLOW_PATHS_SOLR_XML);
cc.assertPathAllowed(Paths.get("/var/solr/foo"));
try {
cc.assertPathAllowed(Paths.get("/tmp"));
fail("Path /tmp should not be allowed");
} catch(SolrException e) {
/* Ignore */
} finally {
cc.shutdown();
System.clearProperty("solr.allowPaths");
}
}
@Test
public void assertAllowPathFromSolrXmlWin() throws Exception {
Assume.assumeTrue(OS.isFamilyWindows());
System.setProperty("solr.allowPaths", "C:\\solr");
CoreContainer cc = init(ALLOW_PATHS_SOLR_XML);
cc.assertPathAllowed(Paths.get("C:\\solr\\foo"));
try {
cc.assertPathAllowed(Paths.get("C:\\tmp"));
fail("Path C:\\tmp should not be allowed");
} catch(SolrException e) {
/* Ignore */
} finally {
cc.shutdown();
System.clearProperty("solr.allowPaths");
}
}
@Test
public void assertAllowPath() {
Assume.assumeFalse(OS.isFamilyWindows());
assertPathAllowed("/var/solr/foo");
assertPathAllowed("/var/log/../solr/foo");
assertPathAllowed("relative");
assertPathBlocked("../../false");
assertPathBlocked("./../../false");
assertPathBlocked("/var/solr/../../etc");
}
@Test
public void assertAllowPathWindows() {
Assume.assumeTrue(OS.isFamilyWindows());
assertPathAllowed("C:\\var\\solr\\foo");
assertPathAllowed("C:\\var\\log\\..\\solr\\foo");
assertPathAllowed("relative");
assertPathBlocked("..\\..\\false");
assertPathBlocked(".\\../\\..\\false");
assertPathBlocked("C:\\var\\solr\\..\\..\\etc");
// UNC paths are always blocked
assertPathBlocked("\\\\unc-server\\share\\path");
}
private static final Set<Path> ALLOWED_PATHS = new HashSet<>(Collections.singletonList(Paths.get("/var/solr")));
private static final Set<Path> ALLOWED_PATHS_WIN = new HashSet<>(Collections.singletonList(Paths.get("C:\\var\\solr")));
private void assertPathBlocked(String path) {
try {
SolrPaths.assertPathAllowed(Paths.get(path), OS.isFamilyWindows() ? ALLOWED_PATHS_WIN : ALLOWED_PATHS);
fail("Path " + path + " sould have been blocked.");
} catch (SolrException e) { /* Expected */ }
}
private void assertPathAllowed(String path) {
SolrPaths.assertPathAllowed(Paths.get(path), OS.isFamilyWindows() ? ALLOWED_PATHS_WIN : ALLOWED_PATHS);
}
@Test
public void testCustomHandlers() throws Exception {
CoreContainer cc = init(CUSTOM_HANDLERS_SOLR_XML);
try {
assertThat(cc.getCollectionsHandler(), is(instanceOf(CustomCollectionsHandler.class)));
assertThat(cc.getInfoHandler(), is(instanceOf(CustomInfoHandler.class)));
assertThat(cc.getMultiCoreHandler(), is(instanceOf(CustomCoreAdminHandler.class)));
}
finally {
cc.shutdown();
}
}
private static class MockCoresLocator implements CoresLocator {
List<CoreDescriptor> cores = new ArrayList<>();
void add(CoreDescriptor cd) {
cores.add(cd);
}
@Override
public void create(CoreContainer cc, CoreDescriptor... coreDescriptors) {
// noop
}
@Override
public void persist(CoreContainer cc, CoreDescriptor... coreDescriptors) {
}
@Override
public void delete(CoreContainer cc, CoreDescriptor... coreDescriptors) {
}
@Override
public void rename(CoreContainer cc, CoreDescriptor oldCD, CoreDescriptor newCD) {
}
@Override
public void swap(CoreContainer cc, CoreDescriptor cd1, CoreDescriptor cd2) {
}
@Override
public List<CoreDescriptor> discover(CoreContainer cc) {
return cores;
}
}
@Test
public void testCoreInitFailuresFromEmptyContainer() throws Exception {
// reused state
Map<String,CoreContainer.CoreLoadFailure> failures = null;
Collection<String> cores = null;
Exception fail = null;
// ----
// init the CoreContainer
CoreContainer cc = init(CONFIGSETS_SOLR_XML);
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 0, cores.size());
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 0, failures.size());
// -----
// try to add a collection with a configset that doesn't exist
ignoreException(Pattern.quote("bogus_path"));
SolrException thrown = expectThrows(SolrException.class, () -> {
cc.create("bogus", ImmutableMap.of("configSet", "bogus_path"));
});
Throwable rootCause = Throwables.getRootCause(thrown);
assertTrue("init exception doesn't mention bogus dir: " + rootCause.getMessage(),
0 < rootCause.getMessage().indexOf("bogus_path"));
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 0, cores.size());
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 1, failures.size());
fail = failures.get("bogus").exception;
assertNotNull("null failure for test core", fail);
assertTrue("init failure doesn't mention problem: " + fail.getMessage(),
0 < fail.getMessage().indexOf("bogus_path"));
// check that we get null accessing a non-existent core
assertNull(cc.getCore("does_not_exist"));
// check that we get a 500 accessing the core with an init failure
thrown = expectThrows(SolrException.class, () -> {
SolrCore c = cc.getCore("bogus");
});
assertEquals(500, thrown.code());
String cause = Throwables.getRootCause(thrown).getMessage();
assertTrue("getCore() ex cause doesn't mention init fail: " + cause,
0 < cause.indexOf("bogus_path"));
cc.shutdown();
}
@Test
public void testCoreInitFailuresOnReload() throws Exception {
// reused state
Map<String,CoreContainer.CoreLoadFailure> failures = null;
Collection<String> cores = null;
Exception fail = null;
// -----
// init the CoreContainer with the mix of ok/bad cores
MockCoresLocator cl = new MockCoresLocator();
Path solrHome = createTempDir();
System.setProperty("configsets", getFile("solr/configsets").getAbsolutePath());
final CoreContainer cc = new CoreContainer(SolrXmlConfig.fromString(solrHome, CONFIGSETS_SOLR_XML), cl);
cl.add(new CoreDescriptor("col_ok", solrHome.resolve("col_ok"), cc,
"configSet", "minimal"));
cl.add(new CoreDescriptor("col_bad", solrHome.resolve("col_bad"), cc,
"configSet", "bad-mergepolicy"));
cc.load();
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 1, cores.size());
assertTrue("col_ok not found", cores.contains("col_ok"));
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 1, failures.size());
fail = failures.get("col_bad").exception;
assertNotNull("null failure for test core", fail);
assertTrue("init failure doesn't mention problem: " + fail.getMessage(),
0 < fail.getMessage().indexOf("DummyMergePolicy"));
// check that we get null accessing a non-existent core
assertNull(cc.getCore("does_not_exist"));
assertFalse(cc.isLoaded("does_not_exist"));
// check that we get a 500 accessing the core with an init failure
SolrException thrown = expectThrows(SolrException.class, () -> {
SolrCore c = cc.getCore("col_bad");
});
assertEquals(500, thrown.code());
String cause = thrown.getCause().getCause().getMessage();
assertTrue("getCore() ex cause doesn't mention init fail: " + cause, 0 < cause.indexOf("DummyMergePolicy"));
// -----
// "fix" the bad collection
FileUtils.copyFile(getFile("solr/collection1/conf/solrconfig-defaults.xml"),
FileUtils.getFile(cc.getSolrHome(), "col_bad", "conf", "solrconfig.xml"));
FileUtils.copyFile(getFile("solr/collection1/conf/schema-minimal.xml"),
FileUtils.getFile(cc.getSolrHome(), "col_bad", "conf", "schema.xml"));
cc.create("col_bad", ImmutableMap.of());
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 2, cores.size());
assertTrue("col_ok not found", cores.contains("col_ok"));
assertTrue(cc.isLoaded("col_ok"));
assertTrue("col_bad not found", cores.contains("col_bad"));
assertTrue(cc.isLoaded("col_bad"));
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 0, failures.size());
// -----
// try to add a collection with a path that doesn't exist
ignoreException(Pattern.quote("bogus_path"));
thrown = expectThrows(SolrException.class, () -> {
cc.create("bogus", ImmutableMap.of("configSet", "bogus_path"));
});
assertTrue("init exception doesn't mention bogus dir: " + thrown.getCause().getCause().getMessage(),
0 < thrown.getCause().getCause().getMessage().indexOf("bogus_path"));
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 2, cores.size());
assertTrue("col_ok not found", cores.contains("col_ok"));
assertTrue("col_bad not found", cores.contains("col_bad"));
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 1, failures.size());
fail = failures.get("bogus").exception;
assertNotNull("null failure for test core", fail);
assertTrue("init failure doesn't mention problem: " + fail.getMessage(),
0 < fail.getMessage().indexOf("bogus_path"));
// check that we get null accessing a non-existent core
assertNull(cc.getCore("does_not_exist"));
// check that we get a 500 accessing the core with an init failure
thrown = expectThrows(SolrException.class, () -> {
SolrCore c = cc.getCore("bogus");
});
assertEquals(500, thrown.code());
cause = thrown.getCause().getMessage();
assertTrue("getCore() ex cause doesn't mention init fail: " + cause,
0 < cause.indexOf("bogus_path"));
// -----
// break col_bad's config and try to RELOAD to add failure
final long col_bad_old_start = getCoreStartTime(cc, "col_bad");
FileUtils.write
(FileUtils.getFile(cc.getSolrHome(), "col_bad", "conf", "solrconfig.xml"),
"This is giberish, not valid XML <",
IOUtils.UTF_8);
ignoreException(Pattern.quote("SAX"));
thrown = expectThrows(SolrException.class,
"corrupt solrconfig.xml failed to trigger exception from reload",
() -> { cc.reload("col_bad"); });
Throwable rootException = getWrappedException(thrown);
assertTrue("We're supposed to have a wrapped SAXParserException here, but we don't",
rootException instanceof SAXParseException);
SAXParseException se = (SAXParseException) rootException;
assertTrue("reload exception doesn't refer to slrconfig.xml " + se.getSystemId(),
0 < se.getSystemId().indexOf("solrconfig.xml"));
assertEquals("Failed core reload should not have changed start time",
col_bad_old_start, getCoreStartTime(cc, "col_bad"));
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 2, cores.size());
assertTrue("col_ok not found", cores.contains("col_ok"));
assertTrue("col_bad not found", cores.contains("col_bad"));
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 2, failures.size());
Throwable ex = getWrappedException(failures.get("col_bad").exception);
assertNotNull("null failure for test core", ex);
assertTrue("init failure isn't SAXParseException",
ex instanceof SAXParseException);
SAXParseException saxEx = (SAXParseException) ex;
assertTrue("init failure doesn't mention problem: " + saxEx.toString(), saxEx.getSystemId().contains("solrconfig.xml"));
// ----
// fix col_bad's config (again) and RELOAD to fix failure
FileUtils.copyFile(getFile("solr/collection1/conf/solrconfig-defaults.xml"),
FileUtils.getFile(cc.getSolrHome(), "col_bad", "conf", "solrconfig.xml"));
cc.reload("col_bad");
assertTrue("Core reload should have changed start time",
col_bad_old_start < getCoreStartTime(cc, "col_bad"));
// check that we have the cores we expect
cores = cc.getLoadedCoreNames();
assertNotNull("core names is null", cores);
assertEquals("wrong number of cores", 2, cores.size());
assertTrue("col_ok not found", cores.contains("col_ok"));
assertTrue("col_bad not found", cores.contains("col_bad"));
// check that we have the failures we expect
failures = cc.getCoreInitFailures();
assertNotNull("core failures is a null map", failures);
assertEquals("wrong number of core failures", 1, failures.size());
cc.shutdown();
}
private long getCoreStartTime(final CoreContainer cc, final String name) {
try (SolrCore tmp = cc.getCore(name)) {
return tmp.getStartTimeStamp().getTime();
}
}
}