get MongoDatabase with spring data mongodb - spring

I am trying to tail the mongo oplog collection indefinitely. The code that I currently use is as below.
MongoClient mongoClient = new MongoClient(<host>,<27017>);
MongoCollection oplogColl =
mongoClient
.getDatabase("local")
.getCollection("oplog.rs");
MongoCursor oplogCursor =
oplogColl
.find(new Document("ts", filter))
.cursorType(CursorType.TailableAwait)
.noCursorTimeout(true)
.sort(new Document("$natural", 1))
.iterator();
I am trying to get the same implementation using spring-data where the mongo uri will be specified in the properties file. Hence I need to access MongoDatabase or MongoClient.
Tried using MongodbFactory class in spring, but it returns a instance of type DB, which is a old mongo implementation to access mongo.
How can I get to use MongoDatabase/MongoCollection/MongoClient using spring data.

MongoClient extends Mongo, so
#Configuration
public class AppConfig {
public #Bean Mongo mongo() throws UnknownHostException {
return new Mongo("localhost");
}
}
If you don't want to code to Mongo classes directly
/*
* Factory bean that creates the com.mongodb.Mongo instance
*/
#Bean
public MongoClientFactoryBean mongo() {
MongoClientFactoryBean mongo = new MongoClientFactoryBean();
mongo.setHost("localhost");
return mongo;
}
Edit:
#Configuration
public class AppConfig {
public #Bean MongoClient mongo() throws UnknownHostException {
return new MongoClient("localhost");
}
}
Usage:
#Autowired
private Mongo mongo;
MongoOperations mongoOps = new MongoTemplate(mongo, "databaseName");
Edit:
#Autowired
private MongoClient mongoClient;
MongoOperations mongoOps = new MongoTemplate(mongoClient, "databaseName");

Related

Spring Boot + Mongo DB + Shiro configuration

I am developing a web-based application in Spring boot and Mongo DB. Now I want to use Apache Shiro for Authentication and Authorisation. Can somebody explain to me the procedure and how to establish a mongo db realm and where to mention the permission-user mapping? Thank You.
Basically you need three component
#Component
public class YourMongoConfiguration {
#Bean(name = "mongoTemplate")
#DependsOn({ "lifecycleBeanPostProcessor" })
public MongoTemplate mongoTemplate() throws Exception {
MongoTemplate mt = new MongoTemplate(YOUR_CONFIGURATIOP_HERE);
return mt;
}
}
Then a MongoRealm
#Component("mongoRealm")
public class MongoRealm extends AuthorizingRealm {
private final MongoTemplate mongoTemplate;
#Autowired
public MongoRealm(MongoTemplate mongoTemplate) {
this.mongoTemplate = mongoTemplate;
HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher();
credentialsMatcher.setHashAlgorithmName(Sha512Hash.ALGORITHM_NAME);
credentialsMatcher.setHashIterations(53);
setCredentialsMatcher(credentialsMatcher);
}
#Override
protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) {
// YOUR IMPLEMENTATION
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token)
throws AuthenticationException {
// YOUR IMPLEMENTATION
}
}
and finally a security manager
#Component("securityManager")
public class SecurityManager extends DefaultWebSecurityManager {
#Autowired
public SecurityManager(Realm mongoRealm, SessionDAO mongoSessionDAO) {
super(mongoRealm);
setRealm(mongoRealm);
SessionManager sessionManager = new SessionManager();
setSessionManager(sessionManager);
sessionManager.setSessionDAO(mongoSessionDAO);
}
}
From now on either Shiro will call your MongoRealm to validate login and permission and you will be able to hadle your collection with classes like
#Service
public class ONE_OF_YOUR_Services {
#Autowired
private MongoTemplate mongoTemplate;
protected List<T> getDocuments(Class<T> clazz, String collection) {
return mongoTemplate.findAll(clazz, collection);
}
}
I hope it helps.
There are a few MongoDB realms up on GitHub. I don't want to link to them as haven't tried them out, but that would be your best place to start.

Spring boot common application properties

Spring boot application properties needs to follow convention from https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html when we use any DB like cassandra/mongo. In case if we want to declare our own properties for DB setup instead of spring-boot convention, what are all the steps we need to do for setting up DB?
You can do this: Spring boot - custom variables in Application.properties
or you can just create your own property in your application.properties file like:
my.property.someDb.hostname=http://wherever.comand then reference to it in your code like:
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
Update 1:
If you want to create the MongoDb with your own properties you have to define the right Java Beans in an #Configuration file. For MongoDB it could look like the following:
#Configuration
public class MyMongoConfig extends AbstractMongoConfiguration{
#Value("${my.property.someDb.hostname}")
private String someDbHostname;
#Value("${my.property.someDb.myOwnPortDefinition}")
private int myOwnPortDefinition;
#Value("${my.property.someDb.myDatabasename}")
private String myDatabasename;
#Override
protected String getDatabaseName() {
return myDatabasename;
}
#Override
#Bean
public Mongo mongo() throws Exception{
return new MongoClient(someDbHostname, myOwnPortDefinition );
}
#Bean
public MongoTemplate mongoTemplate() throws Exception{
return new MongoTemplate(mongo(), getDatabaseName());
}
}
These are the essential steps you need in order to get a data source like Jdbc, mongodb set up in Spring Boot
Need a #Configuration class that has transaction management enabled
on it
Read the environment properties for the datasource i.e. dataSource
url, username, password etc.
Create beans for datasource, session factory, transaction manager
etc.
Once all of the above setup, use this #Configuration in your
consumer to initialize the spring application context
Here are some snippets of wiring mongodb datasource in spring boot
DataSourceConfiguration.java
#Configuration
#EnableTransactionManagement
#ComponentScan(basePackages = {"com.example.xyz"})
public class DatabaseEntityConfiguration {
public static final String DATABASE_ENTITY_DATA_SOURCE = "databaseDataSource";
public static final String DATABASE_HIBERNATE_PROPERTIES = "databaseHibernateProperties";
public static final String DATABASE_ENTITY_SESSION_FACTORY = "databaseSessionFactory";
public static final String DATABASE_ENTITY_TRANSACTION_MANAGER = "databaseTransactionManager";
public static final String DATABASE_ENTITY_DB_CONFIG_DAO = "dmdatabaseDbConfigDao";
public static final String DATABASE_ENTITY_DB_CONFIG_SERVICE = "dmdatabaseDbConfigService";
private static final String ENTITY_PACKAGE = "com.example.xyz.database.entity";
#Autowired
private org.springframework.core.env.Environment environment;
#Bean(name = DATABASE_ENTITY_DATA_SOURCE)
public DataSource databaseEntitydataSource() throws PropertyVetoException {
// mongodb properties
String driverClass = environment.getProperty("databaseEntity.mongodb.driverClassName");
String mongodbUrl = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.url");
String user = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.username");
String password = environment.getProperty("databaseEntity.mongodb.dmdatabaseDataSource.password");
Preconditions.checkArgument(StringUtils.isNotBlank(driverClass), "The property mongodb driverClass must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(mongodbUrl), "The property mongodb mongodbUrl must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(user), "The property mongodb user must not be null or blank");
Preconditions.checkArgument(StringUtils.isNotBlank(password), "The property mongodb password must not be null or blank");
dataSource.setDriverClass(driverClass);
dataSource.setmongodbUrl(mongodbUrl);
dataSource.setUser(user);
dataSource.setPassword(password);
return dataSource;
}
#Bean(name = DATABASE_ENTITY_SESSION_FACTORY)
public AnnotationSessionFactoryBean databaseEntitySessionFactory() throws PropertyVetoException {
AnnotationSessionFactoryBean annotationSessionFactoryBean = new AnnotationSessionFactoryBean();
annotationSessionFactoryBean.setDataSource(databaseEntitydataSource());
annotationSessionFactoryBean.setPackagesToScan(ENTITY_PACKAGE);
annotationSessionFactoryBean.setAnnotatedClasses(DBConfig.class);
annotationSessionFactoryBean.setHibernateProperties(databaseEntityHibernateProperties());
return annotationSessionFactoryBean;
}
#Bean(name = DATABASE_ENTITY_TRANSACTION_MANAGER)
public HibernateTransactionManager databaseEntityTransactionManager() throws PropertyVetoException {
HibernateTransactionManager transactionManager = new HibernateTransactionManager();
transactionManager.setSessionFactory(databaseEntitySessionFactory().getObject());
return transactionManager;
}
}

Spring Boot & Mongo: custom MappingMongoConverter doesn't work during DotReplacement

I need to achive dotReplacementKey
I'm using MongoDB with such config:
#Configuration
public class MongoTemplateConfig {
#Value("${adserver_mongo_connection_string}")
private String databaseConnectionString;
#Bean
public MongoDbFactory mongoDbFactory() throws UnknownHostException {
MongoClientURI uri = new MongoClientURI(databaseConnectionString.trim());
return new SimpleMongoDbFactory(uri);
}
#Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(mongoDbFactory());
}
#Bean
public MappingMongoConverter mongoConverter(MongoDbFactory mongoFactory, MongoMappingContext mongoMappingContext) throws Exception {
DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoFactory);
MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mongoMappingContext);
mongoConverter.setMapKeyDotReplacement(".");
return mongoConverter;
}
}
I'm doing upsert as follows:
mongoTemplate.bulkOps(...).upsert(...)
but during runtime via debug I found out that different MappingMongoConverter is used rather then this that was configured as a #Bean
btw, if #Inject wherever MappingMongoConverter I get proper bean from config with keyDotReplacement="."
but looks like Spring Boot uses another one under the hood
P.S. I have seen this question without correct answer, but tried with mongoConverter.afterPropertiesSet() and obviously it doesn't work as well
It's a bit ridiculous but the point is that we should pass this custom mappingMongoConverter into MongoTemplate initialization :
#Bean
public MongoTemplate mongoTemplate(MappingMongoConverter mappingMongoConverter) throws UnknownHostException {
return new MongoTemplate(mongoDbFactory(), mappingMongoConverter);
}

Spring Boot + Neo4j get GraphDatabaseService bean?

Using the new-style (Spring Data Neo4j 4.1.2.RELEASE) Neo4jConfiguration can I get a reference to the underlying embedded GraphDatabaseService to pass to the web ui?
New style config:
#Configuration
#EnableNeo4jRepositories(basePackages = "fu.bar")
#EnableTransactionManagement
public class Neo4j extends Neo4jConfiguration {
#Bean
#Scope(value = ConfigurableBeanFactory.SCOPE_SINGLETON, proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
#Bean
public org.neo4j.ogm.config.Configuration getConfiguration() {
org.neo4j.ogm.config.Configuration config = new org.neo4j.ogm.config.Configuration();
config.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver")
.setURI("file:///var/tmp/graph.db");
return config;
}
#Bean
public SessionFactory getSessionFactory() {
SessionFactory sessionFactory = new SessionFactory(getConfiguration(), "fu.bar");
return sessionFactory;
}
I'm not seeing anything in the Javadoc that helps but I suspect Boot has an instance someplace.
Thanks.
If you're using the embedded driver, the GraphDatabaseService can be obtained as follows:
EmbeddedDriver embeddedDriver = (EmbeddedDriver) Components.driver();
GraphDatabaseService databaseService = embeddedDriver.getGraphDatabaseService();
With HTTP, direct access to the database can be achieved with:
String uri = Components.driver().getConfiguration().getURI() +
"/db/data/index/node/" + indexName;
HttpPost httpPost = new HttpPost(uri);
These examples come from the section on indexes of the Spring Data Neo4j reference guide.

Spring jdbc configuration

I have been trying to implement a web service using spring. This webservice will provide data access to a mySQL database using JDBC. I am trying to not use any xml configuration files, so I have come across a problem trying to connect to the database.
I am following the tutorial: http://spring.io/guides/tutorials/rest/ but I changed a few things along the way.
Now that I am trying to implement the connection with the database I get an error when trying to execute the tomcat instance, and I guess the problem is within the configurations.
Here follows some of my code:
Datasource configuration:
#Configuration
#Profile("mySQL")
#PropertySource("classpath:/services.properties")
public class MySQLDataSourceConfiguration implements DataSourceConfiguration{
#Inject
private Environment environment;
#Bean
public DataSource dataSource() throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setPassword(environment.getProperty("dataSource.password"));
dataSource.setUrl(environment.getProperty("dataSource.url"));
dataSource.setUsername(environment.getProperty("dataSource.user"));
dataSource.setDriverClassName(environment.getPropertyAsClass("dataSource.driverClass", Driver.class).getName());
return dataSource;
}
}
the file service.properties is where I keep my configurations for the database, so when I desire to change the database I will just have to change 4 fields.
The JDBCConfiguration class for the setup of the JDBCtemplate
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSourceConfiguration.dataSource());
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
}
Then there is the Repository, that recieves the template.
#Transactional
#Repository
#Qualifier("jdbcRepository")
public class JdbcIndividualRepository implements IndividualsRepository{
private static final Logger LOG = LoggerFactory.getLogger(JdbcIndividualRepository.class);
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
public JdbcIndividualRepository(DataSource jdbcDataSource) {
LOG.info("JDBCRepo arg constructor");
this.jdbcTemplate = new JdbcTemplate(jdbcDataSource);
}
#Override
public Individual save(Individual save) {
String sql = "INSERT INTO Individual(idIndividual, Name) VALUES(?,?)";
this.jdbcTemplate.update(sql, save.getId(), save.getName());
return save;
}
#Override
public void delete(String key) {
String sql = "DELETE FROM Individual WHERE idIndividual=?";
jdbcTemplate.update(sql, key);
}
#Override
public Individual findById(String key) {
String sql = "SELECT i.* FROM Individual i WHERE i.idIndividual=?";
return this.jdbcTemplate.queryForObject(sql, new IndividualRowMapper(), key);
}
#Override
public List<Individual> findAll() {
String sql = "SELECT * FROM Individual";
return new LinkedList<Individual>(this.jdbcTemplate.query(sql, new IndividualRowMapper()));
}
}
Then I register the jdbc configuration in the initializer class when creating the root context of the application as follows:
private WebApplicationContext createRootContext(ServletContext servletContext) {
AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
rootContext.register(CoreConfig.class, SecurityConfig.class, JdbcConfiguration.class);
rootContext.refresh();
servletContext.addListener(new ContextLoaderListener(rootContext));
servletContext.setInitParameter("defaultHtmlEscape", "true");
return rootContext;
}
However, the Tomcat server wont run because it can't autowire the class MySQLDataSourceConfiguration.
Anyone knows what the problem might be? I can give more details on the code, but the question is already really large.
Appreciate any kind of help!
Cheers
EDIT
Solved changing the JdbcConfiguration class to:
#Configuration
#EnableTransactionManagement
#PropertySource("classpath:/services.properties")
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Inject
private Environment environment;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
#Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) throws Exception {
return new DataSourceTransactionManager(dataSource);
}
#Bean
public IndividualsRepository createRepo(){
return new JdbcIndividualRepository(dataSource);
}
}
Remove
#Autowired
private DataSourceConfiguration dataSourceConfiguration;
Because that's not how it's supposed to be used. Instead add to the same class the following:
#Autowired DataSource dataSource;
and use it like this: new JdbcTemplate(dataSource);
Also, try adding #ComponentScan to JdbcConfiguration class. From what I see in your code the class JdbcIndividualRepository is not picked up by anything.
In your class JdbcConfiguration, you are trying to autowire DataSourceConfiguration. I'm not really sure if that's possible - typically you should try to autwire the DataSource, not the DataSourceConfiguration.
#Import( { MySQLDataSourceConfiguration.class })
public class JdbcConfiguration {
#Autowired
private DataSource dataSource;
#Bean
public JdbcTemplate setupJdbcTemplate() throws Exception {
return new JdbcTemplate(dataSource);
}
Also if you have several DataSources and you're using Spring profiles to separate them, it's easier to provide all the DataSource beans in one file and annotate each bean with a different profile:
#Configuration
public class DataSourceConfig {
#Bean
#Profile("Test")
public DataSource devDataSource() {
.... configure data source
}
#Bean
#Profile("Prod")
public DataSource prodDataSource() {
... configure data source
}

Resources