Implementing Oracle Coherence Cache in WebApplications

Hi All, in past i have written one article regarding oracle coherence cache and its simple implementation. from past couple of months we are implementing coherence in our project and i thought to put its implementation here, so lets start with the topic. lets create a database table as below

create table product(product_id varchar2(5),product_name varchar2(50),unit_price varchar2(5),quantity varchar2(5));

prior to creating this example you need to have the coherence and weblogic installed here(code is tested with coherence 3.7 and weblogic 12c)

create a dynamic web project in eclipse and put coherence.jar as dependent lib, which acts as coherence server inside the weblogic application server.
proj_struct

Now lets design the form/page to insert the details
Note: here i am using the servlet 3.0 api and to get the database connection i used the @Resource injection(JSR specification)

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script scr="js/jquery.js"></script>
<script>
	$(document).ready(function() {
		if (!("autofocus" in document.createElement("input"))) {
			$("#q").focus();
		}
	});
</script>
</head>
<body>
	<form action="writetoCache" method="post">
	<table>
		<tr>
			<td>Product ID:</td>
			<td><input name="prod_id" id="prodid" autofocus required></td>
		</tr>
		<tr>
			<td>Product Name:</td>
			<td><input name="productName" id="name" required></td>
		</tr>
		<tr>
			<td>Unit Price:</td>
			<td><input name="unitPrice" id="unit_price"></td>
		</tr>
		<tr>
			<td>Total Quantity:</td>
			<td><input name="totQty" id="totQty"></td>
		</tr>
		<tr>
			<td>
				<input type="submit" value="SaveDetails">
			</td>
		</tr>
	</table>
		 
	</form>
</body>
</html>

Now lets write the controller for this above JSP, the aim of this controller is to write the data to database and cache server as well

package com.spark.coherence.cache.controller;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;

/**
 * Servlet implementation class WriteToCacheController
 */
@WebServlet("/writetoCache")
public class WriteToCacheController extends HttpServlet {
	private static final long serialVersionUID = 1L;

	@Resource(name = "oracleDS")
	DataSource dataSource;

	Connection connection;
	PreparedStatement preparedStatement;

	/**
	 * @see HttpServlet#HttpServlet()
	 */
	public WriteToCacheController() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doGet(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
	 *      response)
	 */
	protected void doPost(HttpServletRequest request,
			HttpServletResponse response) throws ServletException, IOException {
		// TODO Auto-generated method stub

		String productId = request.getParameter("prod_id");
		String productName = request.getParameter("productName");
		String unitPrice = request.getParameter("unitPrice");
		String Qunatity = request.getParameter("totQty");
		String sql = "insert into product values(?,?,?,?)";
		try {
			connection = dataSource.getConnection();
			System.out.println("-------------- Connection Closed Status: "
					+ connection.isClosed() + " ------------------");
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setString(1, productId);
			preparedStatement.setString(2, productName);
			preparedStatement.setString(3, unitPrice);
			preparedStatement.setString(4, Qunatity);

			preparedStatement.execute();
			writeToCache(productId, productName, unitPrice, Qunatity);
			
			RequestDispatcher requestDispatcher = request.getRequestDispatcher("getRecord.jsp");
			requestDispatcher.forward(request, response);

		} catch (SQLException e) {
			System.out
					.println("--------------------------Exception no record insert into Database !---------------------");
			e.printStackTrace();
		}
		
		
	}

	/**
	 * @param productId
	 * @param productName
	 * @param unitPrice
	 * @param Qunatity
	 */
	public void writeToCache(String productId, String productName,
			String unitPrice, String Qunatity) {

		CacheFactory.ensureCluster();
		NamedCache namedCache = CacheFactory.getCache("product");
		System.out.println("connected to cluster: "
				+ CacheFactory.getCluster().getClusterName()
				+ " With cache name " + namedCache.getCacheName());

		Map<String, String> dbObject = new HashMap<String, String>();
		dbObject.put("productId", productId);
		dbObject.put("productName", productName);
		dbObject.put("unitPrice", unitPrice);
		dbObject.put("quantity", Qunatity);

		if (namedCache.get(productId) == null) {
			namedCache.put(productId, dbObject);
		}

		System.out
				.println("------------- Object got cached ! -----------------");
		
	}
	
}

Once we are done with the insertion of data to database and cache, now its time to fetch the data either from database or cache. here the logic is if data is available in cache the controller pull data from cache if not it will pull from database and also update cache with the latest data so that next time it will from cache but not from database.

<%@page import="java.util.Map"%>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
	pageEncoding="ISO-8859-1"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
	<form action="readCache" method="post">
		<table>
			<tr>
				<td>Product ID:</td>
				<td><input name="prod_id" id="prodid" autofocus required></td>
			</tr>
			<tr>
				<td><input type="submit" value="getDetails"></td>
			</tr>
		</table>
	</form>

	<%
		Map cacheObject = (Map) session.getAttribute("cacheObj");
		if (cacheObject != null) {
	%>
	<table>
		<thead>
			<tr>
				<td>Product ID</td>
				<td>Product Name</td>
				<td>Unit Price</td>
				<td>Quantity Ordered</td>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td><%=cacheObject.get("productId")%></td>
				<td><%=cacheObject.get("productName")%></td>
				<td><%=cacheObject.get("unitPrice")%></td>
				<td><%=cacheObject.get("quantity")%></td>
			</tr>
		</tbody>
	</table>

	<%
		}
	%>
</body>
</html>

ReadFromCacheController.java:

/**
 * 
 */
package com.spark.coherence.cache.controller;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.Resource;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.DataSource;

import com.tangosol.net.CacheFactory;
import com.tangosol.net.NamedCache;

/**
 * @author Sony
 * 
 */
@WebServlet("/readCache")
public class ReadFromCacheController extends HttpServlet {

	@Resource(name = "oracleDS")
	DataSource dataSource;

	Connection connection;
	PreparedStatement preparedStatement;
	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		super.doGet(req, resp);
	}

	/* (non-Javadoc)
	 * @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		
		CacheFactory.ensureCluster();
		NamedCache namedCache = CacheFactory.getCache("product");
		
		String productId = request.getParameter("prod_id");
		HttpSession httpSession = request.getSession();
		Map<String, String> cacheObject = (Map)namedCache.get(productId);
		
		if(cacheObject != null){
			System.out.println("----------- Read from cache -------------");
			httpSession.setAttribute("cacheObj", cacheObject);
			RequestDispatcher requestDispatcher = request.getRequestDispatcher("getRecord.jsp");
			requestDispatcher.forward(request, response);
		}else{
			System.out.println("----------- Read from db and update cache -------------");
			httpSession.setAttribute("cacheObj", readFromDatabase(productId));
			RequestDispatcher requestDispatcher = request.getRequestDispatcher("getRecord.jsp");
			requestDispatcher.forward(request, response);
		}
	}
	
	public Map<String, String> readFromDatabase(String productId){
		
		String sql = "select * from product where product_id = ?";
		Map<String, String> dbObject = new HashMap<String, String>();
		
		try {
			connection = dataSource.getConnection();
			preparedStatement = connection.prepareStatement(sql);
			preparedStatement.setString(1, productId);
			ResultSet resultSet = preparedStatement.executeQuery();
			
			while (resultSet.next()) {
				dbObject.put("productId", resultSet.getString("product_id"));
				dbObject.put("productName", resultSet.getString("product_name"));
				dbObject.put("unitPrice", resultSet.getString("unit_price"));
				dbObject.put("quantity", resultSet.getString("quantity"));
			}
			
		}catch(SQLException sqlException){
			sqlException.printStackTrace();
		}
		writeToCache(dbObject,productId);
		return dbObject;
	}
	
	/**
	 * @param productId
	 * @param productName
	 * @param unitPrice
	 * @param Qunatity
	 */
	public void writeToCache(Map<String, String> dbObject,String productId) {

		CacheFactory.ensureCluster();
		NamedCache namedCache = CacheFactory.getCache("product");
		
		if (namedCache.get(productId) == null) {
			namedCache.put(productId, dbObject);
		}
		
	}
}

now that all coding is done now here is some config related to project, first i have implemented a ServletListener
for notifying contextDestroyed,contextInitialized events

now in order to connect with our coherence server the following two config files are required.

coherence-config.xml

<?xml version="1.0"?>
<!DOCTYPE cache-config SYSTEM "cache-config.dtd">
<cache-config>
    <caching-scheme-mapping>
        <cache-mapping>
            <cache-name>product</cache-name>
            <scheme-name>example-distributed</scheme-name>
        </cache-mapping>
    </caching-scheme-mapping>
 
    <caching-schemes>
        <distributed-scheme>
            <scheme-name>example-distributed</scheme-name>
            <service-name>DistributedCache</service-name>
            <backing-map-scheme>
                <local-scheme />
            </backing-map-scheme>
            <autostart>true</autostart>
        </distributed-scheme>
    </caching-schemes>
</cache-config>

tangosol-coherence-override.xml

<?xml version="1.0" encoding="UTF-8"?>
<coherence
	xmlns="http://xmlns.oracle.com/coherence/coherence-operational-config"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.oracle.com/coherence/coherence-operational-config http://xmlns.oracle.com/coherence/coherence-operational-config/1.2/coherence-operational-config.xsd">
	<cluster-config>
		<member-identity>
			<cluster-name>MyCluster</cluster-name>
		</member-identity>
		<unicast-listener>
			<address system-property="tangosol.coherence.localhost">192.168.0.100</address>
			<port system-property="tangosol.coherence.localport">8088</port>
		</unicast-listener>
		<multicast-listener>
			<time-to-live system-property="tangosol.coherence.ttl">60</time-to-live>
		</multicast-listener>
	</cluster-config>

	<configurable-cache-factory-config>
		<init-params>
			<init-param>
				<param-type>java.lang.String</param-type>
				<param-value system-property="tangosol.coherence.cacheconfig">
					coherence-cache-config.xml
				</param-value>
			</init-param>
		</init-params>
	</configurable-cache-factory-config>
</coherence>

Now as a final step lets run the application by deploying to weblogic server and below are the screen shots.

landing-page

now fill the details and submit the form and it should look as shown.

after-save

If suppose we query any record that is not available in cache it should get that from database and update cache as shown below.
read_and_update

if you query any record that is available in cache, it should directly read from cache and it will not hit database.
read_direct_cache

Advantage of implementing cache technology is, it will drastically improve the performance of the applications.
Happy cache šŸ™‚

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s