im trying to connect my spring boot app to mongodb using ssl. I followed the steps described here, but they dont work for me.
https://www.compose.com/articles/how-to-connecting-to-compose-mongodb-with-java-and-ssl/
any idea?
Thanks Alem
I would suggest that you look at Accessing Data with MongoDB available here https://spring.io/guides/gs/accessing-data-mongodb/ for basic usage examples. spring-boot-starter-data-mongodb will get you a long way, what you need to do is configure a MongoClientOptions bean like this
#Bean
public MongoClientOptions mongoClientOptions(){
System.setProperty ("javax.net.ssl.keyStore","<<PATH TO KEYSTOR >>");
System.setProperty ("javax.net.ssl.keyStorePassword","PASSWORD");
MongoClientOptions.Builder builder = MongoClientOptions.builder();
MongoClientOptions options=builder.sslEnabled(true).build();
return options;
}
and pass the mongo client options to MongoClient instance as an argument as follows
public MongoClient(ServerAddress addr, MongoClientOptions options) {
super(addr, options);
}
Adding further, when mongo processs is started with
mongo --ssl --sslAllowInvalidCertificates --host --port
clients connecting to the mongo process dont have to set any options to support this.
I used this post Spring data mongodb, how to set SSL? and this spring.io guide as reference.
Hope that it helps
If you just want to connect your spring boot app with mongodb, you can use the keyStore and trustStore with java code. So you dont have to add your certificate via command line. If you are using cloud foundry you can connect your app with mongodbServices and then you have all the credentials you need in System.getEnv("VCAP_SERVICES").
#Configuration
public class MongoConfiguration extends AbstractMongoConfiguration {
private static Log logger = LogFactory.getLog(MongoConfiguration.class);
#Value("${spring.data.mongodb.database}")
private String defaultDatabase; //database you want to connect
private String host;
private int port;
private String authenticationDb; //usually admin
private String username;
private char[] password;
private String certificateDecoded; //your CA Certifcate decoded (starts with BEGIN CERTIFICATE)
public MongoConfiguration() {
//method for credentials initialization
}
//you can't set replicaset=replset in mongooptions so if you want set replicaset, you have to use
// customEditorConfigurer in combintaion with class that implementsPropertyEditorRegistrar
#Bean
public static CustomEditorConfigurer customEditorConfigurer(){
CustomEditorConfigurer configurer = new CustomEditorConfigurer();
configurer.setPropertyEditorRegistrars(
new PropertyEditorRegistrar[]{new ServerAddressPropertyEditorRegistrar()});
return configurer;
}
#Override
protected String getDatabaseName() {
return authenticationDb;
}
#Override
#Bean
public MongoClient mongoClient() {
MongoClient mongoClient = new MongoClient(Arrays.asList(new ServerAddress(host, port)), mongoCredentials(), mongoClientOptions());
return mongoClient;
}
#Bean
public MongoClientOptions mongoClientOptions() {
MongoClientOptions.Builder mongoClientOptions = MongoClientOptions.builder().sslInvalidHostNameAllowed(true).sslEnabled(true);
try {
InputStream inputStream = new ByteArrayInputStream(certificateDecoded.getBytes(StandardCharsets.UTF_8));
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate caCert = (X509Certificate) certificateFactory.generateCertificate(inputStream);
TrustManagerFactory trustManagerFactory = TrustManagerFactory
.getInstance(TrustManagerFactory.getDefaultAlgorithm());
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null); // You don't need the KeyStore instance to come from a file.
keyStore.setCertificateEntry("caCert", caCert);
trustManagerFactory.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, trustManagerFactory.getTrustManagers(), null);
mongoClientOptions.sslContext(sslContext);
mongoClientOptions.sslInvalidHostNameAllowed(true);
} catch (Exception e) {
throw new IllegalStateException(e);
}
return mongoClientOptions.build();
}
private MongoCredential mongoCredentials() {
return MongoCredential.createCredential(username, authenticationDb, password);
}
//With MongoTemplate you have access to db.
#Bean
public MongoTemplate mongoTemplate() {
SimpleMongoDbFactory factory = new SimpleMongoDbFactory(mongoClient(), defaultDatabase);
return new MongoClient(factory);
}
}
public final class ServerAddressPropertyEditorRegistrar implements PropertyEditorRegistrar {
#Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(ServerAddress[].class, new ServerAddressPropertyEditor());
}
}
Related
We have a mongo server hosted on mongo atlas . It is a M3 replica set and all the testing in done on the replica set only.
My app configuration is as follows.
public #Bean MongoClient mongoClient() {
String userName = env.getProperty("spring.data.mongodb.username");
String password = env.getProperty("spring.data.mongodb.password");
String authDatabase = env.getProperty("spring.data.mongodb.authentication-database");
String uriString = env.getProperty("spring.data.mongodb.uri");
uri = new ConnectionString(uriString);
MongoCredential credential = MongoCredential.createCredential(userName, authDatabase, password.toCharArray());
return MongoClients
.create(MongoClientSettings.builder().credential(credential).applyConnectionString(uri).build());
}
public #Bean MongoTemplate mongoTemplate() {
String database = env.getProperty("spring.data.mongodb.database");
MongoTemplate template = new MongoTemplate(mongoClient(), database);
MappingMongoConverter mongoMapping = (MappingMongoConverter) template.getConverter();
mongoMapping.afterPropertiesSet();
template.setSessionSynchronization(SessionSynchronization.ALWAYS);
return template;
}
public #Bean MongoTransactionManager transactionManager(MongoDatabaseFactory dbFactory) {
return new MongoTransactionManager(dbFactory);
}
Now when we intiating a multi document transaction it is not rolling back .
#Transactional
public void createMultiDocument {
flag =true;
template.save1(doc1);
template.save2(doc2);
if(flag) {
throw new RuntimeException();
}
template.save3(doc3);
}
In the above operation document1 and document2 are saving to the database.
Sorry false alarm.
We are throwing a checked exception which does not ensure a rollback.
I have implemented a springboot application to retrieve files from an FTP server and to download them into my local directory.
Following is the code which I used to do that.
#Configuration
public class FTPConfiguration {
#ServiceActivator(inputChannel = "ftpMGET")
#Bean
public FtpOutboundGateway getFiles() {
FtpOutboundGateway gateway = new FtpOutboundGateway(sf(), "mget", "payload");
gateway.setAutoCreateDirectory(true);
gateway.setLocalDirectory(new File("./downloads/"));
gateway.setFileExistsMode(FileExistsMode.REPLACE_IF_MODIFIED);
gateway.setFilter(new AcceptOnceFileListFilter<>());
gateway.setOutputChannelName("fileResults");
return gateway;
}
#Bean
public MessageChannel fileResults() {
DirectChannel channel = new DirectChannel();
channel.addInterceptor(tap());
return channel;
}
#Bean
public WireTap tap() {
return new WireTap("logging");
}
#ServiceActivator(inputChannel = "logging")
#Bean
public LoggingHandler logger() {
LoggingHandler logger = new LoggingHandler(LoggingHandler.Level.INFO);
logger.setLogExpressionString("'Files:' + payload");
return logger;
}
#Bean
public DefaultFtpSessionFactory sf() {
DefaultFtpSessionFactory sf = new DefaultFtpSessionFactory();
sf.setHost("localhost");
sf.setPort(2121);
sf.setUsername("anonymous");
sf.setPassword("");
return sf;
}
#MessagingGateway(defaultRequestChannel = "ftpMGET", defaultReplyChannel = "fileResults")
public interface GateFile {
List<File> mget(String directory);
}
}
Now I need to upload these files to my MongoDB automatically, when I run this program.
Can anyone please help me or guide me the steps I should follow?
Please, take a look into a MongoDB support in Spring Integration: https://docs.spring.io/spring-integration/docs/current/reference/html/mongodb.html#mongodb-outbound-channel-adapter
You probably need to think how to make some POJO from files of that MGET result and send it to that MongoDB channel adapter for storing as documents in some collection.
For my Springboot application, I have a requirement that MongoDB URI should be specified with "app1.mongodb.uri" in application.properties. Yes we don't want to use "spring.data.mongodb.uri" because I was told that it's misleading (what!?). Does anyone know what is the simplest way to do that ? My application is all running fine, and I'm so reluctant to make any big change because of this "requirement".
Figured it out how to do it. The trick is to override the beam MongoClient and Mongotemplate.
import com.mongodb.MongoClient;
import com.mongodb.MongoClientURI;
#SuppressWarnings("deprecation")
#Configuration
#EnableMongoRepositories
#PropertySource("classpath:application.properties")
public class MongoDBConfig extends AbstractMongoConfiguration {
#Value("${app1.mongodb.db}")
private String database;
#Value("${app1.mongodb.uri}")
private String uri;
#Override
#Bean
public MongoClient mongoClient() {
MongoClientURI mongoURI = new MongoClientURI(uri);
MongoClient client = new MongoClient(mongoURI);
return client;
}
#Override
protected String getDatabaseName() {
return database;
}
#Bean
public MongoTemplate mongoTemplate() throws Exception {
return new MongoTemplate(mongoClient(), getDatabaseName());
}
}
I'm using SpringBoot + MongoDB. I created my object as follows.
I am able to #Autowrite the DocumentStoreConfig object in my Service/Controller and make calls to Mongo.
Sample call:
#Autowired
private DocumentStoreConfig docStoreConfig;
this.docStoreConfig.mongoClient().getDatabase("db_name").getCollection(collection).insertOne(doc);
Problem I see is that each call does a 'new' MongoClient and opens up a new connections.
What is the guidance on setting up a pool.. or reusing the same connection object rather than making the painful cost of opening a brand new connection.
#Configuration
public class DocumentStoreConfig extends AbstractMongoConfiguration {
#Value("${spring.data.mongodb.uri}")
private String connectionString;
#Value("${documentstore.database}")
private String databaseName;
#Override
public String getDatabaseName() {
return this.databaseName;
}
#Override
public MongoClient mongoClient() {
System.out.println("**** \n\n\n NEW MONGO \n\n\n");
return new MongoClient(new MongoClientURI(this.connectionString));
}
public MongoCollection<Document> getFailureCollection() {
return this.mongoClient().getDatabase(this.databaseName).getCollection("failure");
}
}
When Mongodb is down, the spring boot application is down. I wish to handle exception of connectiontimeout and log the error without stopping application.
When finding an item from database is failed because the connection is not possible, the application should do another treatment like calling web service to find data.
Did you have any idea about this ?
Configuration
spring.data.mongodb.uri=mongodb://${MONGODB_DB_HOST}:${MONGODB_DB_PORT}/${MONGODB_DB_DATABASE}?connectTimeoutMS=${mongodb.connection.timeout}
I have used below code to configure mongodb connection in spring boot
you can specify socket timeout and connection timeout according to your need.
#Configuration
public class DatabaseConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(DatabaseConfiguration.class);
#Value("${spring.data.mongodb.uri}")
private String mongoUri;
#Value("${spring.data.mongodb.database}")
private String mongoDbName;
#Value("${spring.data.mongodb.host}")
private String host;
#Value("${spring.data.mongodb.port}")
private int port;
#Value("${spring.data.mongodb.username}")
private String username;
#Value("${spring.data.mongodb.password}")
private String password;
#Bean
public MongoTemplate mongoTemplate() {
LOGGER.debug(" instantiating MongoDbFactory ");
SimpleMongoDbFactory mongoDbFactory = new SimpleMongoDbFactory(mongoClient(), mongoDbName);
MongoTemplate mongoTemplate = new MongoTemplate(mongoDbFactory);
return mongoTemplate;
}
#Bean
public MongoClient mongoClient() {
List<ServerAddress> servers = new ArrayList<ServerAddress>();
servers.add(new ServerAddress(host, port));
MongoClientOptions mongoClientOptions = MongoClientOptions.builder()
.connectionsPerHost(10)
.socketTimeout(2000)
.connectTimeout(2000)
.build();
if (Utils.isNotEmpty(username) && Utils.isNotEmpty(password)) {
List<MongoCredential> creds = new ArrayList<MongoCredential>();
creds.add(MongoCredential.createCredential(username, mongoDbName, password.toCharArray()));
return new MongoClient(servers,creds, mongoClientOptions);
} else
return new MongoClient(servers, mongoClientOptions);
}
#Bean
public MongoClientURI mongoClientURI() {
LOGGER.debug(" creating connection with mongodb with uri [{}] ", mongoUri);
return new MongoClientURI(mongoUri);
}
}
Define below properties in your application.yml file
mongodb specific properties
spring:
data:
mongodb:
database: dbname
host: localhost
port: 27017
username: dbusername
password: dbpassword
You can use the below sample example to configure mongodb timeout. I hope it's easy to convert to bean annonation for springboot. Else you can import the resource bean (#ImportResource)
<beans>
<mongo:mongo host="localhost" port="27017">
<mongo:options connections-per-host="8"
threads-allowed-to-block-for-connection-multiplier="4"
connect-timeout="1000"
max-wait-time="1500}"
auto-connect-retry="true"
socket-keep-alive="true"
socket-timeout="1500"
slave-ok="true"
write-number="1"
write-timeout="0"
write-fsync="true"/>
</mongo:mongo/>
</beans>