QC Adsense

Thursday, June 25, 2009

Tinyurl Clone in Java in 40 lines

My colleague (in fact my Boss' Boss) Sau Sheong wrote a hack: Build a tinyurl.com clone in just 40 lines of code. He used Ruby and I was left wondering if Java could be used to do something similar. I tried it last week and have a working code in 40 lines of Java code. This post is to show the code and how I did it:
First, persistence is a breeze in Googe AppEngine and hence I used their JDO approach to store it. The class (with the annotation) is as follows:

package info.shreeni.snip4java;import javax.jdo.annotations.*;
@PersistenceCapable(identityType = IdentityType.DATASTORE)
public class Mapping {
@PrimaryKey @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
public Long id;
public String url;

As you can see, since the idea was to be as terse as possible, all imports were lumped into the same line and no JavaDocs. Also, I completely did away with getter and setter since that would have consumed more lines of code. :-)

Then comes the MappingManager, the piece that performs the I/O with the Datastore:

package info.shreeni.snip4java;import java.util.*;import javax.jdo.JDOHelper;import javax.jdo.PersistenceManager;
public class MappingManager {
private static PersistenceManager pm = JDOHelper.getPersistenceManagerFactory("transactions-optional").getPersistenceManager();
public static Long storeMapping(Mapping mapping) {
long existingMapping = getMappingByUrl(mapping.url);
return (existingMapping == -1)?pm.makePersistent(mapping).id:existingMapping;
public static Long getMappingByUrl(String url) {
Iterator i = ((List)(pm.newQuery("select from " + Mapping.class.getName() + " where url == '" + url + "'").execute())).iterator();
return (i == null || !i.hasNext())?-1:i.next().id;
public static String getLink(String urlId) {
Iterator i = ((List)(pm.newQuery("select from " + Mapping.class.getName() + " where id == " + Long.valueOf(urlId, 36)).execute())).iterator();
return (i == null || !i.hasNext())?null:i.next().url;

The first method stores the mapping, the second gets a url id based on the url (so that we don't keep bloating our DB with repeated URLs) and the third gets a URL based on the Id. The expression "Long.valueOf(urlId, 36)" converts the url id from Base 36 String to a long. Its the same approach as used by Sau Sheong's Snip. All exceptions and error handling is conveniently ignored so that it would end in a HTTP 500 at the end, which I think is a reasonable approach considering the limited amount of code we can write in 40 lines.

Then comes the servlet code which converts a URL into a short URL and print it to the user:

package info.shreeni.snip4java;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.*;
public class Snip extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Mapping mapping = new Mapping();
mapping.url = req.getPathInfo().substring(1);
resp.getWriter().print(req.getRequestURL().toString().replaceAll(req.getPathInfo(), "/").replace("/s/", "/r/") + Long.toString(MappingManager.storeMapping(mapping), 36));

Quite fascinatingly, the first two lines in the doGet method serve very little, but the third pretty much does all the business logic, or storing the url to db, getting the id, converting to Base 36, constructing the URL and printing it.

Now, the last class which does the real work or taking a short URL and redirecting it to the correct URL stored in the DB:

package info.shreeni.snip4java;import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.*;
public class Redirect extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

Right then, we are done. Thats exactly 40 lines of Java Code. The only thing left is the Web.xml and the appengine-web.xml which are XML required for running all this Java code (first required by any Servlet container and the second required by Google AppEngine.) Being XML, each of those can be compressed into 1 line each, but I am not going to bother presenting them here since they are both too trivial. I put in a index.html just for those users who may not know how to use it (again, this can be packaged into one line). Even if we were to include these three, it would come to only 43 lines.

Thats it, go ahead and try SnipOnJava. Examples: Yahoo.com, Sau Sheong's Snip blog entry.

Monday, June 01, 2009

Stray Story of the Dogs

Back in Bangalore, stray dogs were a most common sight. They were everywhere - streets, parks, lakes, bus stops - absolutely everywhere. This is not unexpected, considering that Bangalore is one of the cities with highest number of dogbites anywhere in the world. While most times they were a harmless sight, but once in a while they turned dangerous - they killed couple of kids sometime back. Closer to my life, my father was paranoid about being attacked by the canine sorts and hence anytime post 9PM if he had to return home, he would call from the bus stop to ask me to come pick him up. Efforts from the Municipal authorities never seemed to heed any results (given that they killed only 200 when easily that many can be found in any one of the thousands of localities in Bangalore, it isn't surprising)

And after I moved to Singapore, stray dogs are nowhere to be found. I started to almost miss the sight of an ungainly looking dog begging for attention from you with their most soulful eyes. I am guessing that the ASD and the SPCA do a wonderful job of keeping the strays off the streets.

And then today arrived. I managed to sight one dog, rather a bitch, right in my apartment block. I present, ladies and gentlemen, Bitch Stray Simei to you:

Administrative note

All tech posts have now moved to http://tech.shreeni.info or you may subscribe to its RSS feed. In due course, I shall be moving it out completely, so if you follow my tech posts, please shift to following that blog.