Cannot connect to redis using spring and lettuce - spring-boot

I am struggling to find what could be wring here; need help.
I am using spring-data-redis 2.4.1
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration()
redisStandaloneConfiguration.setHostname(hostname)
redisStandaloneConfiguration.setPort(6379)
redisStandaloneConfiguration.setPassword("password")
I then create lettuceClientConfigurationBuilder and specify clientName
I then use lettuceClientConfiguration and redisStandaloneConfiguration to create ClientConnectionFactory.
However, when we call getConnection() on the connection Factory, we get
WrongPass Invalid username-password pair
The same set of username-password works with Redis-CLI on cmd prompt.
Is there is something wrong in the way I am using in my java application?
Any pointer/hint towards solving this would be greatly appreciated.

Spring Boot configures LettuceConnectionFactory for you, you can specify the connection params on the application.properties file.
spring.redis.database=0
spring.redis.host=localhost
spring.redis.port=6379
spring.redis.password=yourPassword
spring.redis.timeout=60000
If you wanna do it programmatically, set the spring.redis.password in application.properties and try this:
#Configuration
class AppConfig {
#Bean
public LettuceConnectionFactory redisConnectionFactory() {
return new LettuceConnectionFactory(new RedisStandaloneConfiguration("server", 6379));
}
}

I had mistaken username for clientname set on LettuceClientConfigurationBuilder but username had to be specified on the redisstandaloneconfiguratuon.
This works for me; also please note acl was introduced only after lettuce 2.4.1 so any prior version will not work.
redisStandaloneConfiguration.setUsername(connectionFactoryConfigs.getUserName());

Related

Include newly added data sources into route Data Source object without restarting the application server

Implemented Spring's AbstractRoutingDatasource by dynamically determining the actual DataSource based on the current context.
Refered this article : https://www.baeldung.com/spring-abstract-routing-data-source.
Here on spring boot application start up . Created a map of contexts to datasource objects to configure our AbstractRoutingDataSource. All these client context details are fetched from a database table.
#Bean
#DependsOn("dataSource")
#Primary
public DataSource routeDataSource() {
RoutingDataSource routeDataSource = new RoutingDataSource();
DataSource defaultDataSource = (DataSource) applicationContext.getBean("dataSource");
List<EstCredentials> credentials = LocalDataSourcesDetailsLoader.getAllCredentails(defaultDataSource); // fetching from database table
localDataSourceRegistrationBean.registerDataSourceBeans(estCredentials);
routeDataSource.setDefaultTargetDataSource(defaultDataSource);
Map<Object, Object> targetDataSources = new HashMap<>();
for (Credentials credential : credentials) {
targetDataSources.put(credential.getEstCode().toString(),
(DataSource) applicationContext.getBean(credential.getEstCode().toString()));
}
routeDataSource.setTargetDataSources(targetDataSources);
return routeDataSource;
}
The problem is if i add a new client details, I cannot get that in routeDataSource. Obvious reason is that these values are set on start up.
How can I achieve to add new client context and I had to re intialize the routeDataSource object.
Planning to write a service to get all the client context newly added and reset the routeDataSource object, no need to restart the server each time any changes in the client details.
A simple solution to this situation is adding #RefreshScope to the bean definition:
#Bean
#Primary
#RefreshScope
public DataSource routeDataSource() {
RoutingDataSource routeDataSource = new RoutingDataSource();
DataSource defaultDataSource = (DataSource) applicationContext.getBean("dataSource");
List<EstCredentials> credentials = LocalDataSourcesDetailsLoader.getAllCredentails(defaultDataSource); // fetching from database table
localDataSourceRegistrationBean.registerDataSourceBeans(estCredentials);
routeDataSource.setDefaultTargetDataSource(defaultDataSource);
Map<Object, Object> targetDataSources = new HashMap<>();
for (Credentials credential : credentials) {
targetDataSources.put(credential.getEstCode().toString(),
(DataSource) applicationContext.getBean(credential.getEstCode().toString()));
}
routeDataSource.setTargetDataSources(targetDataSources);
return routeDataSource;
}
Add Spring Boot Actuator as a dependency:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Then trigger the refresh endpoint POST to /actuator/refresh to update the DataSource (actually every refresh scoped bean).
So this will depend on how much you know about the datasources to be added, but you could set this up as a multi-tenant project. Another example of creating new datasources:
#Autowired private Map <String, Datasource> mars2DataSources;
public void addDataSourceAtRuntime() {
DataSourceBuilder dataSourcebuilder = DataSourcebuilder.create(
MultiTenantJPAConfiguration.class.getclassloader())
.driverclassName("org.postgresql.Driver")
.username("postgres")
.password("postgres")
.url("Jdbc: postgresql://localhost:5412/somedb");
mars2DataSources("tenantX", datasourcebuilder.build())
}
Given that you are using Oracle, you could also use its database change notification features.
Think of it as a listener in the JDBC driver that gets notified whenever something changes in your database table. So upon receiving a change, you could reinitialize/add datasources.
You can find a tutorial of how to do this here: https://docs.oracle.com/cd/E11882_01/java.112/e16548/dbchgnf.htm#JJDBC28820
Though, depending on your organization database notifications need some extra firewall settings for the communication to work.
Advantage: You do not need to manually call the REST Endpoint if something changes, (though Marcos Barberios answer is perfectly valid!)

how can I solve ldap socket closed error in spring LDAP?

I tried to make the sample program for my project. It is LDAP with spring boot.
I tested it in my development environment. Then, it works well. However, when I test it in the deployment environment, It occurs socket closed error.
The difference is just the LDAP URL and password(I couldn't make a password of admin with special characters, eg. #, #).
So, I tried to access LDAP using ldapsearch in deployment environment. Then, I got some errors. However, when I search for this error, I couldn't search a suitable solution for me.
This is my spring configuration for access to LDAP.
#Bean
public ContextSource contextSource() {
LdapContextSource contextSource = new LdapContextSource();
contextSource.setUrl("ldap://192.168.113.12");
contextSource.setBase("dc=test,dc=test");
contextSource.setUserDn("cn=admin,dc=test,dc=test");
contextSource.setPassword("test2019!#");
contextSource.afterPropertiesSet();
//for develop
// contextSource.setUrl("ldap://192.168.0.192");
// contextSource.setPassword("test2019");
PoolingContextSource pcs = new PoolingContextSource();
pcs.setDirContextValidator(new DefaultDirContextValidator());
pcs.setContextSource(contextSource);
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(pcs);
return proxy;
}
#Bean
public LdapTemplate ldapTemplate() {
return new LdapTemplate(contextSource());
}
This is error pictures when access to LDAP using spring LDAP.
This is error pictures using ldapsearch.
Help me.
ps. I didn't know how implemented the LDAP server, because it is installed by another team...
I would say the port is missing ;)
contextSource.setUrl("ldap://192.168.113.12:389");
Btw. in my opinion a nicer approach is to set the properties like this:
application.yml (or application.properties)
ldap:
contextSource:
url: ldap://192.168.113.12:389 #Local
base: dc=test,dc=test
userDn: cn=admin,dc=test,dc=test
password: test2019!#
and in the config class:
#Configuration
public class LdapConfiguration {
#Bean
#ConfigurationProperties(prefix="ldap.context-source")
public LdapContextSource contextSource() {
return new LdapContextSource();
}
#Bean
public ContextSource poolingLdapContextSource() {
PoolingContextSource pcs = new PoolingContextSource();
pcs.setDirContextValidator(new DefaultDirContextValidator());
pcs.setContextSource(contextSource());
TransactionAwareContextSourceProxy proxy = new TransactionAwareContextSourceProxy(pcs);
return proxy;
}
// other configs like ldaptemplate
}

Set heartbeatintervalseconds using spring xml

I am using spring-data-Cassandra v1.3.2 in my project.
Is it possible to set heartbeatintervalseconds using spring configuration XML file.
Getting 4 lines of hearbeat DEBUG logs every 30 seconds in my application logs and i am not sure how to avoid them.
Unfortunately, no.
After reviewing the SD Cassandra CassandraCqlClusterParser class, it is apparent that you can specify both "local" and "remote" connection pooling options, however, neither handler handles all the Cassandra Java driver "pooling options" appropriately (such as heartbeatIntervalSeconds).
It appears several other options are missing as well: idleTimeoutSeconds, initializationExecutor, poolTimeoutMillis, and protocolVersion.
Equally unfortunate is it appears the SD Cassandra PoolOptionsFactoryBean does not support these "pooling options" either.
However, not all is lost.
While your SD Cassandra application may resolve it's configuration primarily from XML, it does not preclude you from using a combination of Java config and XML.
For instance, you could use a Spring Java config class to configure your cluster and express your PoolingOptions in Java config...
#Configuration
#ImportResource("/class/path/to/cassandra/config.xml")
class CassandraConfig {
#Bean
PoolingOptions poolingOptions() {
PoolingOptions poolingOptions = new PoolingOptions();
poolingOptions.setHeartbeatIntervalSeconds(30);
poolingOptions.setIdleTimeoutSeconds(300);
poolingOptions.setMaxConnectionsPerHost(50);
poolingOptions.set...
return poolingOptions;
}
#Bean
CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean()
cluster.setContactPoints("..");
cluster.setPort(1234);
cluster.setPoolingOptions(poolingOptions());
cluster.set...
return cluster;
}
}
Hope this helps.
As an FYI, you may want to upgrade to the "current" Spring Data Cassandra version, 1.4.1.RELEASE.
Sadly, but the answer is no. It's not possible to configure the heartbeat interval using XML configuration. Only the following local/remote properties can be configured in PoolingOptions:
min-simultaneous-requests
max-simultaneous-requests
core-connections
max-connections
If you switch to Java-based configuration, then you're able to configure PoolingOptions by extending AbstractClusterConfiguration:
#Configuration
public class MyConfig extends AbstractClusterConfiguration {
#Override
protected PoolingOptions getPoolingOptions() {
PoolingOptions poolingOptions = new PoolingOptions();
poolingOptions.setHeartbeatIntervalSeconds(10);
return poolingOptions
}
}

how to modify tomcat8 acceptCount in spring boot

how to modify the tomcat default thread count using spring boot?
when i use spring mvc,i can find the tomcat,and modify the in conf/server.xml,then i modify the maxProcessors and acceptCount,but in spring boot i can't do that.
in org.apache.catalina.connector, i can't find the properties.
try to check what everything you can modify via properties: http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties
server.tomcat.max-threads = 0 # number of threads in protocol handler
otherwise you will have to get your hands dirty with programmatic configuration - http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#howto-configure-tomcat by providing your own TomcatEmbeddedServletContainerFactory
acceptCount not support to modify in properties files, you can you following code to modify:
#Bean
public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
TomcatEmbeddedServletContainerFactory tomcatFactory = new TomcatEmbeddedServletContainerFactory();
tomcatFactory.addConnectorCustomizers(new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
//tomcat default nio connector
Http11NioProtocol handler = (Http11NioProtocol)connector.getProtocolHandler();
//acceptCount is backlog, default value is 100, you can change which you want value in here
handler.setBacklog(100);
}
});
return tomcatFactory;
}
In current spring boot it should be possible through server.tomcat.accept-count application property, see: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#server-properties

Using Jackson as Jersey client serializer

Is it possible to use Jackson as the serializer/marshaller for JSON data instead of JAXB when using Jersey Client API?
If so how to configure it?
OK, I found it out, it turns out to be quite simple after all:
ClientConfig cc = new DefaultClientConfig();
cc.getClasses().add(JacksonJsonProvider.class);
Client clientWithJacksonSerializer = Client.create(cc);
The JacksonJsonProvider comes from the jackson-jaxrs package.
You may skip the creation of external config and register the provider directly:
Client client = ClientBuilder.newClient().register(JacksonJsonProvider.class)
Solution with JacksonJaxbJsonProvider
Common way how to use Jackson with custom configuration in Jersey client was to use JacksonJaxbJsonProvider for example like this
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(yourObjectMapper());
Client client = ClientBuilder.newClient(new ClientConfig(provider));
Unfortunately in Jersey 2.26 they copied JacksonJaxbJsonProvider class
from com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider artifact (Jackson)
to org.glassfish.jersey.media:jersey-media-json-jackson artifact (Jersey)
and changed package
from com.fasterxml.jackson.jaxrs.json
to org.glassfish.jersey.jackson.internal.jackson.jaxrs.json.
It is still possible to use this approach it's just needed to change JacksonJaxbJsonProvider import.
Apart from JacksonJaxbJsonProvider being now in internal package drawback is also
that you must know on which Jersey version your code runs which might be a problem when different dependencies require different Jersey versions.
Better solution with ContextResolver<ObjectMapper>
Better possibility how to configure Jackson in Jersey client is to use the same way how it is configured in Jersey server which is to create ObjectMapper provider like this:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class ObjectMapperProvider implements ContextResolver<ObjectMapper> {
private ObjectMapper objectMapper = yourObjectMapper();
#Override
public ObjectMapper getContext(Class<?> objectType) {
return objectMapper;
}
}
and use it for example like this:
ClientConfig clientConfig = new ClientConfig();
clientConfig.register(JacksonFeature.class); // usually auto-discovered
clientConfig.register(new ObjectMapperProvider());
Client client = ClientBuilder.newClient(clientConfig);
If you have both the server and the client you can reuse ObjectMapperProvider class.
It seems that this approach works from Jersey version 2.9.
You might also want to try org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider (jackson-jaxrs 1.6.1).
I ran into similar issue, but for me none of the suggestions given here worked.
What worked for me was below piece of code:
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.client.Client;
...
ClientBuilder clientBuilder = ClientBuilder.newBuilder()
clientBuilder.register(JacksonFeature.class);
...
Client client = clientBuilder.build();
The key change was usage of JacksonFeature.class - it comes from jersey-media-json-jackson-x.yy.jar
I got clue to use this solution from this article - http://www.baeldung.com/jersey-jax-rs-client
For jersey 2.22.2 and Jackson 2.7.2 gradle dependencies are:
dependencies {
compile("com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider:2.7.2")
compile("org.glassfish.jersey.core:jersey-client:2.22.2")
}
Sample client code is:
final String name = "world";
final Client client = ClientBuilder.newClient().register(JacksonJaxbJsonProvider.class);
final WebTarget target = client.target("http://localhost:8080").path("hello").path(name);
final Message message = target.request().get(Message.class);
System.out.println(message.getWelcomeMessage()); // hello world

Resources