package org.apache.solr.core;
/*
* 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.
*/
import com.google.common.base.Charsets;
import com.google.common.collect.Lists;
import org.apache.solr.common.SolrException;
import org.apache.solr.util.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.List;
import java.util.Properties;
/**
* Persists CoreDescriptors as properties files
*/
public class CorePropertiesLocator implements CoresLocator {
public static final String PROPERTIES_FILENAME = "core.properties";
private static final Logger logger = LoggerFactory.getLogger(CoresLocator.class);
private final File rootDirectory;
public CorePropertiesLocator(String coreDiscoveryRoot) {
this.rootDirectory = new File(coreDiscoveryRoot);
logger.info("Config-defined core root directory: {}", this.rootDirectory.getAbsolutePath());
}
@Override
public void create(CoreContainer cc, CoreDescriptor... coreDescriptors) {
for (CoreDescriptor cd : coreDescriptors) {
File propFile = new File(new File(cd.getInstanceDir()), PROPERTIES_FILENAME);
if (propFile.exists())
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Could not create a new core in " + cd.getInstanceDir()
+ "as another core is already defined there");
writePropertiesFile(cd, propFile);
}
}
// TODO, this isn't atomic! If we crash in the middle of a rename, we
// could end up with two cores with identical names, in which case one of
// them won't start up. Are we happy with this?
@Override
public void persist(CoreContainer cc, CoreDescriptor... coreDescriptors) {
for (CoreDescriptor cd : coreDescriptors) {
File propFile = new File(new File(cd.getInstanceDir()), PROPERTIES_FILENAME);
writePropertiesFile(cd, propFile);
}
}
private void writePropertiesFile(CoreDescriptor cd, File propfile) {
Properties p = buildCoreProperties(cd);
Writer os = null;
try {
propfile.getParentFile().mkdirs();
os = new OutputStreamWriter(new FileOutputStream(propfile), Charsets.UTF_8);
p.store(os, "Written by CorePropertiesLocator");
}
catch (IOException e) {
logger.error("Couldn't persist core properties to {}: {}", propfile.getAbsolutePath(), e);
}
finally {
IOUtils.closeQuietly(os);
}
}
@Override
public void delete(CoreContainer cc, CoreDescriptor... coreDescriptors) {
if (coreDescriptors == null) {
return;
}
for (CoreDescriptor cd : coreDescriptors) {
if (cd == null) continue;
File instanceDir = new File(cd.getInstanceDir());
File propertiesFile = new File(instanceDir, PROPERTIES_FILENAME);
propertiesFile.renameTo(new File(instanceDir, PROPERTIES_FILENAME + ".unloaded"));
// This is a best-effort: the core.properties file may already have been
// deleted by the core unload, so we don't worry about checking if the
// rename has succeeded.
}
}
@Override
public void rename(CoreContainer cc, CoreDescriptor oldCD, CoreDescriptor newCD) {
persist(cc, newCD);
}
@Override
public void swap(CoreContainer cc, CoreDescriptor cd1, CoreDescriptor cd2) {
persist(cc, cd1, cd2);
}
@Override
public List<CoreDescriptor> discover(CoreContainer cc) {
logger.info("Looking for core definitions underneath {}", rootDirectory.getAbsolutePath());
List<CoreDescriptor> cds = Lists.newArrayList();
discoverUnder(rootDirectory, cds, cc);
logger.info("Found {} core definitions", cds.size());
return cds;
}
private void discoverUnder(File root, List<CoreDescriptor> cds, CoreContainer cc) {
if (!root.exists())
return;
for (File child : root.listFiles()) {
File propertiesFile = new File(child, PROPERTIES_FILENAME);
if (propertiesFile.exists()) {
CoreDescriptor cd = buildCoreDescriptor(propertiesFile, cc);
logger.info("Found core {} in {}", cd.getName(), cd.getInstanceDir());
cds.add(cd);
continue;
}
if (child.isDirectory())
discoverUnder(child, cds, cc);
}
}
protected CoreDescriptor buildCoreDescriptor(File propertiesFile, CoreContainer cc) {
FileInputStream fis = null;
try {
File instanceDir = propertiesFile.getParentFile();
Properties coreProperties = new Properties();
fis = new FileInputStream(propertiesFile);
coreProperties.load(new InputStreamReader(fis, Charsets.UTF_8));
String name = createName(coreProperties, instanceDir);
return new CoreDescriptor(cc, name, instanceDir.getAbsolutePath(), coreProperties);
}
catch (IOException e) {
logger.error("Couldn't load core descriptor from {}:{}", propertiesFile.getAbsolutePath(), e.toString());
return null;
}
finally {
IOUtils.closeQuietly(fis);
}
}
protected static String createName(Properties p, File instanceDir) {
return p.getProperty(CoreDescriptor.CORE_NAME, instanceDir.getName());
}
protected Properties buildCoreProperties(CoreDescriptor cd) {
Properties p = new Properties();
p.putAll(cd.getPersistableStandardProperties());
p.putAll(cd.getPersistableUserProperties());
// We don't persist the instance directory, as that's defined by the location
// of the properties file.
p.remove(CoreDescriptor.CORE_INSTDIR);
return p;
}
}