Spring data mongodb reactive: use spring.data.mongodb.uri or implement AbstractReactiveMongoConfiguration.getDatabaseName - spring-boot

I'm reading on several sites, I need to configure something like that in order to get access to mongodb.
#EnableReactiveMongoRepositories
public class MongoReactiveApplication
extends AbstractReactiveMongoConfiguration {
#Override
protected String getDatabaseName() {
return "<database>";
}
}
I'm using:
spring:
data:
mongodb:
uri: mongodb://<user>:<password>#<rs1>:<port>,<rs2>:<port>,<rs3>:<port>/<database>
As you can see, I'm using spring.data.mongodb.uri property in order to set uri. Inside this value there is the <database>.
Why should I need to implement AbstractReactiveMongoConfiguration.getDatabaseName once again programatically?

Related

Setting basic auth in microprofile rest client

I have a service which exposes a number of Jax-RS interfaces for its services. I now want to use those interfaces to connect with the services. I am using Quarkus, which means I am using the microprofile rest client. Because I already have the JaxRS interface, using the #RegisterRestClient method is not really viable. Instead I am using the RestClientBuilder.
MyService client = RestClientBuilder.newBuilder()
.baseUri(URI.create("https://localhost:8080"))
.build(MyService.class);
The problem I am running into is authentication. The services i need to reach are locked behind basic Auth. All the guides I have found for the microprofile REST client are variations of this where the solution is to add a headerparam. This is not possible however, because I already have the interface premade, and copy-pasting the entire thing to add a header parameter is really something i would rather avoid.
It should also be mentioned that i have tried a #Provider filter to set the headers, but I can't seem to figure out how to only target a single REST client using that method, and I have several.
So: How do i set up basic authentication without messing with the Jax-Rs interface itself, using the microprofile rest client?
You should be able to use the #ClientHeaderParam annotation on MyService.
Something like:
#Path("/my")
#ClientHeaderParam(name = "Authorization", value = "{lookupAuth}")
public interface MyService {
default String lookupAuth() {
return "Basic " +
Base64.getEncoder().encodeToString("someuser:somepass".getBytes());
}
}
See this for more details
If modifying the interface is not possible, you have two options:
Create an interface that extends the one you use with this annotation:
#ClientHeaderParam(name = "Authorization", value = "{lookupAuth}")
public interface MyServiceWrapper extends MyService {
default String lookupAuth() {
return "Basic " + Base64.getEncoder().encodeToString("someuser:somepass".getBytes());
}
}
Create a ClientRequestFilter that fills the Authorization header:
#Priority(Priorities.AUTHENTICATION)
public class BasicRequestFilter implements ClientRequestFilter {
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
requestContext.getHeaders().add(HttpHeaders.AUTHORIZATION, getAccessToken());
}
private String getAccessToken() {
return "Basic " + Base64.getEncoder().encodeToString("someuser:somepass".getBytes());
}
}
And register the filter, e.g. programmatically:
MyService client = RestClientBuilder.newBuilder()
.register(BasicRequestFilter.class)
.baseUri(URI.create("https://localhost:8080"))
.build(MyService.class);
You can register a per-instance org.jboss.resteasy.client.jaxrs.internal.BasicAuthentication (or you can write a similar component) and register it using RestClientBuilder.

Spring Boot 2 + JdbcTemplate - is there a way to provide SQL query for each database supported?

I'm working on a Spring Boot 2.4.2 based project and using "spring-boot-starter-jdbc" and "com.oracle.database.jdbc" for Oracle Jdbc driver.
As I use JdbcTemplate to interact with DB, everything seems clean and easy. But I may need to support multiple database types in future - Oracle, SQL Server, MySQL, DB2, etc.
Did quite a bit of Googling but did not find any option for this..
Like mentioned above, I am using Spring-Jdbc (not Spring Data JDBC or Spring Data JPA) - how do I provide the SQL queries specific to each database supported in the code or configuration?
Please, let me know your thoughts. Thanks.
I'm not familiar with Spring JDBC, but you could user Spring dependency injection mechanism to create a profile for each database.
First an interface:
public interface DbQueries {
String createQueryForSelectingUsers();
}
Then implement the interface for each supported database:
#Profile("mysql")
#Component
public class MySqlDbQueries implements DbQueries {
#Override
public String createQueryForSelectingUsers() {
return "SELECT * FROM USER";
}
}
2nd example:
#Profile("oracle")
#Component
public class OracleDbQueries implements DbQueries {
#Override
public String createQueryForSelectingUsers() {
return "SELECT * FROM USER";
}
}
Afterwards use it where you need it:
public class MyRepository {
private DbQueries dbQueries;
// DbQueries implementation will be injected based on your current profile
public MyRepository(DbQueries dbQueries) {
this.dbQueries = dbQueries;
}
public void printAllUsers() {
String query = dbQueries.createQueryForSelectingUsers();
// stuff to execute query
}
}
Remember to start your app with a profile with e.q. --spring.profiles.active=mysql or adding active profile information into application.properties:
spring.profiles.active=mysql

Spring How to map list of custom pojo

In my application.yml file, I want to define a list of rules.
rules:
- name: abc
value: something
- name: edf
value: something
Then I want to define a service like this
#Service
public class MyService {
public MyService(#Value("${rules}") List<Rule> rules) {
}
}
For the Rule pojo, it's like this.
public class Rule {
public String name, value;
}
Currently, I'm facing these errors.
If I leave rules empty, it throws can't convert String to List<Rule>
rules: []
If I keep the values, it throws could not resolve placeholder 'rules'
I really don't know what I'm doing wrong here.
From Spring docs, I found this.
Using the #Value("${property}") annotation to inject configuration
properties can sometimes be cumbersome, especially if you are working
with multiple properties or your data is hierarchical in nature.
Spring Boot provides an alternative method of working with properties
that lets strongly typed beans govern and validate the configuration
of your application
Link: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
At the end, I have to introduce another class.
#Configuration
#ConfigurationProperties(prefix="rules")
public class Rules {
public List<Rule> list;
}
Then I autowire it in MyService.

Spring Data Key Value Implementation for Oracle KV

I would like to use Oracle NoSQL database together with Spring data. The aim is to access the data over spring data repositories and even use spring data rest on top of it.
So I think the spring-data-keyvalue project would help me, to implement an adapter for Oracle NoSQL KV.
I tried to understand the documentation of spring-data-keyvalue (http://docs.spring.io/spring-data/keyvalue/docs/current/reference/html/#key-value.core-concepts), but didn't get the idea.
An example/tutorial about how to implement an adapter from scratch would be very helpful.
What I have is this configuration class where I provide a custom KeyValueAdapter. Now if I use CrudRepository methods it uses my custom adapter.
#Configuration
#EnableMapRepositories
public class KeyValueConfig {
#Bean
public KeyValueOperations keyValueTemplate() {
return new KeyValueTemplate(new OracleKeyValueAdapter());
}
}
The OracleKeyValueAdapter is an implementation of KeyValueAdapter. I got this from the spring-data-keyvalue-redis project (https://github.com/christophstrobl/spring-data-keyvalue-redis/blob/master/src/main/java/org/springframework/data/keyvalue/redis/RedisKeyValueAdapter.java)
public class OracleKeyValueAdapter extends AbstractKeyValueAdapter {
private KVStore store;
public OracleKeyValueAdapter() {
String storeName = "kvstore";
String hostName = "localhost";
String hostPort = "5000";
store = KVStoreFactory.getStore
(new KVStoreConfig(storeName, hostName + ":" + hostPort));
}
//Custom implementations:
#Override
public Object put(Serializable serializable, Object o, Serializable
serializable1) {
return null;
}
#Override
public boolean contains(Serializable serializable, Serializable
serializable1) {
return false;
}
.
.
.
Now I'm trying to implement this OracleKeyValueAdapter, but i don't know if that does even make sense.
Can you help me?
You might want to start with how spring-data-keyvalue is implemented over Redis, the link here should be a good starting point - http://docs.spring.io/spring-data/data-keyvalue/docs/1.0.0.BUILD-SNAPSHOT/reference/redis.html
Let me know how that goes, I am interested in what you are trying to accomplish.
The following configuration should work (tested on v2.4.3)
#Configuration
#EnableMapRepositories
public class Configuration {
#Bean
public KeyValueOperations mapKeyValueTemplate() {
return new KeyValueTemplate(keyValueAdapter());
}
#Bean
public KeyValueAdapter keyValueAdapter() {
return new YourKeyValueAdapter();
}
}
The name (mapKeyValueTemplate) of the KeyValueOperations bean is important here but it can also be changed as followed:
#Configuration
#EnableMapRepositories(keyValueTemplateRef = "foo")
public class Configuration {
#Bean
public KeyValueOperations foo() {
return new KeyValueTemplate(keyValueAdapter());
}
#Bean
public KeyValueAdapter keyValueAdapter() {
return new YourKeyValueAdapter();
}
}
I saw sources of Spring KeyValue Repository:
https://github.com/spring-projects/spring-data-keyvalue
I recomend to understand, how Spring Repository work inside.
If you want to realise own repository (CustomKeyValueRepository), you must create at least 6 classes:
EnableCustomKeyValueRepositories - annotation to enable repository type in your project.
CustomKeyValueRepositoriesRegistrar - registrator for this annotaion.
CustomKeyValueRepository - repository
CustomKeyValueRepositoryConfigurationExtension - implementation of Spring ConfigurationExtension.
CustomKeyValueAdapter - implementation of custom adapter for your data store.
CustomKeyValueConfiguration - configuration of beans Adapter and Template.
I code Infinispan KeyValue Repository by this way:
https://github.com/OsokinAlexander/infinispan-spring-repository
I also write article about this:
https://habr.com/ru/post/535218/
In Chrome you can translate it to your language.
The simplest way you can try implement only CustomKeyValueAdapter and Configuration. In Configuration you must redefine Spring KeyValueAdapter bean and KeyValueTemplate (it is very important that the name of the bean is with a lowercase letter, that's the only way it worked for me):
#Configuration
public class CustomKeyValueConfiguration extends CachingConfigurerSupport {
#Autowired
private ApplicationContext applicationContext;
#Bean
public CustomKeyValueAdapter getKeyValueAdapter() {
return new CustomKeyValueAdapter();
}
#Bean("keyValueTemplate")
public KeyValueTemplate getKeyValueTemplate() {
return new KeyValueTemplate(getKeyValueAdapter());
}
}

Spring-boot ConditionalOnProperty with map-based properties

My spring-boot yaml properties look like this:
service:
mycomponent:
foo:
url: http://foo
bar:
url: http://bar
This results in the following properties being set in the Spring environment:
service.mycomponent.foo.url: http://foo
service.mycomponent.bar.url: http://bar
I'd like to define a 'mycomponent' bean if there are any properties that match service.mycomponent.[a-z]*.url. Is this possible using #ConditionalOnExpression or some other type of #Conditional?
I realize I can work around this by either adding a property such as service.mycomponent.enabled: true that could be used with #ConditionalOnProperty but I'd rather avoid that if possible.
Here's the solution I ended up taking:
Create a custom Condition which searches for any properties with a certain prefix. The RelaxedPropertyResolver has the convenient getSubProperties() method. Alternative options I found were cumbersome to iterate through the PropertySource instances.
public class MyComponentCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(final ConditionContext context,
final AnnotatedTypeMetadata metadata) {
final RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
final Map<String, Object> properties = resolver.getSubProperties("service.mycomponent.");
return new ConditionOutcome(!properties.isEmpty(), "My Component");
}
}
Use that condition when setting up the bean:
#Conditional(MyComponentCondition.class)
#Bean
public MyComponent myComponent() {
return new MyComponent();
}
I'm still curious if the same thing could be done with #ConditionalOnExpression directly.

Resources