/*
* Licensed to CRATE Technology GmbH ("Crate") under one or more contributor
* license agreements. See the NOTICE file distributed with this work for
* additional information regarding copyright ownership. Crate 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.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial agreement.
*/
package io.crate.metadata.doc;
import io.crate.Constants;
import io.crate.exceptions.TableUnknownException;
import io.crate.exceptions.UnhandledServerException;
import io.crate.metadata.Functions;
import io.crate.metadata.PartitionName;
import io.crate.metadata.TableIdent;
import org.apache.logging.log4j.Logger;
import org.elasticsearch.Version;
import org.elasticsearch.action.admin.indices.template.put.TransportPutIndexTemplateAction;
import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.cluster.ClusterState;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.cluster.metadata.IndexNameExpressionResolver;
import org.elasticsearch.cluster.metadata.IndexTemplateMetaData;
import org.elasticsearch.cluster.metadata.MetaData;
import org.elasticsearch.cluster.service.ClusterService;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_REPLICAS;
import static org.elasticsearch.cluster.metadata.IndexMetaData.SETTING_NUMBER_OF_SHARDS;
class DocTableInfoBuilder {
private final TableIdent ident;
private final ClusterState state;
private final boolean checkAliasSchema;
private final Functions functions;
private final ClusterService clusterService;
private final IndexNameExpressionResolver indexNameExpressionResolver;
private final TransportPutIndexTemplateAction transportPutIndexTemplateAction;
private final MetaData metaData;
private String[] concreteIndices;
private static final Logger logger = Loggers.getLogger(DocTableInfoBuilder.class);
DocTableInfoBuilder(Functions functions,
TableIdent ident,
ClusterService clusterService,
IndexNameExpressionResolver indexNameExpressionResolver,
TransportPutIndexTemplateAction transportPutIndexTemplateAction,
boolean checkAliasSchema) {
this.functions = functions;
this.clusterService = clusterService;
this.indexNameExpressionResolver = indexNameExpressionResolver;
this.transportPutIndexTemplateAction = transportPutIndexTemplateAction;
this.ident = ident;
this.state = clusterService.state();
this.metaData = state.metaData();
this.checkAliasSchema = checkAliasSchema;
}
private DocIndexMetaData docIndexMetaData() {
DocIndexMetaData docIndexMetaData;
String templateName = PartitionName.templateName(ident.schema(), ident.name());
boolean createdFromTemplate = false;
if (metaData.getTemplates().containsKey(templateName)) {
docIndexMetaData = buildDocIndexMetaDataFromTemplate(ident.indexName(), templateName);
createdFromTemplate = true;
concreteIndices = indexNameExpressionResolver.concreteIndexNames(
state, IndicesOptions.lenientExpandOpen(), ident.indexName());
} else {
try {
concreteIndices = indexNameExpressionResolver.concreteIndexNames(
state, IndicesOptions.strictExpandOpen(), ident.indexName());
if (concreteIndices.length == 0) {
// no matching index found
throw new TableUnknownException(ident);
}
docIndexMetaData = buildDocIndexMetaData(concreteIndices[0]);
} catch (IndexNotFoundException ex) {
throw new TableUnknownException(ident.fqn(), ex);
}
}
if ((!createdFromTemplate && concreteIndices.length == 1) || !checkAliasSchema) {
return docIndexMetaData;
}
for (String concreteIndex : concreteIndices) {
if (IndexMetaData.State.CLOSE.equals(metaData.indices().get(concreteIndex).getState())) {
throw new UnhandledServerException(
String.format(Locale.ENGLISH, "Unable to access the partition %s, it is closed", concreteIndex));
}
try {
docIndexMetaData = docIndexMetaData.merge(
buildDocIndexMetaData(concreteIndex),
transportPutIndexTemplateAction,
createdFromTemplate);
} catch (IOException e) {
throw new UnhandledServerException("Unable to merge/build new DocIndexMetaData", e);
}
}
return docIndexMetaData;
}
private DocIndexMetaData buildDocIndexMetaData(String indexName) {
DocIndexMetaData docIndexMetaData;
IndexMetaData indexMetaData = metaData.index(indexName);
try {
docIndexMetaData = new DocIndexMetaData(functions, indexMetaData, ident);
} catch (IOException e) {
throw new UnhandledServerException("Unable to build DocIndexMetaData", e);
}
try {
return docIndexMetaData.build();
} catch (Exception e) {
try {
logger.error(
"Could not build DocIndexMetaData from: {}", indexMetaData.mapping("default").getSourceAsMap());
} catch (Exception ignored) {
}
throw e;
}
}
private DocIndexMetaData buildDocIndexMetaDataFromTemplate(String index, String templateName) {
IndexTemplateMetaData indexTemplateMetaData = metaData.getTemplates().get(templateName);
DocIndexMetaData docIndexMetaData;
try {
IndexMetaData.Builder builder = new IndexMetaData.Builder(index);
builder.putMapping(Constants.DEFAULT_MAPPING_TYPE,
indexTemplateMetaData.getMappings().get(Constants.DEFAULT_MAPPING_TYPE).toString());
Settings.Builder settingsBuilder = Settings.builder()
.put(indexTemplateMetaData.settings())
.put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT);
Settings settings = settingsBuilder.build();
builder.settings(settings);
builder.numberOfShards(settings.getAsInt(SETTING_NUMBER_OF_SHARDS, 5));
builder.numberOfReplicas(settings.getAsInt(SETTING_NUMBER_OF_REPLICAS, 1));
docIndexMetaData = new DocIndexMetaData(functions, builder.build(), ident);
} catch (IOException e) {
throw new UnhandledServerException("Unable to build DocIndexMetaData from template", e);
}
return docIndexMetaData.build();
}
private List<PartitionName> buildPartitions(DocIndexMetaData md) {
List<PartitionName> partitions = new ArrayList<>();
if (md.partitionedBy().size() > 0) {
for (String indexName : concreteIndices) {
if (PartitionName.isPartition(indexName)) {
try {
PartitionName partitionName = PartitionName.fromIndexOrTemplate(indexName);
assert partitionName.tableIdent().equals(ident) : "ident must equal partitionName";
partitions.add(partitionName);
} catch (IllegalArgumentException e) {
// ignore
logger.warn(String.format(Locale.ENGLISH, "Cannot build partition %s of index %s", indexName, ident.indexName()));
}
}
}
}
return partitions;
}
public DocTableInfo build() {
DocIndexMetaData md = docIndexMetaData();
List<PartitionName> partitions = buildPartitions(md);
return new DocTableInfo(
ident,
md.columns(),
md.partitionedByColumns(),
md.generatedColumnReferences(),
md.indices(),
md.references(),
md.analyzers(),
md.primaryKey(),
md.routingCol(),
md.isAlias(),
md.hasAutoGeneratedPrimaryKey(),
concreteIndices,
clusterService,
indexNameExpressionResolver,
md.numberOfShards(), md.numberOfReplicas(),
md.tableParameters(),
md.partitionedBy(),
partitions,
md.columnPolicy(),
md.getRoutingHashFunction(),
md.versionCreated(),
md.versionUpgraded(),
md.supportedOperations());
}
}