Antwan
Antwan

Reputation: 13

how to dynamically set the hostname with MongoRepository

I am using Spring-Boot project and MongoRepository instead of MongoTemplate.

When using MongoTemplate, one can dynamically set the hostname by using MongoConnectionPool like so:

@Autowired
MongoConnectionPool mongoConn
....
mongoConn.setHostname("127.23.45.89");
mongoConn.setPort(27017);

How do I achieve the same effect using MongoRepository?

I know that I can specified the hostname and port by specifying the

spring.data.mongodb.host=hostname1
spring.data.mongodb.port=27017

in application.properties file.

However I am using GenericContainer to spin up a mongo instance through Docker container to run my unit test. The container dynamically assigned IP Address and port to the mongo instance and thus I need to be able to dynamically set the hostname and port for the MongoRepository at run time.

This is how I am setting up my unit test.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MongoConfiguration.class)
public class HierarchiesServiceImplTests {
private static final Logger log = LoggerFactory.getLogger(HierarchiesServiceImplTests.class);

@Autowired
private HierarchiesService hierarchiesService;

@Autowired
private HierarchyRepository hierarchyRepo;

@BeforeClass
public void setUp() {
    MockitoAnnotations.initMocks(this);

    startMongo();
}

/**
 * Starts a Mongo docker container, and configures the repository factory to use this instance.
 */
private void startMongo() {
    GenericContainer mongo = new GenericContainer("mongo:3")
            .withExposedPorts(27017);
    mongo.start();

    String containerIpAddress = mongo.getContainerIpAddress();
    int mappedPort = mongo.getMappedPort(27017);

    //TODO: set the hostname and port here so that the MongoRepository use this mongo instance instead of the default localhost.

    log.info("Container mongo:3 listening on {}:{}", containerIpAddress, mappedPort);
}

This is what I have in my MongoConfiguration.class

@Configuration
@EnableMongoRepositories
@ComponentScan({"com.is.hierarchies.service"})
public class MongoConfiguration {

}

Upvotes: 1

Views: 3369

Answers (3)

alexbt
alexbt

Reputation: 17085

The container dynamically assigned IP Address and port to the mongo instance and thus I need to be able to dynamically set the hostname and port for the MongoRepository at run time.

You could also override the properties at launch time:

$ java -jar YourApp.jar --spring.data.mongodb.host=hostnamexyz --spring.data.mongodb.port=123

or

$ java -Dspring.data.mongodb.host=hostnamexyz -Dspring.data.mongodb.port=123 -jar YourApp.jar

Or through Docker:

$ docker run -e spring.data.mongodb.host=hostnamexyz -e spring.data.mongodb.port=123 -p 8080:8080 -i -t yourdocker:latest

I only tested the docker command with one -e param, but I don't see why we couldn't provide more than one!

Upvotes: 2

Antwan
Antwan

Reputation: 13

@Alex gave me an idea to make a cleaner solution. Instead of modifying the MongoTemplate in my test suite, I move that code to the MongoConfiguration.java.

Here is how I ended up resolving the problem.

in my MongoConfiguration.class I made the following changes:

@Configuration
@EnableMongoRepositories
@ComponentScan({"com.is.hierarchies.service"})
public class MongoConfiguration extends AbstractMongoConfiguration {

String containerIpAddress;
Integer mappedPort;

@Override
public Mongo mongo() throws Exception {
    startMongo();
    return new Mongo(containerIpAddress,mappedPort);
}

@Override
public String getDatabaseName() {
    return "hierarchies_db";
}

/**
 * Starts a Mongo docker container, and configures the repository factory to use this instance.
 */
private void startMongo() {
    GenericContainer mongo = new GenericContainer("mongo:3")
            .withExposedPorts(27017);
    mongo.start();

    containerIpAddress = mongo.getContainerIpAddress();
    mappedPort = mongo.getMappedPort(27017);

}
}

Upvotes: 0

alexbt
alexbt

Reputation: 17085

Override MongoTemplate Bean with yours

You may define an additional @Configuration class within this test, just to override MongoTemplate.

MongoRepository uses a MongoTemplate to performs its operations, so by defining a @Bean MongoTemplate, we are able to set the connection details of our choice. And this is then what the Repository will be using.

The complete code:

RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = MongoConfiguration.class)
public class HierarchiesServiceImplTests {
private static final Logger log = LoggerFactory.getLogger(HierarchiesServiceImplTests.class);

private static String containerIpAddress;
private static int mappedPort;

@Autowired
private HierarchiesService hierarchiesService;

@Autowired
private HierarchyRepository hierarchyRepo;

/**
 * Starts a Mongo docker container, and configures the repository factory to use this instance.
 */
private void startMongo() {
    GenericContainer mongo = new GenericContainer("mongo:3").withExposedPorts(27017);
    mongo.start();

    containerIpAddress = mongo.getContainerIpAddress();
    mappedPort = mongo.getMappedPort(27017);

    log.info("Container mongo:3 listening on {}:{}", containerIpAddress, mappedPort);
}

@Configuration
public static class MongoTestConfiguration {    
    @Bean
    @Primary
    public MongoTemplate  mongoTemplate() throws DataAccessException, Exception{
        return createMongoOperations(containerIpAddress, mappedPort, "mydb", "user", "pwd");
    }

    private MongoTemplate createMongoOperations(String hostname, int port, String dbName, String user, String pwd) throws DataAccessException, Exception {
        MongoCredential mongoCredentials = MongoCredential.createScramSha1Credential(user, dbName, pwd.toCharArray());
        MongoClient mongoClient = new MongoClient(new ServerAddress(hostname, port), Arrays.asList(mongoCredentials));
        Mongo mongo = new SimpleMongoDbFactory(mongoClient, dbName).getDb().getMongo();
        return new MongoTemplate(mongo, dbName);
    }
}

@BeforeClass
public static void setUp() {
    MockitoAnnotations.initMocks(this);

    startMongo();
}

What I did:

  • add static to your setUp method
  • convert the ip to a static field: containerIpAddress = mongo.getContainerIpAddress();
  • add @Configuration MongoTestConfiguration to redefine MongoTemplate

Upvotes: 0

Related Questions