I'm configuring mongoDB in my spring boot application like this:
#Configuration
#SpringBootApplication
public class ConfigDbApp extends SpringBootServletInitializer {
#Value("${myapp.mongodb.uri}")
private String mongoDbUri;
[...]
#Bean
public MongoClient mongoClient() {
MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(mongoDbUri.trim()))
.build();
return MongoClients.create(mongoClientSettings);
}
[...]
}
The database uri is:
myapp.mongodb.uri=mongodb://localhost:27017/myApp?sslInvalidHostNameAllowed=true&streamType=netty&ssl=false
The connection is fine but instead of connecting to "myApp" database it connect to "test" database (the default).
If I use the spring property:
spring.data.mongodb.uri=mongodb://localhost:27017/myApp?sslInvalidHostNameAllowed=true&streamType=netty&ssl=false
it works well, but I don't want to use it because I can have diferent apps in the same server reading the same configuration file.
Any suggestion is appreciated, thanks.
This worked with me : override the database name
#Configuration
public class MongoConfig extends AbstractMongoClientConfiguration {
#Value("${spring.data.mongodb.uri}")
private String uri;
#Value("${spring.data.mongodb.database}")
private String database;
#Override
protected String getDatabaseName() {
return database;
}
#Bean
#Override
public MongoClient mongoClient() {
final ConnectionString connectionString = new ConnectionString(uri);
final MongoClientSettings.Builder mongoClientSettings = MongoClientSettings.builder().applyConnectionString(connectionString)
.applyToConnectionPoolSettings(builder -> builder.applySettings(connectionPoolSettings()));
return MongoClients.create(mongoClientSettings.build());
}
private ConnectionPoolSettings connectionPoolSettings() {
return ConnectionPoolSettings.builder()
.maxSize(50)
.maxWaitTime(20, TimeUnit.SECONDS)
.maxConnectionIdleTime(20, TimeUnit.SECONDS)
.maxConnectionLifeTime(60, TimeUnit.SECONDS).build();
}
}
Related
I'm working on a project whereby my spring service needs to connect to multiple Mongo DB's.
In order to achieve the ability to connect to multiple mongo db's I've created a MongoDatabaseFactory config class to represent each connection. I'am however getting random mongo SSL timeout errors. Normally when I would connect to a single mongo db I would create the client like so:
#Value("${spring.data.mongodb.host}")
private String connectionString;
#Bean
public MongoClient mongoClient() {
CodecRegistry pojoCodecRegistry = fromProviders(PojoCodecProvider.builder().automatic(true).build());
CodecRegistry codecRegistry = fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), pojoCodecRegistry);
return MongoClients.create(MongoClientSettings.builder()
.applyConnectionString(new ConnectionString(connectionString))
.uuidRepresentation(UuidRepresentation.STANDARD)
.codecRegistry(codecRegistry)
.build());
}
#Bean
public MongoClientSettings mongoClientSettings() {
final MongoClientSettings clientSettings = MongoClientSettings.builder()
.retryWrites(true)
.applyToConnectionPoolSettings((ConnectionPoolSettings.Builder builder) -> {
builder.maxSize(300) //connections count
.minSize(100)
.maxConnectionLifeTime(300, TimeUnit.MILLISECONDS)
.maintenanceFrequency(5000, TimeUnit.MILLISECONDS)
.maxConnectionIdleTime(200, TimeUnit.MILLISECONDS)
.maxWaitTime(150, TimeUnit.MILLISECONDS);
})
.applyToSocketSettings(builder -> {
builder.connectTimeout(2000, TimeUnit.MILLISECONDS)
.readTimeout(5500, TimeUnit.MILLISECONDS);
})
.applicationName("TestApplication")
.retryWrites(true)
.build();
return clientSettings;
}
#Bean
public MongoCustomConversions mongoCustomConversions() {
return new MongoCustomConversions(Arrays.asList(
new BigDecimalDecimal128Converter(),
new Decimal128BigDecimalConverter()
));
}
#WritingConverter
private static class BigDecimalDecimal128Converter implements Converter<BigDecimal, Decimal128> {
#Override
public Decimal128 convert(#NonNull BigDecimal source) {
return new Decimal128(source);
}
}
#ReadingConverter
private static class Decimal128BigDecimalConverter implements Converter<Decimal128, BigDecimal> {
#Override
public BigDecimal convert(#NonNull Decimal128 source) {
return source.bigDecimalValue();
}
}
And when connecting to multiple mongo db's here's how I do it:
#Configuration
public class UserProfileInformationMongoConfig {
#Autowired
private Environment env;
#Bean
public MongoDatabaseFactory userProfileInformationFactory() {
return new SimpleMongoClientDatabaseFactory(new ConnectionString(env.getProperty("spring.data.mongo.userprofileinformationdb.uri")));
}
#Bean
public MongoTemplate userProfileInformationMongoTemplate() {
return new MongoTemplate(userProfileInformationFactory());
}
}
So, my question again is how do I set my mongo client options like in the first code example when using MongoDatabaseFactory.
Many thanks
Except the maintenanceFrequency configuration, the rest can be provided in the connection string e.g.
mongodb://db1.example.net:27017,db2.example.net:2500/?replicaSet=test&maxPoolSize=300&minPoolSize=100&maxLifeTimeMS=300&maxIdleTimeMS=200&waitQueueTimeoutMS=150&connectTimeoutMS=2000&socketTimeoutMS=5500
I'm getting this exception when adding Transactional to Spring mongo code.
Caused by: com.mongodb.MongoClientException: Transactions are not supported by the MongoDB cluster to which this client is connected
This is my code: #Configuration
#EnableMongoRepositories(basePackages = { "com.repository" })
public class MongoConfig extends AbstractMongoClientConfiguration {
#Value("${spring.data.mongodb.uri}")
private String connection;
#Value("${spring.data.mongodb.database}")
private String database;
#Autowired
MongoProperties mongoProperties;
#Bean
MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
#Override
protected String getDatabaseName() {
return "hendrix";
}
#Override
public MongoClient mongoClient() {
final ConnectionString connectionString = new ConnectionString(connection);
final MongoClientSettings mongoClientSettings = MongoClientSettings.builder()
.applyConnectionString(connectionString)
.build();
return MongoClients.create(mongoClientSettings);
}
}
How do I add replicas to a barebones Mongo setup I have access to? If the cluster is on AWS what setting would I have to change?
I am trying to use an ApiClient generated by swagger-codegen-maven-plugin (Version 3.0.0), to consume an OAuth2 secured REST API from within my spring boot application. The auth server (keycloak) provides a JWT and refresh token, but I cannot figure out how to best handle tokens in my bean.
At the moment my bean looks like this:
#Configuration
public class SomeApiClientConfiguration {
#Bean
public SomeApi someApi() {
return new SomeApi(apiClient());
}
#Bean
public ApiClient apiClient() {
ApiClient apiClient = new ApiClient();
OAuth oAuth = (OAuth) apiClient.getAuthentication("auth");
oAuth.setAccessToken("");
return apiClient;
}
}
Question is: What is the best approach for getting the token and handling the refresh token?
EDIT: In order to get the token I want to use client ID, username, and password. Grant type: Password Credentials.
Best,
Marc
I was able to solve this problem and want to share the solution for future reference:
This is my SomeApiClientConfiguration:
#Configuration
public class SomeApiClientConfiguration{
#Value("${app.api.url}")
private String apiURL;
#Bean
public SomeApi someApi(OAuth2RestTemplate restTemplate) {
return new SomeApi(apiClient(restTemplate));
}
#Bean
public ApiClient apiClient(OAuth2RestTemplate restTemplate) {
var apiClient = new ApiClient(restTemplate);
apiClient.setBasePath(apiURL);
return apiClient;
}
}
Additionally I needed a SomeApiOAuth2Config class, which look as follows:
#Configuration
#EnableOAuth2Client
public class SomeApiOAuth2Config {
#Value("${app.api.client-id}")
private String clientId;
#Value("${app.api.token-endpoint}")
private String accessTokenUri;
#Value("${app.api.name}")
private String username;
#Value("${app.api.password}")
private String password;
#Bean
public ClientHttpRequestFactory httpRequestFactory() {
return new HttpComponentsClientHttpRequestFactory(httpClient());
}
#Bean
public HttpClient httpClient() {
var connectionManager = new PoolingHttpClientConnectionManager();
var maxPoolSize = 1;
connectionManager.setMaxTotal(maxPoolSize);
// This client is for internal connections so only one route is expected
connectionManager.setDefaultMaxPerRoute(maxPoolSize);
return HttpClientBuilder.create().setConnectionManager(connectionManager).build();
}
#Bean
public OAuth2ProtectedResourceDetails oauth2ProtectedResourceDetails() {
var details = new ResourceOwnerPasswordResourceDetails();
var resourceId = "";
details.setId(resourceId);
details.setClientId(clientId);
var clientSecret = "";
details.setClientSecret(clientSecret);
details.setAccessTokenUri(accessTokenUri);
details.setClientAuthenticationScheme(AuthenticationScheme.form);
return details;
}
#Bean
public AccessTokenProvider accessTokenProvider() {
var tokenProvider = new ResourceOwnerPasswordAccessTokenProvider();
tokenProvider.setRequestFactory(httpRequestFactory());
return new AccessTokenProviderChain(
Collections.<AccessTokenProvider>singletonList(tokenProvider)
);
}
#Bean
#Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public OAuth2RestTemplate restTemplate(#Qualifier("oauth2ClientContext") OAuth2ClientContext oauth2ClientContext) {
var template = new OAuth2RestTemplate(oauth2ProtectedResourceDetails(), oauth2ClientContext);
template.setRequestFactory(httpRequestFactory());
template.setAccessTokenProvider(accessTokenProvider());
template.getOAuth2ClientContext().getAccessTokenRequest().set("username", username);
template.getOAuth2ClientContext().getAccessTokenRequest().set("password", password);
return template;
}
}
I am trying to use both Cassandra and MySQL in my project. Some data will be saved into Cassandra and some to Mysql. I had been using mySql for last 1 yr in the same project and now since I'm Expanding it, I want to add Cassandra DB also.
My Cassandra Configuration file is as follows.
#Configuration
#PropertySource(value = {"classpath:META-INF/application.properties"})
#EnableCassandraRepositories(basePackages = {"com.example.repository"})
public class CassandraConfig {
#Autowired
private Environment environment;
private static final Logger LOGGER = LoggerFactory.getLogger(CassandraConfig.class);
#Bean
public CassandraClusterFactoryBean cluster() {
CassandraClusterFactoryBean cluster = new CassandraClusterFactoryBean();
cluster.setContactPoints(environment.getProperty("spring.cassandra.contactpoints"));
cluster.setPort(Integer.parseInt(environment.getProperty("spring.cassandra.port")));
return cluster;
}
#Bean
public CassandraMappingContext mappingContext() {
return new BasicCassandraMappingContext();
}
#Bean
public CassandraConverter converter() {
return new MappingCassandraConverter(mappingContext());
}
#Bean
public CassandraSessionFactoryBean session() throws Exception {
CassandraSessionFactoryBean session = new CassandraSessionFactoryBean();
session.setCluster(cluster().getObject());
session.setKeyspaceName(environment.getProperty("spring.cassandra.keyspace"));
session.setConverter(converter());
session.setSchemaAction(SchemaAction.NONE);
return session;
}
#Bean
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session().getObject());
}
}
My Repository is
public interface NewRepository extends CassandraRepository<ID>{
}
Now I'm trying to save an entity to it using the reposiroty
repo.save(entity);
where repo is the object for NewRepository.
But it shows InvalidDataAccessApiUsageException: unknown Type.
Where am i wrong.
Thank You in advance.
I use javaee 8 for my project and have this classes :
public class PersonRepository {
#Inject
#Mongo(collection = "users")
private MongoCollection collection;
// define some methods ....
}
#ApplicationScoped
public class MongoProducer {
#Inject
private MongoClient mongoClient;
#Produces
#Mongo
protected MongoCollection produceCollection(InjectionPoint ip) {
Mongo mongo = getMongoAnnotation(ip);
return mongoClient.getDatabase("sample").getCollection(mongo.collection());
}
private Mongo getMongoAnnotation(InjectionPoint ip) {
return ip.getAnnotated().getAnnotation(Mongo.class);
}
}
#MongoClientDefinition(
name = "mongoClient",
dbName = "sample",
username = "admin",
password = "adminpass"
)
public class MongoConnectionConfig {
}
Unfortunately mongoClient has not any method for get database name currently connected to that. (maybe i cant find that !)
How can scan MongoClientDefinition annotation from produceCollection method ?
I want set database name from that annotation . (replace "sample") .
Note : I created CDI extension for MongoClientDefinition .
simple idea is BeanManager :
I modified my extension and add getter method for dbName .
full project on github
#ApplicationScoped
public class MongoProducer {
#Inject
private MongoClient mongoClient;
#Inject
private BeanManager bm;
#Produces
#Mongo
protected MongoCollection produceCollection(InjectionPoint ip) {
String dbName = bm.getExtension(MongoClientExtension.class).getDatabaseName();
Mongo mongo = getMongoAnnotation(ip);
return mongoClient.getDatabase(dbName).getCollection(collectionName,mongo.collection());
}
private Mongo getMongoAnnotation(InjectionPoint ip) {
return ip.getAnnotated().getAnnotation(Mongo.class);
}
}