Drop support for auto-configuring an embedded Elasticsearch node

Elastic have announced [1] that embedded Elasticsearch is no longer
supported. This commit brings us into line with that announcement by
removing the auto-configuration that would create an Elasticsearch
Node and NodeClient.

To use the Elasticsearch auto-configuration, a user must now provide
the address of one or more cluster nodes
(via the spring.elastisearch.cluster-nodes property) which will then
be used to create a TransportClient.

See gh-9374

[1] https://www.elastic.co/blog/elasticsearch-the-server
This commit is contained in:
Andy Wilkinson 2017-06-15 16:29:46 +01:00
parent dbabfc224c
commit 4a030d5a7a
13 changed files with 268 additions and 354 deletions

View File

@ -441,7 +441,7 @@ public class HealthIndicatorAutoConfigurationTests {
@Test
public void elasticsearchHealthIndicator() {
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.home:target",
.of("spring.data.elasticsearch.cluster-nodes:localhost:0",
"management.health.diskspace.enabled:false")
.applyTo(this.context);
this.context.register(JestClientConfiguration.class, JestAutoConfiguration.class,

View File

@ -16,34 +16,18 @@
package org.springframework.boot.autoconfigure.data.elasticsearch;
import java.io.Closeable;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.InternalSettingsPreparer;
import org.elasticsearch.node.Node;
import org.elasticsearch.plugins.Plugin;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.NodeClientFactoryBean;
import org.springframework.data.elasticsearch.client.TransportClientFactoryBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* {@link org.springframework.boot.autoconfigure.EnableAutoConfiguration
@ -55,101 +39,25 @@ import org.springframework.util.StringUtils;
* @since 1.1.0
*/
@Configuration
@ConditionalOnClass({ Client.class, TransportClientFactoryBean.class,
NodeClientFactoryBean.class })
@ConditionalOnClass({ Client.class, TransportClientFactoryBean.class })
@ConditionalOnProperty(prefix = "spring.data.elasticsearch", name = "cluster-nodes", matchIfMissing = false)
@EnableConfigurationProperties(ElasticsearchProperties.class)
public class ElasticsearchAutoConfiguration implements DisposableBean {
private static final Map<String, String> DEFAULTS;
static {
Map<String, String> defaults = new LinkedHashMap<>();
defaults.put("http.enabled", String.valueOf(false));
defaults.put("transport.type", "local");
defaults.put("path.home", System.getProperty("user.dir"));
DEFAULTS = Collections.unmodifiableMap(defaults);
}
private static final Set<String> TRANSPORT_PLUGINS;
static {
Set<String> plugins = new LinkedHashSet<>();
plugins.add("org.elasticsearch.transport.Netty4Plugin");
plugins.add("org.elasticsearch.transport.Netty3Plugin");
TRANSPORT_PLUGINS = Collections.unmodifiableSet(plugins);
}
private static final Log logger = LogFactory
.getLog(ElasticsearchAutoConfiguration.class);
public class ElasticsearchAutoConfiguration {
private final ElasticsearchProperties properties;
private Closeable closeable;
public ElasticsearchAutoConfiguration(ElasticsearchProperties properties) {
this.properties = properties;
}
@Bean
@ConditionalOnMissingBean
public Client elasticsearchClient() {
try {
return createClient();
}
catch (Exception ex) {
throw new IllegalStateException(ex);
}
}
private Client createClient() throws Exception {
if (StringUtils.hasLength(this.properties.getClusterNodes())) {
return createTransportClient();
}
return createNodeClient();
}
private Client createNodeClient() throws Exception {
Settings.Builder settings = Settings.builder();
for (Map.Entry<String, String> entry : DEFAULTS.entrySet()) {
if (!this.properties.getProperties().containsKey(entry.getKey())) {
settings.put(entry.getKey(), entry.getValue());
}
}
settings.put(this.properties.getProperties());
settings.put("cluster.name", this.properties.getClusterName());
Node node = createNode(settings.build());
this.closeable = node;
node.start();
return node.client();
}
private Node createNode(Settings settings) {
Collection<Class<? extends Plugin>> plugins = findPlugins();
if (plugins.isEmpty()) {
return new Node(settings);
}
return new PluggableNode(settings, plugins);
}
@SuppressWarnings("unchecked")
private Collection<Class<? extends Plugin>> findPlugins() {
for (String candidate : TRANSPORT_PLUGINS) {
if (ClassUtils.isPresent(candidate, null)) {
Class<? extends Plugin> pluginClass = (Class<? extends Plugin>) ClassUtils
.resolveClassName(candidate, null);
return Collections.singleton(pluginClass);
}
}
return Collections.emptySet();
}
private Client createTransportClient() throws Exception {
public TransportClient elasticsearchClient() throws Exception {
TransportClientFactoryBean factory = new TransportClientFactoryBean();
factory.setClusterNodes(this.properties.getClusterNodes());
factory.setProperties(createProperties());
factory.afterPropertiesSet();
TransportClient client = factory.getObject();
this.closeable = client;
return client;
}
@ -160,34 +68,4 @@ public class ElasticsearchAutoConfiguration implements DisposableBean {
return properties;
}
@Override
public void destroy() throws Exception {
if (this.closeable != null) {
try {
if (logger.isInfoEnabled()) {
logger.info("Closing Elasticsearch client");
}
this.closeable.close();
}
catch (final Exception ex) {
if (logger.isErrorEnabled()) {
logger.error("Error closing Elasticsearch client: ", ex);
}
}
}
}
/**
* {@link Node} subclass to support {@link Plugin Plugins}.
*/
private static class PluggableNode extends Node {
PluggableNode(Settings preparedSettings,
Collection<Class<? extends Plugin>> classpathPlugins) {
super(InternalSettingsPreparer.prepareEnvironment(preparedSettings, null),
classpathPlugins);
}
}
}

View File

@ -37,8 +37,7 @@ public class ElasticsearchProperties {
private String clusterName = "elasticsearch";
/**
* Comma-separated list of cluster node addresses. If not specified, starts a client
* node.
* Comma-separated list of cluster node addresses.
*/
private String clusterNodes;

View File

@ -230,6 +230,10 @@ public class SpringBootWebSecurityConfiguration {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests()
.requestMatchers(
(request) -> request.getHeader("Host").equals("whatever"))
.permitAll();
http.requestMatcher(new RequestMatcher() {
@Override
public boolean matches(HttpServletRequest request) {

View File

@ -16,15 +16,17 @@
package org.springframework.boot.autoconfigure.data.elasticsearch;
import java.util.List;
import org.assertj.core.api.Assertions;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.cluster.node.DiscoveryNode;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@ -38,6 +40,7 @@ import static org.mockito.Mockito.mock;
* Tests for {@link ElasticsearchAutoConfiguration}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ElasticsearchAutoConfigurationTests {
@ -46,57 +49,11 @@ public class ElasticsearchAutoConfigurationTests {
private AnnotationConfigApplicationContext context;
@Before
public void preventElasticsearchFromConfiguringNetty() {
System.setProperty("es.set.netty.runtime.available.processors", "false");
}
@After
public void close() {
if (this.context != null) {
this.context.close();
}
System.clearProperty("es.set.netty.runtime.available.processors");
}
@Test
public void createNodeClientWithDefaults() {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.properties.monitor.process.refresh_interval:2s",
"spring.data.elasticsearch.properties.path.home:target")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(Client.class).length).isEqualTo(1);
NodeClient client = (NodeClient) this.context.getBean(Client.class);
assertThat(client.settings().get("monitor.process.refresh_interval"))
.isEqualTo("2s");
assertThat(client.settings().get("transport.type")).isEqualTo("local");
assertThat(client.settings().get("http.enabled")).isEqualTo("false");
}
@Test
public void createNodeClientWithOverrides() {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.properties.monitor.process.refresh_interval:2s",
"spring.data.elasticsearch.properties.path.home:target",
"spring.data.elasticsearch.properties.transport.type:local",
"spring.data.elasticsearch.properties.node.data:true",
"spring.data.elasticsearch.properties.http.enabled:true")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(Client.class).length).isEqualTo(1);
NodeClient client = (NodeClient) this.context.getBean(Client.class);
assertThat(client.settings().get("monitor.process.refresh_interval"))
.isEqualTo("2s");
assertThat(client.settings().get("transport.type")).isEqualTo("local");
assertThat(client.settings().get("node.data")).isEqualTo("true");
assertThat(client.settings().get("http.enabled")).isEqualTo("true");
}
@Test
@ -106,25 +63,28 @@ public class ElasticsearchAutoConfigurationTests {
PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBeanNamesForType(Client.class).length).isEqualTo(1);
assertThat(this.context.getBean("myClient"))
Assertions.assertThat(this.context.getBeanNamesForType(Client.class).length)
.isEqualTo(1);
Assertions.assertThat(this.context.getBean("myClient"))
.isSameAs(this.context.getBean(Client.class));
}
@Test
public void createTransportClient() throws Exception {
// We don't have a local elasticsearch server so use an address that's missing
// a port and check the exception
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.cluster-nodes:localhost",
"spring.data.elasticsearch.properties.path.home:target")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class);
this.thrown.expect(BeanCreationException.class);
this.thrown.expectMessage("port");
this.context.refresh();
new ElasticsearchNodeTemplate().doWithNode((node) -> {
TestPropertyValues
.of("spring.data.elasticsearch.cluster-nodes:localhost:"
+ node.getTcpPort(),
"spring.data.elasticsearch.properties.path.home:target/es/client")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class);
this.context.refresh();
List<DiscoveryNode> connectedNodes = this.context
.getBean(TransportClient.class).connectedNodes();
assertThat(connectedNodes).hasSize(1);
});
}
@Configuration

View File

@ -48,43 +48,55 @@ public class ElasticsearchDataAutoConfigurationTests {
@Test
public void templateExists() {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(ElasticsearchTemplate.class);
new ElasticsearchNodeTemplate().doWithNode((node) -> {
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs",
"spring.data.elasticsearch.cluster-nodes:localhost:"
+ node.getTcpPort())
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(ElasticsearchTemplate.class);
});
}
@Test
public void mappingContextExists() {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(SimpleElasticsearchMappingContext.class);
new ElasticsearchNodeTemplate().doWithNode((node) -> {
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs",
"spring.data.elasticsearch.cluster-nodes:localhost:"
+ node.getTcpPort())
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(SimpleElasticsearchMappingContext.class);
});
}
@Test
public void converterExists() {
this.context = new AnnotationConfigApplicationContext();
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs")
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(ElasticsearchConverter.class);
new ElasticsearchNodeTemplate().doWithNode((node) -> {
TestPropertyValues
.of("spring.data.elasticsearch.properties.path.data:target/data",
"spring.data.elasticsearch.properties.path.logs:target/logs",
"spring.data.elasticsearch.cluster-nodes:localhost:"
+ node.getTcpPort())
.applyTo(this.context);
this.context.register(PropertyPlaceholderAutoConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class);
this.context.refresh();
assertHasSingleBean(ElasticsearchConverter.class);
});
}
private void assertHasSingleBean(Class<?> type) {

View File

@ -0,0 +1,111 @@
/*
* Copyright 2012-2017 the original author or authors.
*
* Licensed 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.springframework.boot.autoconfigure.data.elasticsearch;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.function.Consumer;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.node.InternalSettingsPreparer;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeValidationException;
import org.elasticsearch.transport.Netty4Plugin;
import org.elasticsearch.transport.Transport;
/**
* Helper class for managing an Elasticsearch {@link Node}.
*
* @author Andy Wilkinson
*/
public class ElasticsearchNodeTemplate {
public void doWithNode(Consumer<ElasticsearchNode> consumer) {
System.setProperty("es.set.netty.runtime.available.processors", "false");
Node node = null;
try {
node = startNode();
consumer.accept(new ElasticsearchNode(node));
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
finally {
if (node != null) {
try {
node.close();
}
catch (Exception ex) {
// Continue
}
}
System.clearProperty("es.set.netty.runtime.available.processors");
}
}
private Node startNode() throws NodeValidationException {
Node node = new NettyTransportNode();
node.start();
return node;
}
private static final class NettyTransportNode extends Node {
private NettyTransportNode() {
super(InternalSettingsPreparer.prepareEnvironment(Settings.builder()
.put("path.home", "target/es/node").put("transport.type", "netty4")
.put("http.enabled", true).put("node.portsfile", true)
.put("http.port", 0).put("transport.tcp.port", 0).build(), null),
Arrays.asList(Netty4Plugin.class));
new File("target/es/node/logs").mkdirs();
}
}
public final class ElasticsearchNode {
private final Node node;
private ElasticsearchNode(Node node) {
this.node = node;
}
public int getTcpPort() {
return this.node.injector().getInstance(Transport.class).boundAddress()
.publishAddress().getPort();
}
public int getHttpPort() {
try {
for (String line : Files
.readAllLines(Paths.get("target/es/node/logs/http.ports"))) {
if (line.startsWith("127.0.0.1")) {
return Integer.parseInt(line.substring(line.indexOf(":") + 1));
}
}
throw new IllegalStateException("HTTP port not found");
}
catch (IOException ex) {
throw new IllegalStateException("Failed to read HTTP port", ex);
}
}
}
}

View File

@ -23,6 +23,7 @@ import org.junit.Test;
import org.springframework.boot.autoconfigure.TestAutoConfigurationPackage;
import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration;
import org.springframework.boot.autoconfigure.data.alt.elasticsearch.CityElasticsearchDbRepository;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchNodeTemplate.ElasticsearchNode;
import org.springframework.boot.autoconfigure.data.elasticsearch.city.City;
import org.springframework.boot.autoconfigure.data.elasticsearch.city.CityRepository;
import org.springframework.boot.autoconfigure.data.empty.EmptyDataPackage;
@ -37,6 +38,7 @@ import static org.assertj.core.api.Assertions.assertThat;
* Tests for {@link ElasticsearchRepositoriesAutoConfiguration}.
*
* @author Phillip Webb
* @author Andy Wilkinson
*/
public class ElasticsearchRepositoriesAutoConfigurationTests {
@ -49,46 +51,45 @@ public class ElasticsearchRepositoriesAutoConfigurationTests {
@Test
public void testDefaultRepositoryConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
addElasticsearchProperties(this.context);
this.context.register(TestConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(CityRepository.class)).isNotNull();
assertThat(this.context.getBean(Client.class)).isNotNull();
new ElasticsearchNodeTemplate().doWithNode((node) -> {
load(TestConfiguration.class, node);
assertThat(this.context.getBean(CityRepository.class)).isNotNull();
assertThat(this.context.getBean(Client.class)).isNotNull();
});
}
@Test
public void testNoRepositoryConfiguration() throws Exception {
this.context = new AnnotationConfigApplicationContext();
addElasticsearchProperties(this.context);
this.context.register(EmptyConfiguration.class,
ElasticsearchAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(Client.class)).isNotNull();
new ElasticsearchNodeTemplate().doWithNode((address) -> {
load(EmptyConfiguration.class, address);
assertThat(this.context.getBean(Client.class)).isNotNull();
});
}
@Test
public void doesNotTriggerDefaultRepositoryDetectionIfCustomized() {
new ElasticsearchNodeTemplate().doWithNode((address) -> {
load(CustomizedConfiguration.class, address);
assertThat(this.context.getBean(CityElasticsearchDbRepository.class))
.isNotNull();
});
}
private void load(Class<?> config, ElasticsearchNode node) {
this.context = new AnnotationConfigApplicationContext();
addElasticsearchProperties(this.context);
this.context.register(CustomizedConfiguration.class,
ElasticsearchAutoConfiguration.class,
addElasticsearchProperties(this.context, node);
this.context.register(config, ElasticsearchAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class,
ElasticsearchDataAutoConfiguration.class,
PropertyPlaceholderAutoConfiguration.class);
this.context.refresh();
assertThat(this.context.getBean(CityElasticsearchDbRepository.class)).isNotNull();
}
private void addElasticsearchProperties(AnnotationConfigApplicationContext context) {
TestPropertyValues.of("spring.data.elasticsearch.properties.path.home:target")
private void addElasticsearchProperties(AnnotationConfigApplicationContext context,
ElasticsearchNode node) {
TestPropertyValues.of("spring.data.elasticsearch.properties.path.home:target",
"spring.data.elasticsearch.cluster-nodes:localhost:" + node.getTcpPort())
.applyTo(context);
}

View File

@ -16,21 +16,18 @@
package org.springframework.boot.autoconfigure.elasticsearch.jest;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import com.google.gson.Gson;
import io.searchbox.action.Action;
import io.searchbox.client.JestClient;
import io.searchbox.client.JestResult;
import io.searchbox.client.config.HttpClientConfig;
import io.searchbox.client.http.JestHttpClient;
import io.searchbox.core.Index;
import io.searchbox.core.Search;
import org.elasticsearch.client.node.NodeClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.junit.After;
@ -39,16 +36,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration;
import org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchNodeTemplate;
import org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration;
import org.springframework.boot.test.util.TestPropertyValues;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@ -122,22 +113,31 @@ public class JestAutoConfigurationTests {
@Test
public void jestCanCommunicateWithElasticsearchInstance() throws IOException {
new File("target/elastic/logs").mkdirs();
load(HttpPortConfiguration.class,
"spring.data.elasticsearch.properties.path.home:target/elastic",
"spring.data.elasticsearch.properties.http.enabled:true",
"spring.data.elasticsearch.properties.http.port:0",
"spring.data.elasticsearch.properties.node.portsfile:true");
JestClient client = this.context.getBean(JestClient.class);
Map<String, String> source = new HashMap<>();
source.put("a", "alpha");
source.put("b", "bravo");
Index index = new Index.Builder(source).index("foo").type("bar").build();
client.execute(index);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("a", "alpha"));
assertThat(client.execute(new Search.Builder(searchSourceBuilder.toString())
.addIndex("foo").build()).getResponseCode()).isEqualTo(200);
new ElasticsearchNodeTemplate().doWithNode((node) -> {
load("spring.elasticsearch.jest.uris=http://localhost:" + node.getHttpPort());
JestClient client = this.context.getBean(JestClient.class);
Map<String, String> source = new HashMap<>();
source.put("a", "alpha");
source.put("b", "bravo");
Index index = new Index.Builder(source).index("foo").type("bar").build();
execute(client, index);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("a", "alpha"));
assertThat(
execute(client,
new Search.Builder(searchSourceBuilder.toString())
.addIndex("foo").build()).getResponseCode())
.isEqualTo(200);
});
}
private JestResult execute(JestClient client, Action<? extends JestResult> action) {
try {
return client.execute(action);
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}
private void load(String... environment) {
@ -199,53 +199,4 @@ public class JestAutoConfigurationTests {
}
@Configuration
@Import(ElasticsearchAutoConfiguration.class)
static class HttpPortConfiguration {
@Bean
public static BeanPostProcessor portPropertyConfigurer() {
return new PortPropertyConfigurer();
}
private static final class PortPropertyConfigurer
implements BeanPostProcessor, ApplicationContextAware {
private ConfigurableApplicationContext applicationContext;
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof NodeClient) {
this.applicationContext.getBean(JestProperties.class)
.setUris(Arrays.asList("http://localhost:" + readHttpPort()));
}
return bean;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext)
throws BeansException {
this.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
private int readHttpPort() {
try {
for (String line : Files
.readAllLines(Paths.get("target/elastic/logs/http.ports"))) {
if (line.startsWith("127.0.0.1")) {
return Integer
.parseInt(line.substring(line.indexOf(":") + 1));
}
}
throw new FatalBeanException("HTTP port not found");
}
catch (IOException ex) {
throw new FatalBeanException("Failed to read HTTP port", ex);
}
}
}
}
}

View File

@ -3712,19 +3712,10 @@ To take full control over the registration, define a `JestClient` bean.
[[boot-features-connecting-to-elasticsearch-spring-data]]
==== Connecting to Elasticsearch using Spring Data
You can inject an auto-configured `ElasticsearchTemplate` or Elasticsearch `Client`
instance as you would any other Spring Bean. By default the instance will embed a
local in-memory server (a `Node` in Elasticsearch terms) and use the current working
directory as the home directory for the server. In this setup, the first thing to do
is to tell Elasticsearch where to store its files:
[source,properties,indent=0]
----
spring.data.elasticsearch.properties.path.home=/foo/bar
----
Alternatively, you can switch to a remote server (i.e. a `TransportClient`) by setting
`spring.data.elasticsearch.cluster-nodes` to a comma-separated '`host:port`' list.
To connect to Elasticsearch you must provide the address of one or more cluster nodes.
The address can be specified by setting the `spring.data.elasticsearch.cluster-nodes`
property to a comma-separated '`host:port`' list. With this configuration in place, an
`ElasticsearchTemplate` or `TransportClient` can be injected like any other Spring bean:
[source,properties,indent=0]
----
@ -3736,9 +3727,8 @@ Alternatively, you can switch to a remote server (i.e. a `TransportClient`) by s
@Component
public class MyBean {
private ElasticsearchTemplate template;
private final ElasticsearchTemplate template;
@Autowired
public MyBean(ElasticsearchTemplate template) {
this.template = template;
}
@ -3748,8 +3738,8 @@ Alternatively, you can switch to a remote server (i.e. a `TransportClient`) by s
}
----
If you add a `@Bean` of your own of type `ElasticsearchTemplate` it will replace the
default.
If you add your own `ElasticsearchTemplate` or `TransportClient` `@Bean` it will
replace the default.

View File

@ -27,12 +27,6 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- Runtime -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<scope>runtime</scope>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>

View File

@ -1,6 +1 @@
#
# Home directory of the embedded Elasticsearch instance. Default to the
# current working directory.
#
spring.data.elasticsearch.properties.path.home=target/elastic
spring.data.elasticsearch.properties.transport.tcp.connect_timeout=120s
spring.data.elasticsearch.cluster-nodes=localhost:9200

View File

@ -18,6 +18,8 @@ package sample.data.elasticsearch;
import java.io.File;
import org.assertj.core.api.Assertions;
import org.elasticsearch.client.transport.NoNodeAvailableException;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
@ -28,8 +30,6 @@ import org.junit.runners.model.Statement;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.test.rule.OutputCapture;
import static org.assertj.core.api.Assertions.assertThat;
/**
* Tests for {@link SampleElasticsearchApplication}.
*
@ -45,9 +45,28 @@ public class SampleElasticsearchApplicationTests {
@Test
public void testDefaultSettings() throws Exception {
new SpringApplicationBuilder(SampleElasticsearchApplication.class).run();
try {
new SpringApplicationBuilder(SampleElasticsearchApplication.class).run();
}
catch (Exception ex) {
if (!elasticsearchRunning(ex)) {
return;
}
throw ex;
}
String output = this.outputCapture.toString();
assertThat(output).contains("firstName='Alice', lastName='Smith'");
Assertions.assertThat(output).contains("firstName='Alice', lastName='Smith'");
}
private boolean elasticsearchRunning(Exception ex) {
Throwable candidate = ex;
while (candidate != null) {
if (candidate instanceof NoNodeAvailableException) {
return false;
}
candidate = candidate.getCause();
}
return true;
}
static class SkipOnWindows implements TestRule {