mirror of
https://github.com/spring-projects/spring-boot.git
synced 2024-09-03 04:26:12 +08:00
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:
parent
dbabfc224c
commit
4a030d5a7a
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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.
|
||||
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user