All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.redlink.solrlib.embedded.EmbeddedCoreContainer Maven / Gradle / Ivy

/*
 * Copyright 2017 redlink GmbH
 *
 * Licensed 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 io.redlink.solrlib.embedded;

import com.google.common.base.Preconditions;
import io.redlink.solrlib.SolrCoreContainer;
import io.redlink.solrlib.SolrCoreDescriptor;
import io.redlink.utils.PathUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
import org.apache.solr.client.solrj.request.CoreAdminRequest;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.CoreContainer;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.Date;
import java.util.Objects;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ExecutorService;

/**
 * SolrCoreContainer
 */
public class EmbeddedCoreContainer extends SolrCoreContainer {

    private CoreContainer coreContainer = null;
    private Path solrHome = null;
    private boolean deleteOnShutdown;

    public EmbeddedCoreContainer(Set coreDescriptors,
                                 EmbeddedCoreContainerConfiguration configuration) {
        this(coreDescriptors, configuration, null);
    }

    public EmbeddedCoreContainer(Set coreDescriptors,
                                 EmbeddedCoreContainerConfiguration configuration,
                                 ExecutorService executorService) {
        super(coreDescriptors, executorService);
        deleteOnShutdown = configuration.isDeleteOnShutdown();
        solrHome = configuration.getHome();
    }

    @Override
    protected synchronized void init(ExecutorService executorService) throws IOException {
        Preconditions.checkState(Objects.isNull(coreContainer), "Already initialized!");

        if (solrHome == null) {
            solrHome = Files.createTempDirectory("solr-home");
            log.debug("No solr-home set, using temp directory {}", solrHome);
            deleteOnShutdown = true;
        }

        final Path solrHome = this.solrHome.toAbsolutePath();
        final Path lib = solrHome.resolve("lib");
        Files.createDirectories(solrHome);
        Files.createDirectories(lib);

        final Path solrXml = solrHome.resolve("solr.xml");
        if (!Files.exists(solrXml)) {
            log.info("no solr.xml found, creating new at {}", solrXml);
            try (PrintStream writer = new PrintStream(Files.newOutputStream(solrXml, StandardOpenOption.CREATE))) {
                writer.printf("%n", getClass().getSimpleName(), new Date());
                writer.println("");
                writer.printf("  %s%n", "sharedLib", solrHome.relativize(lib));
                writer.println("");
            }
        }

        for (SolrCoreDescriptor coreDescriptor : coreDescriptors) {
            final String coreName = coreDescriptor.getCoreName();
            if (availableCores.containsKey(coreName)) {
                log.warn("CoreName-Clash: {} already initialized. Skipping {}", coreName, coreDescriptor.getClass());
                continue;
            }
            final Path coreDir = solrHome.resolve(coreName);
            Files.createDirectories(coreDir);
            coreDescriptor.initCoreDirectory(coreDir, lib);

            final Properties coreProperties = new Properties();
            final Path corePropertiesFile = coreDir.resolve("core.properties");
            if (Files.exists(corePropertiesFile)) {
                try (InputStream inStream = Files.newInputStream(corePropertiesFile, StandardOpenOption.CREATE)) {
                    coreProperties.load(inStream);
                }
                log.debug("core.properties for {} found, updating", coreName);
            } else {
                log.debug("Creating new core {} in {}", coreName, coreDir);
            }
            coreProperties.setProperty("name", coreName);
            try (OutputStream outputStream = Files.newOutputStream(corePropertiesFile)) {
                coreProperties.store(outputStream, null);
            }

            if (coreDescriptor.getNumShards() > 1 || coreDescriptor.getReplicationFactor() > 1) {
                log.warn("Deploying {} to EmbeddedCoreContainer, ignoring config of shards={},replication={}", coreName,
                        coreDescriptor.getNumShards(), coreDescriptor.getReplicationFactor());
            }

            availableCores.put(coreName, coreDescriptor);
        }

        log.info("Starting {} in solr-home '{}'", getClass().getSimpleName(), solrHome);
        coreContainer = CoreContainer.createAndLoad(solrHome, solrXml);

        availableCores.values().forEach(coreDescriptor -> {
            final String coreName = coreDescriptor.getCoreName();
            try (SolrClient solrClient = createSolrClient(coreName)) {
                final NamedList coreStatus = CoreAdminRequest.getStatus(coreName, solrClient).getCoreStatus(coreName);
                final NamedList indexStatus = coreStatus == null ? null : (NamedList)coreStatus.get("index");
                final Object lastModified = indexStatus == null? null : indexStatus.get("lastModified");
                // lastModified is null if there was never a update
                scheduleCoreInit(executorService, coreDescriptor, lastModified == null);
            } catch (SolrServerException | IOException e) {
                if (log.isDebugEnabled()) {
                    log.error("Error initializing core {}", coreName, e);
                }
                //noinspection ThrowableResultOfMethodCallIgnored
                coreInitException.put(coreName, e);
            }
        });
    }

    @Override
    public final void shutdown() throws IOException {
        Preconditions.checkState(Objects.nonNull(this.coreContainer), "Not initialized!");

        try {
            final CoreContainer cc = this.coreContainer;
            this.coreContainer = null;
            cc.shutdown();
        } catch (final Throwable t) {
            log.error("Unexpected Error during CoreContainer.shutdown(): {}", t.getMessage());
            throw t;
        }

        if (deleteOnShutdown) {
            log.debug("Cleaning up solr-home {}", solrHome);
            PathUtils.deleteRecursive(solrHome);
        }
    }

    @Override
    protected SolrClient createSolrClient(String coreName) {
        Preconditions.checkState(Objects.nonNull(coreContainer), "CoreContainer not initialized!");
        Preconditions.checkArgument(StringUtils.isNotBlank(coreName));
        return new EmbeddedSolrServer(coreContainer, coreName) {
            @Override
            public void close() throws IOException {
                // nop;
            }
        };
    }

    /**
     * For testing
     */
    @Override
    protected void scheduleCoreInit(ExecutorService executorService, SolrCoreDescriptor coreDescriptor, boolean newCore) {
        super.scheduleCoreInit(executorService, coreDescriptor, newCore);
    }

    public CoreContainer getCoreContainer() {
        try {
            awaitInitCompletion();
            return coreContainer;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new IllegalStateException("Could not retrieve CoreContainer", e);
        }
    }
}