001/* 002 * (C) Copyright 2017 Nuxeo SA (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.core; 020 021import java.io.Closeable; 022import java.io.IOException; 023import java.net.BindException; 024import java.util.Collection; 025import java.util.HashSet; 026 027import org.apache.commons.logging.Log; 028import org.apache.commons.logging.LogFactory; 029import org.elasticsearch.analysis.common.CommonAnalysisPlugin; 030import org.elasticsearch.common.settings.Settings; 031import org.elasticsearch.node.Node; 032import org.elasticsearch.node.NodeValidationException; 033import org.elasticsearch.plugins.Plugin; 034import org.elasticsearch.transport.Netty4Plugin; 035import org.nuxeo.common.utils.ExceptionUtils; 036import org.nuxeo.ecm.core.api.NuxeoException; 037import org.nuxeo.elasticsearch.config.ElasticSearchEmbeddedServerConfig; 038import org.nuxeo.runtime.api.Framework; 039 040/** 041 * @since 9.3 042 */ 043public class ElasticSearchEmbeddedNode implements Closeable { 044 private static final Log log = LogFactory.getLog(ElasticSearchEmbeddedNode.class); 045 046 private static final int DEFAULT_RETRY = 3; 047 048 protected final ElasticSearchEmbeddedServerConfig config; 049 050 protected Node node; 051 052 protected int retry = DEFAULT_RETRY; 053 054 public ElasticSearchEmbeddedNode(ElasticSearchEmbeddedServerConfig config) { 055 this.config = config; 056 } 057 058 public void start() { 059 log.info("Starting embedded (in JVM) Elasticsearch"); 060 if (Framework.isInitialized() && !Framework.isTestModeSet()) { 061 log.warn("Elasticsearch embedded configuration is ONLY for testing" 062 + " purpose. You need to create a dedicated Elasticsearch" + " cluster for production."); 063 } 064 Settings.Builder buidler = Settings.builder(); 065 buidler.put("network.host", config.getNetworkHost()) 066 .put("path.home", config.getHomePath()) 067 .put("path.data", config.getDataPath()) 068 .put("cluster.name", config.getClusterName()) 069 .put("node.name", config.getNodeName()) 070 .put("discovery.type", "single-node") 071 .put("http.netty.worker_count", 4) 072 .put("http.cors.enabled", true) 073 .put("http.cors.allow-origin", "*") 074 .put("http.cors.allow-credentials", true) 075 .put("http.cors.allow-headers", "Authorization, X-Requested-With, Content-Type, Content-Length") 076 .put("cluster.routing.allocation.disk.threshold_enabled", false) 077 .put("http.port", config.getHttpPort()); 078 if (config.getIndexStorageType() != null) { 079 buidler.put("index.store.type", config.getIndexStorageType()); 080 } 081 Settings settings = buidler.build(); 082 log.debug("Using settings: " + settings.toDelimitedString(',')); 083 084 Collection<Class<? extends Plugin>> plugins = new HashSet<>(); 085 plugins.add(Netty4Plugin.class); 086 plugins.add(CommonAnalysisPlugin.class); 087 try { 088 node = new PluginConfigurableNode(settings, plugins); 089 node.start(); 090 // try with another home path 091 config.setHomePath(null); 092 } catch (NodeValidationException e) { 093 throw new NuxeoException("Cannot start embedded Elasticsearch: " + e.getMessage(), e); 094 } catch (Exception e) { 095 Throwable cause = ExceptionUtils.getRootCause(e); 096 if (cause != null && cause instanceof BindException) { 097 retry--; 098 log.error(String.format("Cannot bind local Elasticsearch on port %s, from %s, retry countdown: %d", 099 config.getHttpPort(), config.getDataPath(), retry)); 100 try { 101 node.close(); 102 Thread.sleep(5000); 103 } catch (InterruptedException e1) { 104 Thread.currentThread().interrupt(); 105 throw new NuxeoException(e1); 106 } catch (IOException e1) { 107 throw new NuxeoException(e1); 108 } 109 if (retry <= 0) { 110 String msg = "Not able to bind to local Elasticsearch after multiple attempts, give up"; 111 log.error(msg); 112 throw new IllegalStateException(msg); 113 } 114 start(); 115 } else { 116 throw e; 117 } 118 } 119 retry = DEFAULT_RETRY; 120 log.debug("Elasticsearch node started."); 121 } 122 123 @Override 124 public void close() throws IOException { 125 if (node != null) { 126 log.info("Closing embedded (in JVM) Elasticsearch"); 127 node.close(); 128 log.info("Node closed: " + node.isClosed()); 129 } 130 node = null; 131 } 132 133 public ElasticSearchEmbeddedServerConfig getConfig() { 134 return config; 135 } 136 137 public Node getNode() { 138 return node; 139 } 140}