Kundera's Client Extension Framework

Devender Yadav edited this page Aug 1, 2017 · 22 revisions
Clone this wiki locally

Introduction

NoSQL databases have gone a long way since their inception. There are plenty of them out there and more are still evolving or getting launched. Many of them don't have high level clients and they come with low level driver APIs that are difficult to use and painful to learn and practice.

Kundera is designed and built keeping in mind requirement for scaling it horizontally, i.e. adding support for other NoSQL data-stores with ease. At the heart of its architecture is Kundera-core that acts as a bridge between JPA API and data-store specific modules that use low level driver interfaces available with NoSQL databases. All of datastore specific modules are built on top of kundera-core in order to provide support for their intended data-store.

This document describes kundera-core's Client Extension Framework that enables developers to implement interfaces within it, and write implementation in order to build datastore-specific modules, say kundera-xxxx.

So if you are a NoSQL data-store developer interested in writing high level java client, or just curious about how it works, you're at the right place. Keep reading!

Pre-requisite

If you are a developer interested in writing a high level java client for your data-store of your choice:

  1. You should be familiar with how data is stored in underlying database and other features like indexing etc.
  2. You should be familiar with java API/ driver provided with that data-store.

Using Client Extension Framework

Write your Client

In Kundera terminology, Client interface is your gateway to CRUD operations on database (except queries). You need to write your client class that should extend com.impetus.kundera.client.ClientBase and implement com.impetus.kundera.client.Client interface like this:

public class MyDatabaseClient extends ClientBase implements Client {
   /**
    * A Node object would be available in this method that holds data variable referring to your entity 
    *  object. This method is responsible for writing node data to underlying database.
   */
   public void persist(com.impetus.kundera.graph.Node node) {}
   
   /**
    * This is called by Kundera when find method is invoked on Entity Manager. This method is responsible 
    * for fetching data from underlying database for given entity class and primary key.
   */
   public Object find(Class entityClass, Object key) {}
   
   /**
    * This is called by Kundera when a remove method is invoked on entity manager. 
    * This is responsible for removing entity object from underlying database.
   */
   public void delete(Object entity, Object pKey) {}
}

Write your Client Factory

Kundera uses Client Factories to build and initialize clients. These factories are specified by user under "kundera.client.lookup.class" property in persistence.xml. You need to extend com.impetus.kundera.loader.GenericClientFactory like this:

public class MyDatabaseClientFactory extends GenericClientFactory {
    /** Entity reader instance */
    private EntityReader reader;

    /** Schema manager instance */
    private SchemaManager schemaManager;
    
    /**
     * This is called by Kundera when you create entity manager factory. 
     * Your responsibility is to initialize entity reader, schema manager and any other instance variable
     * this class might hold. 
    */
    public void initialize() {}
    
    /**
     * This method is called after initialize by Kundera and your responsibility would be to create 
     * a pool (or connection) provided by your Java driver and return it.
    */
    protected Object createPoolOrConnection() {}

    /**
    * Kundera calls this method to get instance of client. You should initialize client and return it here.
    */
    protected Client instantiateClient(String persistenceUnit)

    /**
     * It would return true or false depending upon how you want your client to be.
    */
    public boolean isThreadSafe() {}

    /**
     * Finally, when entity manager factory is closed, Kundera calls this method to free up resources
     * (e.g. closing connection pool)
    */
    public void destroy()
}

Write your Query implementor

You need to write your query implementation class if you want to (and it is advised that you do) support JPA queries in your implementation. Query implemntors are used by Kundera to run JPA queries by invoking appropriate methods in Entity Readers (described in next section). Your query implentory class needs to extend com.impetus.kundera.query.QueryImpl and implement javax.persistence.Query like this:

public class MyDatabaseQuery extends QueryImpl implements Query {

    /** Instance of Entity Reader */
    private EntityReader reader;
    
    /**
    * This method would be called by Kundera to populate entities while it doesn't hold any relationships.
    */
    protected List<Object> populateEntities(EntityMetadata m, Client client) {}

    /**
    * This method would be called by Kundera to populate entities while it holds relationships.
    */
    protected List<Object> recursivelyPopulateEntities(EntityMetadata m, Client client) {}

    /**
    * Initialize and return your entity reader here.
    */
    protected EntityReader getReader() {}

    /**
    * This method is called by Kundera when executeUpdate method is invoked on query instance that
    * represents update/ delete query. Your responsibility would be to call appropriate methods of client.
    */
    protected int onExecuteUpdate() {}
}

Write your Entity Reader

Apart from writing Query Implementor, you are also required to write entity reader. Entity Readers are used by Kundera to translate your queries into correct client method calls. If you need to support JPA queries in your implementation, you need to write an entity reader class that extends com.impetus.kundera.persistence.AbstractEntityReader and implements com.impetus.kundera.persistence.EntityReader like this:

public class MyDatabaseEntityReader extends AbstractEntityReader implements EntityReader {
    
    /**
    * This is used by Query implementor to find entities by their ID.
    */
    public EnhanceEntity findById(Object primaryKey, EntityMetadata m, List<String> relationNames, Client client) {}

     /**
    * This is used by Query implementor to populate relationship entities into their parent entity.
    */
    public List<EnhanceEntity> populateRelation(EntityMetadata m, List<String> relationNames, boolean isParent, Client client) {}

}

Write your Schema Manager

(Optional)

If you want to support automatic schema generation in your implementation through "kundera_ddl_auto_prepare" property in persistence.xml, you need to write Schema Manager class that extends com.impetus.kundera.configure.schema.api.AbstractSchemaManager and implements com.impetus.kundera.configure.schema.api.SchemaManager like this:

public class CassandraSchemaManager extends AbstractSchemaManager implements SchemaManager {
   
    /**
     * Exports schema according to configured schema operation e.g.
     * {create,create-drop,update,validate}
     */
    public void exportSchema() {}

    /**
     * Method required to drop auto create schema,in case of schema operation as
     * {create-drop},
     */
    public void dropSchema() {}
}

Conclusion

A high level client API for databases make working with them easier and faster. Without Kundera, Writing your own high level client would have been like re-inventing the wheel, since you would have to take care of entity life cycle management, caching, class loading, query parsing, indexing, transactions to name a few among many other considerations. Good news is that these difficult(and time-consuming) services have already been written in Kundera Kernel (or rather Kundera-core) and would be available to modules built on top of it.

Kundera Kernel is designed in such a way that it promotes writing high level client implementations and plugging them into it. (In fact that's how kundera-hbase and kundera-mongo modules were built).

Lastly, if this idea looks catchy and exciting and you want to write your own high level client plug-in, start right away, and do let us know at kundera@impetus.co.in or chat with us at gitter. There are plenty of developers here to help you out.

All the best!

Home