/* * 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.ambari.logsearch.handler; import org.apache.ambari.logsearch.conf.SolrPropsConfig; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.configuration.HierarchicalConfiguration.Node; import org.apache.commons.configuration.XMLConfiguration; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.impl.CloudSolrClient; import org.apache.solr.client.solrj.request.schema.SchemaRequest; import org.apache.solr.common.cloud.SolrZkClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class UpgradeSchemaHandler extends AbstractSolrConfigHandler { private static final Logger LOG = LoggerFactory.getLogger(UpgradeSchemaHandler.class); private static final String SCHEMA_FILE = "managed-schema"; private static final String FIELD_NAME_PATH = "field[@name]"; private static final String FIELD_TYPE_NAME_PATH = "fieldType[@name]"; private static final String DYNAMIC_FIELD = "dynamicField"; private static final String DYNAMIC_FIELD_NAME_PATH = DYNAMIC_FIELD + "[@name]"; private CloudSolrClient cloudSolrClient; private XMLConfiguration localFileXml; private List<String> localDynamicFields; public UpgradeSchemaHandler(CloudSolrClient cloudSolrClient, File configSetFolder) { super(configSetFolder); this.cloudSolrClient = cloudSolrClient; } @Override public boolean updateConfigIfNeeded(SolrPropsConfig solrPropsConfig, SolrZkClient zkClient, File file, String separator, String downloadFolderLocation) throws IOException { boolean result = false; if (localSchemaFileHasMoreFields(file, new File(String.format("%s%s%s", downloadFolderLocation, separator, file.getName())))) { LOG.info("Solr schema file differs ('{}'), update config schema...", file.getName()); try { upgradeDynamicFields(); } catch (Exception e) { throw new RuntimeException(e); } result = true; } return result; } // for now we only upgrades dynamic fields, later we can extend this feature if needed private void upgradeDynamicFields() throws IOException, SolrServerException { if (localFileXml.getRoot() != null && CollectionUtils.isNotEmpty(localDynamicFields)) { List<Node> children = localFileXml.getRoot().getChildren(DYNAMIC_FIELD); for (Node dynamicFieldNode : children) { List<Node> attributes = dynamicFieldNode.getAttributes(); Map<String, Object> attributesMap = new HashMap<>(); for (Node attribute : attributes) { attributesMap.put(attribute.getName(), attribute.getValue()); } if (attributesMap.get("name") != null && localDynamicFields.contains(attributesMap.get("name").toString())) { SchemaRequest.AddDynamicField addDynamicFieldRequest = new SchemaRequest.AddDynamicField(attributesMap); addDynamicFieldRequest.process(cloudSolrClient); LOG.info("Added dynamic field request sent. (field name: {})", attributesMap.get("name")); } } } } @Override public String getConfigFileName() { return SCHEMA_FILE; } private boolean localSchemaFileHasMoreFields(File localFile, File downloadedFile) { try { localFileXml = new XMLConfiguration(localFile); XMLConfiguration downloadedFileXml = new XMLConfiguration(downloadedFile); List<String> localFieldNames = (ArrayList<String>) localFileXml.getProperty(FIELD_NAME_PATH); List<String> localFieldTypes = (ArrayList<String>) localFileXml.getProperty(FIELD_TYPE_NAME_PATH); localDynamicFields = (ArrayList<String>) localFileXml.getProperty(DYNAMIC_FIELD_NAME_PATH); List<String> fieldNames = (ArrayList<String>) downloadedFileXml.getProperty(FIELD_NAME_PATH); List<String> fieldTypes = (ArrayList<String>) downloadedFileXml.getProperty(FIELD_TYPE_NAME_PATH); List<String> dynamicFields = (ArrayList<String>) downloadedFileXml.getProperty(DYNAMIC_FIELD_NAME_PATH); boolean fieldNameHasDiff = hasMoreFields(localFieldNames, fieldNames, FIELD_NAME_PATH); boolean fieldTypeHasDiff = hasMoreFields(localFieldTypes, fieldTypes, FIELD_TYPE_NAME_PATH); boolean dynamicFieldNameHasDiff = hasMoreFields(localDynamicFields, dynamicFields, DYNAMIC_FIELD_NAME_PATH); return fieldNameHasDiff || fieldTypeHasDiff || dynamicFieldNameHasDiff; } catch (Exception e) { throw new RuntimeException("Exception during schema xml parsing.", e); } } private boolean hasMoreFields(List<String> localFields, List<String> fields, String tag) { boolean result = false; if (localFields != null) { if (fields == null) { result = true; } else { localFields.removeAll(fields); if (!localFields.isEmpty()) { result = true; } } } if (result) { LOG.info("Found new fields or field types in local schema file.: {} ({})", localFields.toString(), tag); } return result; } }