How to use low-level driver APIs with Spring Data MongoDB - spring

I am using Spring Data MongoDB. But I don't want to map my result to a domain class. Also, I want to access low level MongoAB APIs in few cases. But I want spring to manage the connections pooling etc.
How can i get an instance of com.mongodb.MongoClient to perform low level operations.
Here is what I am trying to do :
MongoClient mongoClient = new MongoClient();
DB local = mongoClient.getDB("local");
DBCollection oplog = local.getCollection("oplog.$main");
DBCursor lastCursor = oplog.find().sort(new BasicDBObject("$natural", -1)).limit(1);
Or I simply want a JSON object / DBCursor / DBObject.

you can do it this way
#Autowired MongoDbFactory factory;
DB local = factory.getDB("local");
DBCollection oplog = local.getCollection("oplog.$main");
DBCursor lastCursor = oplog.find().sort(new BasicDBObject("$natural", -1)).limit(1);
Where
MongoDbFactory is an interface provifed by spring-data-mongo that can obtain a
com.mongodb.DB object and access allthe functionality of a specific MongoDB database
instance
your configuration file should contain these informations :
<bean id="mongoFactoryBean"
class="org.springframework.data.mongodb.core.MongoFactoryBean">
<property name="host" value="127.0.0.1"/>
<property name="port" value="27017"/>
</bean>
<bean id="mongoDbFactory"
class="org.springframework.data.mongodb.core.SimpleMongoDbFactory">
<constructor-arg name="mongo" ref="mongoFactoryBean"/>
<constructor-arg name="databaseName" value="local"/>
</bean>
doing it like that, spring should stay managing your connection pool.

You usually perform low level access through MongoTemplate's execute(…) methods that take callbacks giving you access to the native Mongo driver API.
class MyClient {
private final MongoOperations operations;
#Autowired
public MyClient(MongoOperations mongoOperations) {
this.operations = operations;
}
void yourMethod() {
operations.execute(new CollectionCallback<YourDomainClass>() {
YourDomainClass doInCollection(DBCollection collection) {
// here goes your low-level code
}
});
}
The advantage of this template approach is that the MongoTemplate instance backing the MongoOperations interface will still take care of all resource management and exception translation (converting all Mongo specific exceptions into Spring's DataAccessException hierarchy).
However, for your concrete example you could just go ahead and do the following directly:
Query query = new Query().with(new Sort(DESC, "$natural")).limit(1);
DBObject result = operations.find(query, DBObject.class, "oplog.$main");
Here you can mix and match the type you pass into the find(…) method to let the template convert the result into a Map or a domain object if needed. As indicated above you also get resource management and exception translation which your sample code above is missing.

Related

Spring Batch CompositeItemProcessor get value from other delegates

I have a compositeItemProcessor as below
<bean id="compositeItemProcessor" class="org.springframework.batch.item.support.CompositeItemProcessor">
<property name="delegates">
<list>
<bean class="com.example.itemProcessor1"/>
<bean class="com.example.itemProcessor2"/>
<bean class="com.example.itemProcessor3"/>
<bean class="com.example.itemProcessor4"/>
</list>
</property>
</bean>
The issue i have is that within itemProcessor4 i require values from both itemProcessor1 and itemProcessor3.
I have looked at using the Step Execution Context but this does not work as this is within one step. I have also looked at using #AfterProcess within ItemProcessor1 but this does not work as it isn't called until after ItemProcessor4.
What is the correct way to share data between delegates in a compositeItemProcessor?
Is a solution of using util:map that is updated in itemProcessor1 and read in itemProcessor4 under the circumstances that the commit-interval is set to 1?
Using the step execution context won't work as it is persisted at chunk boundary, so it can't be shared between processors within the same chunk.
AfterProcess is called after the registered item processor, which is the composite processor in your case (so after ItemProcessor4). This won't work neither.
The only option left is to use some data holder object that you share between item processors.
Hope this helps.
This page seems to state that there are two types of ExecutionContexts, one at step-level, one at job-level.
https://docs.spring.io/spring-batch/trunk/reference/html/patterns.html#passingDataToFutureSteps
You should be able to get the job context and set keys on that, from the step context
I had a similar requirement in my application too. I went with creating a data transfer object ItemProcessorDto which will be shared by all the ItemProcessors. You can store data in this DTO object in first processor and all the remaining processors will get the information out of this DTO object. In addition to that any ItemProcessor could update or retrieve the data out of the DTO.
Below is a code snippet:
#Bean
public ItemProcessor1<ItemProcessorDto> itemProcessor1() {
log.info("Generating ItemProcessor1");
return new ItemProcessor1();
}
#Bean
public ItemProcessor2<ItemProcessorDto> itemProcessor2() {
log.info("Generating ItemProcessor2");
return new ItemProcessor2();
}
#Bean
public ItemProcessor3<ItemProcessorDto> itemProcessor3() {
log.info("Generating ItemProcessor3");
return new ItemProcessor3();
}
#Bean
public ItemProcessor4<ItemProcessorDto> itemProcessor4() {
log.info("Generating ItemProcessor4");
return new ItemProcessor4();
}
#Bean
#StepScope
public CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor() {
log.info("Generating CompositeItemProcessor");
CompositeItemProcessor<ItemProcessorDto> compositeItemProcessor = new CompositeItemProcessor<>();
compositeItemProcessor.setDelegates(Arrays.asList(itemProcessor1(), itemProcessor2(), itemProcessor3), itemProcessor4()));
return compositeItemProcessor;
}
#Data
public class ItemProcessorDto {
private List<String> sharedData_1;
private Map<String, String> sharedData_2;
}

Spring bean how to access server resource only once?

I have a Spring bean which refers connection factory to access J2C resource. This code is deployed in webSphere server in 2 jvms load balanced. I had the readAuth method as init-method in bean xml but it was called multiple times during a load test. Since Spring singleton is per bean per container, I assumed there are multiple containers causing it to load multiple times. So I removed the init-method and changed the username and password to static and added null check in get methods. But now also, readAuth method is being called multiple times. I want to ensure this method is called only once per jvm since this method access server resource and connection is timing out during load test.
Please suggest a best approach on how to write this class. Thanks in advance.
<bean id="J2CUtils"
class="test.J2CUtils">
<property name="connectionFactory" ref="connectionFactory" />
</bean>
<jee:jndi-lookup id="connectionFactory" jndi-name="eis/J2CAuth" />
public class J2Ctils {
private ConnectionFactory connectionFactory;
private static String userName;
private static String password;
private void readAuth() throws ResourceException {
System.out.println("Auth loaded");
Connection conn = connectionFactory.getConnection();
Interaction interaction = (Interaction) conn.createInteraction();
Config config = interaction.getConfig();
userName = config.getUserName();
password = config.getPassword();
}
public String getUserName() {
if(null == userName) {
readAuth();
}
return userName;
}
public String getPassword() {
if(null == password) {
readAuth();
}
return password;
}
public void setConnectionFactory(ConnectionFactory connectionFactory) {
this.connectionFactory = connectionFactory;
}
}
Singletons or static methods don't help if you want to access a resource exactly once because every member of the cluster has it's own instance of the class.
Take in mind that neither the Java EE specification the Spring specification do define any kind of cluster behavior. You need to search for specific vendor solution to achieve this kind of requirement. Just as an example see have a look at #ApplicationScped in a cluster.
Other approaches to archive the expected behaviour are using a distributed lock like Zookeeper or maybe even a Queue with duplicate message filter.

Configurable number of Spring DAO objects

I have an app that access a configurable list of databases. I do not want to hardcode the corresponding DAO objects in the class. Is there a way to do that in Spring?
The hardcoded way would be:
databases.properties:
db1.url = jdbc://db1.acess.com:3306
...
db2.url = jdbc://db2.access.com:3306
...
db3.url = ldap://ldapdb.access.com:3306
applicationContext.xml:
<bean id="MyServicebean" class="com.MyServiceImpl">
<property name="DBDao1" ref="DBDao1"/>
<property name="DBDao2" ref="DBDao2"/>
<property name="LdapDao" ref="LdapDao"/>
</bean>
public class MyServiceImpl {
#Autowired
private DBDao DBDao1;
#Autowired
private DBDao DBDao2;
#Autowired
private LdapDao LdapDao;
...
}
Is there a more flexible way of configuring and autowiring DAO objects based on how many DBs we specify in databases.properties?
Looking at your database.properties, it seems not all DAOs are connecting to Databases. Some are connecting to LDAP server. So obviously implementation of DAOs will be different and can't generalize/abstract it.
If all DAOs that connect to databases perform the same set of operations and connects to different databases based on some criteria (ex: clientId) then take a look at Spring's AbstractRoutingDataSource. http://spring.io/blog/2007/01/23/dynamic-datasource-routing/

Using one DataSource object for multiple DB connections - using only one connection at a time

Can we define only one DataSource object and wire it dynamically at runtime connecting to different databases ? I need to connect to only one database at a time.
I will be passing the name of the Database as argument. I will lookup the DB URL and other details from a property file and then I need to connect to the DB using the DB URL.
In Short - I do not know the number of databases I need to connect to. I will have all possible database connection details configured in the database.properties file following a certain syntax (like prefixed with DB01 etc.). The name of the DB will be passed as argument and I need to execute the query against that database.
database.properties file
DB01.driver=com.ibm.db2.jcc.DB2Driver
DB01.url=jdbc:db2://localhost:50000/SAMPLE
DB01.username=db2admin
DB01.password=db2admin
DAO class
#Autowired
#Qualifier("DB01") // how do I make this dynamic ?
private DataSource datasource;
private JdbcTemplate jdbcTemplate;
// some more code
public SqlRowSet executeQuery(String sqlQuery)
{
// can I pass the DB name here (the database.properties file will have the DB details
// with this name as given above) and set the DataSource Object accordingly ?
// so that the query will be executed against that DB ?
setJdbcTemplate(new JdbcTemplate(this.datasource));
return getJdbcTemplate().queryForRowSet(sqlQuery);
}
Using Spring v4.1.4 RELEASE. Thanks !!
You can define a Routing DataSource that redirects the getConnection method to one datasource or another based on a key, in your case, it seems to be the database name.
For instance, the spring xml:
....
<bean id="DB01DataSource" parent="parentDatasource" p:url="${DB01.url}" ... />
<bean id="DB02DataSource" parent="parentDatasource" p:url="${DB02.url}" .../>
<bean id="dataSource" class="DBRoutingDataSource">
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="DB01" value-ref="DB01DataSource"/>
<entry key="DB02" value-ref="DB02DataSource"/>
</map>
</property>
<property name="defaultTargetDataSource" ref="DB01DataSource"/>
</bean>
....
The DBRoutingDataSource class:
public class DBRoutingDataSource extends AbstractRoutingDataSource {
#Override
protected Object determineCurrentLookupKey() {
return DBContextHolder.getDB();
}
}
And the DBContextHolder class:
public final class DBContextHolder {
private static final ThreadLocal<String> CONTEXT = new ThreadLocal<String>();
private DBContextHolder() {
// empty
}
public static void setDB(final String db) {
CONTEXT.set(db);
}
public static String getDB() {
return CONTEXT.get();
}
public static void clearDB() {
CONTEXT.remove();
}
}
In your service class before calling your DAO you set the key that will enable the Routing DataSource to get the right connection:
DBContextHolder.setDB("DB01");
try{
dao.executeQuery(sqlSentence);
}finally{
DBContextHolder.clearDB();
}

Spring: Inject bean depended on context (session/web or local thread/background process)

Is it possible to create a factory or proxy that can decide if thread is running in (Web)Request or background-process (ie. scheduler) and then depending on that information, it creates a session bean or a prototype bean?
Example (pseudo Spring config :)
<bean id="userInfoSession" scope="session" />
<bean id="userInfoStatic" scope="prototype" />
<bean id="currentUserInfoFactory" />
<bean id="someService" class="...">
<property name="userInfo" ref="currentUserInfoFactory.getCurrentUserInfo()" />
</bean>
I hope this makes my question easier to understand...
My Solution
It's never to late to update own questions ;). I solved it with two different instances of client session, one SessionScoped client session and one SingletonScoped session. Both are normal beans.
<bean id="sessionScopedClientSession" class="com.company.product.session.SessionScopedClientSession" scope="session">
<aop:scoped-proxy />
</bean>
<bean id="singletonScopedClientSession" class="com.company.product.session.SingletonScopedClientSession" />
<bean id="clientSession" class="com.company.product.session.ClientSession">
<property name="sessionScopedClientSessionBeanName" value="sessionScopedClientSession" />
<property name="singletonScopedClientSessionBeanName" value="singletonScopedClientSession" />
</bean>
The ClientSession will then decide if singleton or session scope:
private IClientSession getSessionAwareClientData() {
String beanName = (isInSessionContext() ? sessionScopedClientSessionBeanName : singletonScopedClientSessionBeanName);
return (IClientSession) ApplicationContextProvider.getApplicationContext().getBean(beanName);
}
Where session type could be gathered through this:
private boolean isInSessionContext() {
return RequestContextHolder.getRequestAttributes() != null;
}
All the classes implement a interface called IClientSession. Both singletonScoped and sessionScoped beans extends from a BaseClientSession where the implementation is found.
Every service then can use the client session ie:
#Resource
private ClientSession clientSession;
...
public void doSomething() {
Long orgId = clientSession.getSomethingFromSession();
}
Now if we go one step further we can write something like a Emulator for the session. This could be done by initializing the clientSession (which is in no context of a request) the singleton session. Now all services can use the same clientSession and we still can "emulate" a user ie:
clientSessionEmulator.startEmulateUser( testUser );
try {
service.doSomething();
} finally {
clientSessionEmulator.stopEmulation();
}
One more advice: take care about threading in SingletonScoped clientSession instance! Wouw, I thought I could do it with less lines ;) If you like to know more about this approach feel free to contact me.
I created small universal workaround to inject beans depends on context.
Guess we have two beans:
<bean class="xyz.UserInfo" id="userInfo" scope="session" />
<bean class="xyz.UserInfo" id="userInfoSessionLess" />
We want to use "userInfo" bean for web user actions and "userInfoSessionLess" bean for background services for example.
Wa also want to write code and don't want to think about context, for example:
#Autowired
//You will get "java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request?" for session less services.
//We can fix it and autowire "userInfo" or "userInfoSessionLess" depends on context...
private UserInfo userInfo;
public save(Document superSecureDocument) {
...
superSecureDocument.lastModifier = userInfo.getUser();
...
}
Now we need create custom session scope to make it worked:
public class MYSessionScope extends SessionScope implements ApplicationContextAware {
private static final String SESSION_LESS_POSTFIX = "SessionLess";
private ApplicationContext applicationContext;
public Object get(String name, ObjectFactory objectFactory) {
if (isInSessionContext()) {
log.debug("Return session Bean... name = " + name);
return super.get(name, objectFactory);
} else {
log.debug("Trying to access session Bean outside of Request Context... name = " + name + " return bean with name = " + name + SESSION_LESS_POSTFIX);
return applicationContext.getBean(name.replace("scopedTarget.", "") + SESSION_LESS_POSTFIX);
}
}
private boolean isInSessionContext() {
return RequestContextHolder.getRequestAttributes() != null;
}
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
Register new scope:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="mySession">
<bean class="com.galantis.gbf.web.MYSessionScope" />
</entry>
</map>
</property>
</bean>
Now we need modify beans definions like this:
<bean class="xyz.UserInfo" id="userInfo" scope="mySession" autowire-candidate="true"/>
<bean class="xyz.UserInfo" id="userInfoSessionLess" autowire-candidate="false"/>
That's all. Bean with name "SessionLess" will be used for all "mySession" scoped beans if we use bean outside of actual web request thread.
Your rephrase is indeed considerably simpler :)
Your currentUserInfoFactory could make use of RequestContextHolder.getRequestAttributes(). If a session is present and associated with the calling thread, then this will return a non-null object, and you can then safely retrieve the session-scoped bean from the context. If it returns a null, then you should fetch the prototype-scoped bean instead.
It's not very neat, but it's simple, and should work.
Create two custom context loaders that bind the same scope defintion to different implementations:
public final class SessionScopeContextLoader extends GenericXmlContextLoader {
protected void customizeContext(final GenericApplicationContext context) {
final SessionScope testSessionScope = new SessionScope();
context.getBeanFactory().registerScope("superscope", testSessionScope);
}
...
}
Then you make a corresponding one for singleton (make your own scope with just statics)
Then you just specify the appropriate context loader in the xml startup for each of the two contexts.

Resources