org.apache.solr.core.TestCoreContainer.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.core.TestCoreContainer.java

Source

/*
 * 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.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Properties;
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.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.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 {
        SolrResourceLoader loader = new SolrResourceLoader(homeDirectory);
        CoreContainer ret = new CoreContainer(SolrXmlConfig.fromString(loader, xml));
        ret.load();
        return ret;
    }

    @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();
        }
    }

    @Test
    public void testReloadThreaded() throws Exception {
        final CoreContainer cc = init(CONFIGSETS_SOLR_XML);
        cc.create("core1", ImmutableMap.of("configSet", "minimal"));

        class TestThread extends Thread {
            @Override
            public void run() {
                cc.reload("core1");
            }
        }

        List<Thread> threads = new ArrayList<>();
        int numThreads = 4;
        for (int i = 0; i < numThreads; i++) {
            threads.add(new TestThread());
        }

        for (Thread thread : threads) {
            thread.start();
        }

        for (Thread thread : threads) {
            thread.join();
        }

        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
            try {
                cores.unload("non_existent_core");
                fail("Should have thrown an exception when unloading a non-existent core");
            } catch (SolrException e) {
                assertThat(e.getMessage(), containsString("Cannot unload non-existent core [non_existent_core]"));
            }

            // try and remove a null core
            try {
                cores.unload(null);
                fail("Should have thrown an exception when unloading a null core");
            } catch (Exception e) {
                if (!(e instanceof SolrException)) {
                    fail("Should not have thrown SolrException but got " + e);
                }
                assertThat(e.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();

        SolrResourceLoader resourceLoader = new SolrResourceLoader(createTempDir());

        System.setProperty("configsets", getFile("solr/configsets").getAbsolutePath());

        final CoreContainer cc = new CoreContainer(SolrXmlConfig.fromString(resourceLoader, CONFIGSETS_SOLR_XML),
                new Properties(), cl);
        Path corePath = resourceLoader.getInstancePath().resolve("badcore");
        CoreDescriptor badcore = new CoreDescriptor(cc, "badcore", corePath, "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 contextLoader = Thread.currentThread().getContextClassLoader();
            assertSame(contextLoader, 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();

        JarOutputStream jar1 = new JarOutputStream(new FileOutputStream(new File(lib, "jar1.jar")));
        jar1.putNextEntry(new JarEntry("defaultSharedLibFile"));
        jar1.closeEntry();
        jar1.close();

        File customLib = new File(tmpRoot.toFile(), "customLib");
        customLib.mkdirs();

        JarOutputStream jar2 = new JarOutputStream(new FileOutputStream(new File(customLib, "jar2.jar")));
        jar2.putNextEntry(new JarEntry("customSharedLibFile"));
        jar2.closeEntry();
        jar2.close();

        final CoreContainer cc1 = init(tmpRoot, "<solr></solr>");
        try {
            cc1.loader.openResource("defaultSharedLibFile").close();
        } finally {
            cc1.shutdown();
        }

        final CoreContainer cc2 = init(tmpRoot, "<solr><str name=\"sharedLib\">lib</str></solr>");
        try {
            cc2.loader.openResource("defaultSharedLibFile").close();
        } finally {
            cc2.shutdown();
        }

        final CoreContainer cc3 = init(tmpRoot, "<solr><str name=\"sharedLib\">customLib</str></solr>");
        try {
            cc3.loader.openResource("customSharedLibFile").close();
        } finally {
            cc3.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 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 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.getCoreNames();
        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
        try {
            ignoreException(Pattern.quote("bogus_path"));
            cc.create("bogus", ImmutableMap.of("configSet", "bogus_path"));
            fail("bogus inst dir failed to trigger exception from create");
        } catch (SolrException e) {
            Throwable cause = Throwables.getRootCause(e);
            assertTrue("init exception doesn't mention bogus dir: " + cause.getMessage(),
                    0 < cause.getMessage().indexOf("bogus_path"));

        }

        // check that we have the cores we expect
        cores = cc.getCoreNames();
        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
        try {
            SolrCore c = cc.getCore("bogus");
            fail("Failed to get Exception on accessing core with init failure");
        } catch (SolrException ex) {
            assertEquals(500, ex.code());
            String cause = Throwables.getRootCause(ex).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();

        SolrResourceLoader resourceLoader = new SolrResourceLoader(createTempDir());

        System.setProperty("configsets", getFile("solr/configsets").getAbsolutePath());

        final CoreContainer cc = new CoreContainer(SolrXmlConfig.fromString(resourceLoader, CONFIGSETS_SOLR_XML),
                new Properties(), cl);
        cl.add(new CoreDescriptor(cc, "col_ok", resourceLoader.getInstancePath().resolve("col_ok"), "configSet",
                "minimal"));
        cl.add(new CoreDescriptor(cc, "col_bad", resourceLoader.getInstancePath().resolve("col_bad"), "configSet",
                "bad-mergepolicy"));
        cc.load();

        // check that we have the cores we expect
        cores = cc.getCoreNames();
        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"));
        // check that we get a 500 accessing the core with an init failure
        try {
            SolrCore c = cc.getCore("col_bad");
            fail("Failed to get Exception on accessing core with init failure");
        } catch (SolrException ex) {
            assertEquals(500, ex.code());
            // double wrapped
            String cause = ex.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.getCoreNames();
        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", 0, failures.size());

        // -----
        // try to add a collection with a path that doesn't exist
        try {
            ignoreException(Pattern.quote("bogus_path"));
            cc.create("bogus", ImmutableMap.of("configSet", "bogus_path"));
            fail("bogus inst dir failed to trigger exception from create");
        } catch (SolrException e) {
            assertTrue("init exception doesn't mention bogus dir: " + e.getCause().getCause().getMessage(),
                    0 < e.getCause().getCause().getMessage().indexOf("bogus_path"));

        }

        // check that we have the cores we expect
        cores = cc.getCoreNames();
        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
        try {
            SolrCore c = cc.getCore("bogus");
            fail("Failed to get Exception on accessing core with init failure");
        } catch (SolrException ex) {
            assertEquals(500, ex.code());
            // double wrapped
            String cause = ex.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);

        try {
            ignoreException(Pattern.quote("SAX"));
            cc.reload("col_bad");
            fail("corrupt solrconfig.xml failed to trigger exception from reload");
        } catch (SolrException e) {
            Throwable rootException = getWrappedException(e);
            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.getCoreNames();
        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.getCoreNames();
        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();
        }
    }
}