blob: da7e0460fb740fecfef496880af37a3d2fd8e52e [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.schema;
import org.apache.solr.cloud.CloudConfigSetService;
import org.apache.solr.cloud.ZkSolrResourceLoader;
import org.apache.solr.common.ConfigNode;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.solr.core.ConfigSetService;
import org.apache.solr.core.PluginInfo;
import org.apache.solr.core.SolrConfig;
import org.apache.solr.core.SolrResourceLoader;
import org.apache.solr.util.plugin.NamedListInitializedPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.InputStream;
import java.lang.invoke.MethodHandles;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/** Base class for factories for IndexSchema implementations */
public abstract class IndexSchemaFactory implements NamedListInitializedPlugin {
private static final Logger log = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
public static IndexSchema buildIndexSchema(String resourceName, SolrConfig config) {
return buildIndexSchema(resourceName, config, null);
}
/** Instantiates the configured schema factory, then calls create on it. */
public static IndexSchema buildIndexSchema(String resourceName, SolrConfig config, ConfigSetService configSetService) {
return newIndexSchemaFactory(config).create(resourceName, config, configSetService);
}
/** Instantiates us from {@link SolrConfig}. */
public static IndexSchemaFactory newIndexSchemaFactory(SolrConfig config) {
PluginInfo info = config.getPluginInfo(IndexSchemaFactory.class.getName());
IndexSchemaFactory factory;
if (null != info) {
factory = config.getResourceLoader().newInstance(info.className, IndexSchemaFactory.class);
factory.init(info.initArgs);
} else {
factory = config.getResourceLoader().newInstance(ManagedIndexSchemaFactory.class.getName(), IndexSchemaFactory.class);
}
return factory;
}
/**
* Returns the resource (file) name that will be used for the schema itself. The answer may be a guess.
* Do not pass the result of this to {@link #create(String, SolrConfig, ConfigSetService)}.
* The input is the name coming from the {@link org.apache.solr.core.CoreDescriptor}
* which acts as a default or asked-for name.
*/
public String getSchemaResourceName(String cdResourceName) {
return cdResourceName;
}
/**
* Returns an index schema created from a local resource. The input is usually from the core descriptor.
*/
public IndexSchema create(String resourceName, SolrConfig config, ConfigSetService configSetService) {
SolrResourceLoader loader = config.getResourceLoader();
InputStream schemaInputStream = null;
if (null == resourceName) {
resourceName = IndexSchema.DEFAULT_SCHEMA_FILE;
}
try {
schemaInputStream = loader.openResource(resourceName);
return new IndexSchema(resourceName, getConfigResource(configSetService, schemaInputStream, loader, resourceName), config.luceneMatchVersion, loader, config.getSubstituteProperties());
} catch (RuntimeException rte) {
throw rte;
} catch (Exception e) {
final String msg = "Error loading schema resource " + resourceName;
log.error(msg, e);
throw new SolrException(ErrorCode.SERVER_ERROR, msg, e);
}
}
@SuppressWarnings("unchecked")
public static ConfigSetService.ConfigResource getConfigResource(ConfigSetService configSetService, InputStream schemaInputStream, SolrResourceLoader loader, String name) throws IOException {
if (configSetService instanceof CloudConfigSetService && schemaInputStream instanceof ZkSolrResourceLoader.ZkByteArrayInputStream) {
ZkSolrResourceLoader.ZkByteArrayInputStream is = (ZkSolrResourceLoader.ZkByteArrayInputStream) schemaInputStream;
Map<String, VersionedConfig> configCache = (Map<String, VersionedConfig>) ((CloudConfigSetService) configSetService).getSolrCloudManager().getObjectCache()
.computeIfAbsent(ConfigSetService.ConfigResource.class.getName(), s -> new ConcurrentHashMap<>());
VersionedConfig cached = configCache.get(is.fileName);
if (cached != null) {
if (cached.version != is.getStat().getVersion()) {
configCache.remove(is.fileName);// this is stale. remove from cache
} else {
return () -> cached.data;
}
}
return () -> {
ConfigNode data = ConfigSetService.getParsedSchema(schemaInputStream, loader, name);// either missing or stale. create a new one
configCache.put(is.fileName, new VersionedConfig(is.getStat().getVersion(), data));
return data;
};
}
//this is not cacheable as it does not come from ZK
return () -> ConfigSetService.getParsedSchema(schemaInputStream,loader, name);
}
public static class VersionedConfig {
public final int version;
public final ConfigNode data;
public VersionedConfig(int version, ConfigNode data) {
this.version = version;
this.data = data;
}
}
}