Best Way to connect from spring boot to Elasticsearch without deprecations - spring-boot

#Bean
public RestHighLevelClient elasticsearchClient() {
ClientConfiguration clientConfiguration
= ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
}
#Bean
public Client client() throws Exception {
Settings settings = Settings.builder().put("cluster.name", esClusterName).build();
TransportClient client = new TransportClient(settings);
client.addTransportAddress(new TransportAddress(InetAddress.getByName(esHost), esPort));
return client;
}
I have been trying to connect to elasticsearch from spring boot using following techniques:
RestHighLevelClient & TransportClient
Custom binary port : 9300
as these techniques are deprecated.
Please help me with best recommended solution to this.
Thanks,
Dilip

You can probably allow the auto-configuration to create the client for you, so it isn't necessary to create the bean. Set the configuration properties if needed:
spring.elasticsearch.rest.uris=http://localhost:9300
All properties can be found here.

Related

Java JobRunr when using Spring Boot Redis Starter

How do I create and use the Redis connection that spring-boot-starter-data-redis creates? It doesn't seem like there is a Bean for RedisClient created by the default auto configuration so I'm not sure of the best way to do this.
The documentation does state that in this case you need to create the StorageProvider yourself which is fine, but can you reuse what Spring Boot has already created. I believe this would need to be a pooled connection which you would also need to enable through Spring Boot.
RedisTemplate offers a high-level abstraction for Redis interactions:
https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#redis:template
Redis autoconfiguration :
#AutoConfiguration
#ConditionalOnClass({RedisOperations.class})
#EnableConfigurationProperties({RedisProperties.class})
#Import({LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class})
public class RedisAutoConfiguration {
public RedisAutoConfiguration() {
}
#Bean
#ConditionalOnMissingBean(
name = {"redisTemplate"}
)
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
#Bean
#ConditionalOnMissingBean
#ConditionalOnSingleCandidate(RedisConnectionFactory.class)
public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
return new StringRedisTemplate(redisConnectionFactory);
}
}
Here you can find the corresponding configuration properties(including connection pool default configuration).
Simple implementation example :
https://www.baeldung.com/spring-data-redis-tutorial

Spring data elasticsearch 6.8.5 how config HTTP port

I use Elasticsearch version 6.8.5 with 9201 is HTTP port and 9301 is port of cluster node.
On my project, I use spring boot (spring-boot-starter-data-elasticsearch). On application.properties file, I set port of cluster node:
spring.data.elasticsearch.cluster-nodes=localhost:9301
But I don't know how to set HTTP port. So when I start my project, I get an error:
NoNodeAvailableException[None of the configured nodes are available: [{#transport#-1}{de81Kcj-QUqTRdA9HskFWg}{localhost}{localhost:9301}]];
I tried to use High Level REST Client setting (https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#elasticsearch.clients.rest), but it still not work:
#Configuration
public class ElasticsearchConfig {
#Bean(destroyMethod = "close")
public RestHighLevelClient restClient1() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
RestClientBuilder builder = RestClient.builder(new HttpHost("localhost", 9201));
RestHighLevelClient client = new RestHighLevelClient(builder);
return client;
}
}
How can I config HTTP port (not default port)?
You should use the following code:
#Bean
public RestHighLevelClient elasticsearchClient() {
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9201")
// if you need basic authentication:
.withBasicAuth("user", "password")
.build();
return RestClients.create(clientConfiguration).rest();
}
Edit:
Check the complete configuration from the docs, when using a custom configuration like this, I'd recommend to not let Spring Boot try to configure Spring Data Elasticsearch. You can do this by changing the annotation on your application class to
#SpringBootApplication(exclude = ElasticsearchDataAutoConfiguration.class)

setting timeout using #ConfigurationProperties in SpringBoot

I am going through a code that configures dedicated restTemplate for a rest operation. I see the following properties
httpProperties.connection-request-timeout=6100
httpProperties.connect-timeout=6100
httpProperties.read-timeout=6100
My Config class looks like below
#Bean
#ConfigurationProperties(prefix = "httpProperties")
public HttpComponentsClientHttpRequestFactory webSystemHttpRequestFactory() {
SSLContext sslContext;
try {
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);
HttpClient httpClient = HttpClients.custom().setMaxConnTotal(maxTotalConnection)
.setMaxConnPerRoute(maxConnectionPerRoute).setSSLSocketFactory(socketFactory).build();
return new HttpComponentsClientHttpRequestFactory(httpClient);
}
catch(Exception e) {
}
return new HttpComponentsClientHttpRequestFactory();
}
#Bean(name = "webSystemRestTemplate")
public RestTemplate webSystemRestTemplate() {
RestTemplate restTemplate = new RestTemplate(webSystemHttpRequestFactory());
return restTemplate;
}
I can see the logs
o.a.h.i.c.DefaultManagedHttpClientConnection.setSocketTimeout - http-outgoing-1: set socket timeout to 6100
Here is what i want to understand:
How is this value set and to which property by the #CnfigurationProperties annotation?
Is it applicable at the spring boot application level or at each request level?
Please help me understand the concept underlying.
Note: Apache http client version used is 4.5.2
In the source code for HttpComponentsClientHttpRequestFactory.class there is an object called RequestConfig.class.
In it's source code you can see that there are three parameters.
private final Timeout connectionRequestTimeout;
private final Timeout connectTimeout;
private final Timeout responseTimeout;
These are the ones that the parameters map to using
#ConfigurationProperties(prefix = "httpProperties")
That is not the most common way to set these parameters. But there are multiple ways to set these as pointed out here.
RestTemplate timeout examples
The properties are setting the attributes connectionRequestTimeOut, connectTimeOut and readTimeOut of the HttpComponentsClientHttpRequestFactory class. The mapping is done using the ConfigurationProperties annotation that maps the kebab case property names to the bean attributes.
HttpComponentsClientHttpRequestFactory documentation :
HttpComponentsClientHttpRequestFactory

Sending a message to an embedded HornetQ from an external application

I am using spring-boot 1.2.2.
I have an embedded hornet queue setup in application.properties:
spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=myQueue
I want to add a message to "myQueue" from an external application (not the one with the embedded queue). Is this possible?
In the other application (the one without the embedded hornetq), I tried creating a connectionFactory that points to the embedded hornetq server but I don't really know what port I should be using. According to the spring-boot documentation it says it is only valid for "native" mode.
spring.hornetq.mode= # connection mode (native, embedded)
spring.hornetq.host=localhost # hornetQ host (native mode)
spring.hornetq.port=5445 # hornetQ port (native mode)
here is my code so far:
#EnableJms
#Configuration
public class HornetQConfig {
#Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory cachingConnectionFactory =
new CachingConnectionFactory();
cachingConnectionFactory.setSessionCacheSize(10);
cachingConnectionFactory.setCacheProducers(false);
cachingConnectionFactory.setTargetConnectionFactory(hornetQConnectionFactory());
return cachingConnectionFactory;
}
#Bean
public HornetQConnectionFactory hornetQConnectionFactory() {
HornetQConnectionFactory connectionFactory =
new HornetQConnectionFactory(false, transportConfiguration());
return connectionFactory;
}
#Bean
public TransportConfiguration transportConfiguration() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("host", "localhost");
map.put("port", 5445);
TransportConfiguration configuration =
new TransportConfiguration(
"org.hornetq.core.remoting.impl.netty.NettyConnectorFactory", map);
return configuration;
}
}
And then:
#Autowired
private JmsTemplate jmsTemplate;
#Scheduled(fixedDelay = 1000L)
public void send() {
this.jmsTemplate.convertAndSend("myQueue", "Hello from external app");
}
But I am getting a connection problem.
Failed to create session factory; nested exception is HornetQNotConnectedException[errorType=NOT_CONNECTED message=HQ119007: Cannot connect to server(s)
The issue is that the embedded HornetQ server is configured with only an InVMAcceptorFactory by default. You need to add an AcceptorFactory that actually listens on a port, like NettyAcceptorFactory.
You can use the HornetQConfigurationCustomizer to configure this. Below example uses a hardcoded host/port, but you can easily create your own properties to make this configurable.
#Bean
public HornetQConfigurationCustomizer hornetCustomizer() {
return new HornetQConfigurationCustomizer() {
#Override
public void customize(Configuration configuration) {
Set<TransportConfiguration> acceptors = configuration.getAcceptorConfigurations();
Map<String, Object> params = new HashMap<String, Object>();
params.put("host", "localhost");
params.put("port", "5445");
TransportConfiguration tc = new TransportConfiguration(NettyAcceptorFactory.class.getName(), params);
acceptors.add(tc);
}
};
}
In your application with the embedded server, you configure it as embedded (as I believe you already have anyway, just to make sure):
spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.queues=myQueue
And in your "other" application that you want to connect to the embedded server, you configure HornetQ in native mode:
spring.hornetq.mode=native
spring.hornetq.host=localhost
spring.hornetq.port=5445

How to redirect automatically to https with Spring Boot

How I can easily configure the embedded tomcat server to redirect all http traffic to https? I have Spring Boot running on an ec2 instance that is behind an elastic load balancer. I have configured the ELB to handle ssl for me (which is awesome) and it sets the X-FORWARDED-PROTO header to "https". I want to detect when that isn't set, and redirect the user to force them to use https if they aren't already.
So far, I have tried adding the following to my application.properties file with no luck:
server.tomcat.protocol-header=x-forwarded-proto
security.require-ssl=true
My answer is a little late but I just recently had this problem and want to post a solution which worked for me.
Originally, I thought that setting tomcat up to use the X-Forwarded headers would suffice but the RemoteIPValve from Tomcat, which should normally handle this case, didnt work for me.
My solution was to add an EmbeddedServletContainerCustomizer and add a ConnectorCustomizer:
(note that I am using Tomcat 8 here)
#Component
public class TomcatContainerCustomizer implements EmbeddedServletContainerCustomizer {
private static final Logger LOGGER = LoggerFactory.getLogger(TomcatContainerCustomizer.class);
#Override
public void customize(final ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
final TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container;
tomcat.addConnectorCustomizers(connector -> {
connector.setScheme("https");
connector.setProxyPort(443);
});
LOGGER.info("Enabled secure scheme (https).");
} else {
LOGGER.warn("Could not change protocol scheme because Tomcat is not used as servlet container.");
}
}
}
The important thing is that you not only set the Scheme to https but also the ProxyPort without which all internal redirects from Spring Boot were routed to port 80.
The configuration property security.require-ssl doesn't work when basic authentication is disabled (at least on old versions of Spring Boot). So you probably need to secure all the requests manually with code similar to this one:
#Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
#Inject private SecurityProperties securityProperties;
#Override
protected void configure(HttpSecurity http) throws Exception {
if (securityProperties.isRequireSsl()) http.requiresChannel().anyRequest().requiresSecure();
}
}
You can check my full answer here: Spring Boot redirect HTTP to HTTPS
You will need a keystore file and few config classes.
The below link explains it in detail.
Https on embedded tomcat
Spring Boot 2.0 redirection of http to https:
Add the following to the #Configuration
#Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
#Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector(
TomcatServletWebServerFactory.DEFAULT_PROTOCOL);
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}

Resources