001/* 002 * (C) Copyright 2020 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * bdelbosc 018 */ 019package org.nuxeo.elasticsearch; 020 021import java.io.ByteArrayInputStream; 022import java.io.File; 023import java.io.IOException; 024import java.io.InputStream; 025import java.nio.charset.StandardCharsets; 026import java.nio.file.Files; 027 028import org.apache.logging.log4j.LogManager; 029import org.apache.logging.log4j.Logger; 030import org.elasticsearch.cluster.health.ClusterHealthStatus; 031import org.nuxeo.common.xmap.XMap; 032import org.nuxeo.elasticsearch.api.ESClient; 033import org.nuxeo.elasticsearch.api.ESClientFactory; 034import org.nuxeo.elasticsearch.client.ESRestClientFactory; 035import org.nuxeo.elasticsearch.config.ElasticSearchClientConfig; 036import org.nuxeo.launcher.config.ConfigurationException; 037import org.nuxeo.launcher.config.ConfigurationGenerator; 038import org.nuxeo.launcher.config.backingservices.BackingChecker; 039 040/** 041 * @since 11.3 042 */ 043public class ElasticSearchChecker implements BackingChecker { 044 private static final Logger log = LogManager.getLogger(ElasticSearchChecker.class); 045 046 protected static final String ELASTIC_ENABLED_PROP = "elasticsearch.enabled"; 047 048 protected static final String ELASTIC_REST_CLIENT_PROP = "elasticsearch.client"; 049 050 protected static final String ADDRESS_LIST_OPT = "addressList"; 051 052 protected static final String CONFIG_NAME = "elasticsearch-config.xml"; 053 054 @Override 055 public boolean accepts(ConfigurationGenerator cg) { 056 // not using Boolean.parseValue on purpose, only 'true' must trigger the checker 057 if (!"true".equals(cg.getUserConfig().getProperty(ELASTIC_ENABLED_PROP))) { 058 log.debug("Checker skipped because elasticsearch is disabled"); 059 return false; 060 } 061 if (!"RestClient".equals(cg.getUserConfig().getProperty(ELASTIC_REST_CLIENT_PROP))) { 062 log.debug("Checker skipped because not using a rest client"); 063 return false; 064 } 065 log.debug("Checker accepted"); 066 return true; 067 } 068 069 @Override 070 public void check(ConfigurationGenerator cg) throws ConfigurationException { 071 ElasticSearchClientConfig config = getConfig(cg); 072 String addressList = config.getOption(ADDRESS_LIST_OPT); 073 if (addressList == null || addressList.isEmpty()) { 074 log.debug("Elasticsearch config check skipped on embedded configuration"); 075 return; 076 } 077 log.debug("Check elastic config: {}", config); 078 ClusterHealthStatus status = getHealthStatus(config); 079 switch (status) { 080 case GREEN: 081 case YELLOW: 082 log.debug("check is ok, cluster health is {}", status); 083 return; 084 default: 085 throw new ConfigurationException("Elasticsearch cluster is not healthy: " + status); 086 } 087 } 088 089 protected ClusterHealthStatus getHealthStatus(ElasticSearchClientConfig config) throws ConfigurationException { 090 try (ESClient client = getClient(config)) { 091 return client.getHealthStatus(null); 092 } catch (Exception e) { 093 throw new ConfigurationException("Unable to connect to Elasticsearch: " + config.getOption(ADDRESS_LIST_OPT), 094 e); 095 } 096 } 097 098 protected ESClient getClient(ElasticSearchClientConfig config) { 099 ESClientFactory clientFactory = new ESRestClientFactory(); 100 return clientFactory.create(null, config); 101 } 102 103 protected ElasticSearchClientConfig getConfig(ConfigurationGenerator cg) throws ConfigurationException { 104 File configFile = new File(cg.getConfigDir(), CONFIG_NAME); 105 if (!configFile.exists()) { 106 throw new ConfigurationException("Cannot find Elasticsearch configuration: " + CONFIG_NAME); 107 } 108 return getDescriptor(configFile); 109 } 110 111 protected ElasticSearchClientConfig getDescriptor(File file) throws ConfigurationException { 112 XMap xmap = new XMap(); 113 xmap.register(ElasticSearchClientConfig.class); 114 try { 115 // avoid XMap to fail when trying to load class value by removing class attribute 116 String content = Files.readString(file.toPath(), StandardCharsets.UTF_8); 117 content = content.replace("class=", "ignore="); 118 InputStream inStream = new ByteArrayInputStream(content.getBytes()); 119 Object[] nodes = xmap.loadAll(inStream); 120 for (Object node : nodes) { 121 if (node != null) { 122 return (ElasticSearchClientConfig) node; 123 } 124 } 125 throw new ConfigurationException("No ElasticSearchClientConfig found in " + file.getAbsolutePath()); 126 } catch (IOException e) { 127 throw new ConfigurationException("Failed to load ElasticSearchClientConfig from " + file.getAbsolutePath(), 128 e); 129 } 130 } 131}