Wednesday, March 26, 2014

Java 8 Features Part 1

"Change is the only thing that doesn't change". This statement explains well anything and Java is not an exception in this regard. Java version 8 is released officially and you can download it from Oracle. There are some significant changes introduced in Java 8 and following are some of those;
  1. Parallel Operations - Parallel Operations for JVMs running on top of Multi Core Platforms
  2. Method References - Ability to pass in a Method as a Reference. 
  3. Lambda Expressions - Biggest Syntax Changes to Java Ever
So I'll start off with Parallel Operations and make my way down on each topic. 

Parallel Operations have been introduced to leverage the Multiple CPUs available in almost every modern computer. Operations such as Sorting could become significantly faster if Multiple CPUs were used. The Arrays.parallel* methods provide these functions.

import java.util.Arrays;

public class ParallelOperations {

    public static void main(String[] args) {
        Integer[] ints = Util.generateRandomIntegers();
        sort(ints);
        Util.display(ints);
        ints = Util.generateRandomIntegers();
        parallelSort(ints);
        Util.display(ints);
    }

    public static void sort(Integer[] ints) {
        long startTime = System.currentTimeMillis();
        Arrays.sort(ints);
        long endTime = System.currentTimeMillis();
        System.out.printf("sort() Finished in %d milliseconds\n",
                (endTime - startTime));
    }

    public static void parallelSort(Integer[] ints) {
        long startTime = System.currentTimeMillis();
        Arrays.parallelSort(ints);
        long endTime = System.currentTimeMillis();
        System.out.printf("parallelSort() Finished in %d milliseconds\n",
                (endTime - startTime));
    }

}


And passing method references can be done as following;

import java.util.Arrays;
import java.util.Comparator;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

public class MethodReferences {

    public static void main(String[] args) {
        Integer[] ints = Util.generateRandomIntegers();
        // Passing Arrays.sort static method as an argument
        sort(Arrays::sort, ints);
        Util.display(ints);
        ints = Util.generateRandomIntegers();
        // Passing Arrays.parallelSort static method as an argument
        sort(Arrays::parallelSort, ints);
        Util.display(ints);
        String[] strings = new String[] {"one", "TWO", "tHree"};
        // Passing Arrays.parallelSort static method and String.compareTo instance method as an arguments
        sort(Arrays::sort, String::compareTo, strings);
        Util.display(strings);
        strings = new String[] {"ball", "apple", "cat"};
        // Passing Arrays.parallelSort static method and String.compareToIgnoreCase instance method as an arguments
        sort(Arrays::parallelSort, String::compareToIgnoreCase, strings);
        Util.display(strings);
    }
    
    public static void sort(Consumer<Integer[]> sorter, Integer[] values) {
        sorter.accept(values);
    }
    
    public static void sort(BiConsumer<String[], Comparator<String>> sorter, Comparator<String> comparator, String[] values) {
        sorter.accept(values, comparator);
    }
}

References
  • Java 8 Compiler support for Eclipse Kepler - https://wiki.eclipse.org/JDT/Eclipse_Java_8_Support_For_Kepler

Tuesday, March 25, 2014

Tiny Url with Redis

As part of work, I was asked to research about Redis, a Distributed Key-Value Persistence Store developed to make caching extremely fast. So far I have found the following by going through documentation, Redis;
  1. An In-memory Key-Value persistence store with off the shelf advanced data structures and algorithms
  2. Supports Hashes (Tables), Lists, Sets, Sorted Sets and of course Key-Value pairs
  3. Enables I/O concurrency but no execution parallelism (Single threaded engine).
  4. Supports command pipelining for lesser network roundtrips.
  5. Supports transactions on demand by queuing commands.
  6. Supports Custom Script Execution (Similar to Stored Procedures)
  7. Supports Publish/Subscribe on topics
Redis stands out from Memcached because of its built-in datastructures and operations such as Lists, Sets, Hashes and Sorting functions. Redis is widely used in alot of applications which requires high performance such Twitter, Instagram, Stackoverflow and many more.

To get some hands on experience on Redis I wrote a simple Tiny Url application using Redis and Spring MVC. Two Hash data structures are used in my application to store both the Url Code to Url Mapping and Url Code to Click Counts. 
  • tinyurls - To store Url Code to Url Mapping
  • tinyurls:clicks - To store Url Code to Click Counts
The controller for the application looks as the following. Where jedisClient is an instance of Jedis, a Java Client for Redis. 

package com.shazin.tinyurl;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.TransactionBlock;
import redis.clients.jedis.exceptions.JedisException;

@Controller
@RequestMapping("/")
public class TinyUrlController {

    private static final String CLICKS = "tinyurls:clicks";

    private static final String TINY_URLS = "tinyurls";

    @Inject
    private Jedis jedisClient;

    @RequestMapping(value = "create", method = RequestMethod.POST)
    public ModelAndView create(@RequestParam("url") String url,
            HttpServletRequest request) {
        ModelMap map = new ModelMap();
        String tinyUrl = null;
        Long timeInMillis = System.currentTimeMillis();
        String urlKey = Long.toHexString(timeInMillis);
        jedisClient.hset(TINY_URLS, urlKey, url);
        jedisClient.hset(CLICKS, urlKey, "0");

        tinyUrl = generateTinyUrl(request, urlKey);

        map.put("tinyurl", tinyUrl);
        return new ModelAndView("home", map);
    }

    public String generateTinyUrl(HttpServletRequest request, String urlKey) {
        String tinyUrl;
        tinyUrl = new StringBuilder()
                .append((request.isSecure() ? "https://" : "http://"))
                .append(request.getServerName()).append(":")
                .append(request.getServerPort())
                .append(request.getContextPath()).append("/u/").append(urlKey)
                .toString();
        return tinyUrl;
    }

    @RequestMapping(value = "u/{urlKey}", method = RequestMethod.GET)
    public ModelAndView forwardUrl(@PathVariable("urlKey") String urlKey, HttpServletResponse response) {
        String destinationUrl = null;
        ModelMap map = new ModelMap();
        String url = jedisClient.hget(TINY_URLS, urlKey);
        if(url != null && url.length() > 0) {
            jedisClient.hincrBy(CLICKS, urlKey, 1);
            map.put("url", url);
            destinationUrl = "redirect";
        } else {
            map.put("errorMsg", "Invalid Code in Url!");
            destinationUrl = "home";
        }
        
        return new ModelAndView(destinationUrl, map);
    }
    
    @RequestMapping(value="/", method=RequestMethod.GET)
    public ModelAndView home() {
        return new ModelAndView("home");
    }
    
    @RequestMapping(value="/clicks", method=RequestMethod.GET)
    public ModelAndView clicks(HttpServletRequest request) {
        ModelMap map = new ModelMap();
        Map<String, String> clickCounts = jedisClient.hgetAll(CLICKS);
        
        Map<String, String> counts = new HashMap<String, String>();
        
        for(Map.Entry<String, String> entry:clickCounts.entrySet()) {
            counts.put(generateTinyUrl(request, entry.getKey()), entry.getValue());
        }
        
        map.put("clickCounts", counts);
        
        return new ModelAndView("clicks", map);
    }
}


And the home.jsp looks as the following.



<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Tiny Url</title>
<link href="${pageContext.request.contextPath}/resources/main.css" type="text/css" rel="stylesheet"/>
</head>
<body>
    <form id="urlShortenerForm" action="${pageContext.request.contextPath}/create" method="POST">
        <center style="margin-top: 10%">
            <c:if test="${errorMsg != null}">
                <p style="color: red;">
                    <b>${errorMsg}</b>
                </p>
            </c:if>            
            <p>Shorten any url!</p>
            <c:if test="${tinyurl != null}">
                <p>
                    <b>The Tiny Url is <a href="${tinyurl}">${tinyurl}</a></b>
                </p>
            </c:if>
            <p>
                <textarea id="url" name="url" rows="5" cols="50"></textarea>
            </p>
            <p>
                <input type="button" onclick="validateUrl()" value="Shorten" />
            </p>
            <p>
                <a href="${pageContext.request.contextPath}/clicks">Clicks Count</a>
            </p>
        </center>
    </form>
</body>
<script type="text/javascript">
    function validateUrl() {
        var textArea = document.getElementById("url");
        var valid = false;
        var value = textArea.value;
        if(value.length > 0) {
            var httpIndex = value.toLowerCase().indexOf("http");
            var columnIndex = value.indexOf(":");
            var slashIndex = value.indexOf("/");        
            if(httpIndex > -1 && columnIndex > -1 && slashIndex > -1) {
                valid = true;
            }
        }
        if(valid) {
            var urlShortenerForm = document.getElementById("urlShortenerForm");
            urlShortenerForm.submit();
        } else {
            alert("Invalid Url, Please enter a valid Url");
            textArea.value = "";
            textArea.focus();
        }
    }
</script>
</html>

After submitting a URL, this how the Tiny URL will look like.


The clicks.jsp will look like the following


<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
<title>Tiny Url Click Counts</title>
<link href="${pageContext.request.contextPath}/resources/main.css" type="text/css" rel="stylesheet"/>
<style type="text/css">
table
{
border-collapse:collapse;
}
table, td
{
border: 1px solid black;
text-align: center;
}
th {
background: aqua;
}
</style>
</head>
<body>
    <center style="margin-top: 10%">
        <p>Click Counts</p>

        <p>
        <table border="1">
            <tr>
                <th>Url</th>
                <th>Click Count</th>
            </tr>
            <c:choose>
                <c:when test="${clickCounts.size() != 0}">
                    <c:forEach items="${clickCounts}" var="clickCount">
                        <tr>
                            <td><a href="${clickCount.key}">${clickCount.key}</a></td>
                            <td>${clickCount.value}</td>
                        </tr>
                    </c:forEach>
                </c:when>
                <c:otherwise>
                    <tr>
                        <td colspan="2">No clicks found</td>
                    </tr>
                </c:otherwise>
            </c:choose>

        </table>
        </p>

        <p>
            <a href="${pageContext.request.contextPath}">Back</a>
        </p>
    </center>
</body>
</html>

Finally the redirect.jsp will use a Javascript to redirect to the destination URL.

<html>
    <script type="text/javascript">
        window.location = "${url}";
    </script>
    <b>Redirecting Please wait...</b>
</html>

So in the backend the Url Code is generated using the Current Time in Milliseconds converted to Hexadecimal, and stored with the URL in tinyurls hash along with the click count which is 0 in tinyurls:clicks hash. And when the generated tiny url is clicked the corresponding url is retrieved from tinyurls hash based on the Url Code and tinyurls:clicks is incremented by one based on Url Code. Finally the user is redirected to Original URL. 

For this to work redis-server must be running!

This is just a basic example of the capabilities of Redis but is a good starting point. Hoping to learn further!