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("http.netty.worker_count", 4) 071 .put("http.cors.enabled", true) 072 .put("http.cors.allow-origin", "*") 073 .put("http.cors.allow-credentials", true) 074 .put("http.cors.allow-headers", "Authorization, X-Requested-With, Content-Type, Content-Length") 075 .put("cluster.routing.allocation.disk.threshold_enabled", false) 076 .put("http.port", config.getHttpPort()); 077 if (config.getIndexStorageType() != null) { 078 buidler.put("index.store.type", config.getIndexStorageType()); 079 } 080 Settings settings = buidler.build(); 081 log.debug("Using settings: " + settings.toDelimitedString(',')); 082 083 Collection<Class<? extends Plugin>> plugins = new HashSet<>(); 084 plugins.add(Netty4Plugin.class); 085 plugins.add(CommonAnalysisPlugin.class); 086 try { 087 node = new PluginConfigurableNode(settings, plugins); 088 node.start(); 089 // try with another home path 090 config.setHomePath(null); 091 } catch (NodeValidationException e) { 092 throw new NuxeoException("Cannot start embedded Elasticsearch: " + e.getMessage(), e); 093 } catch (Exception e) { 094 Throwable cause = ExceptionUtils.getRootCause(e); 095 if (cause != null && cause instanceof BindException) { 096 retry--; 097 log.error(String.format("Cannot bind local Elasticsearch on port %s, from %s, retry countdown: %d", 098 config.getHttpPort(), config.getDataPath(), retry)); 099 try { 100 node.close(); 101 Thread.sleep(5000); 102 } catch (InterruptedException e1) { 103 Thread.currentThread().interrupt(); 104 throw new NuxeoException(e1); 105 } catch (IOException e1) { 106 throw new NuxeoException(e1); 107 } 108 if (retry <= 0) { 109 String msg = "Not able to bind to local Elasticsearch after multiple attempts, give up"; 110 log.error(msg); 111 throw new IllegalStateException(msg); 112 } 113 start(); 114 } else { 115 throw e; 116 } 117 } 118 retry = DEFAULT_RETRY; 119 log.debug("Elasticsearch node started."); 120 } 121 122 @Override 123 public void close() throws IOException { 124 if (node != null) { 125 log.info("Closing embedded (in JVM) Elasticsearch"); 126 node.close(); 127 log.info("Node closed: " + node.isClosed()); 128 } 129 node = null; 130 } 131 132 public ElasticSearchEmbeddedServerConfig getConfig() { 133 return config; 134 } 135 136 public Node getNode() { 137 return node; 138 } 139}