Monday, October 4, 2010

Thread Helper

Thread Helper will help you to call your class method in threaded environment. It will take care all the low level complexity and provide you a simple interface to call a method in threaded environment.
To use this one should extend the class called ThreadCommand and implement the execute method of that. Whatever code will be written in execute method will be called in threaded manner.

ThreadHelper has 2 methods in it
1. Call
2. Get

Call will notify the thread helper to call the execute method of Command implementation and store the result in a map and when get method will be called, result will returned to the user and it will be removed from the map.


After call method, user can do other stuff and whenever user required the output from thread just get that from get Method. This will simulate the post evaluation mechanism. In this user first call and continue with other operation and whenever required the output just call the get method.

ThreadHelper.java


import java.util.HashMap;

import java.util.Map;

import java.util.concurrent.ExecutionException;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.concurrent.Future;



public class ThreadHelper

{

private boolean callWhenFetch = false;

private ExecutorService executorService = null;

private Map> futurs = null;

private boolean started = false;



/**

*

* @param callWhenFetch

*/

public ThreadHelper(boolean callWhenFetch)

{

this.callWhenFetch = callWhenFetch;

}



/**

*

*/

public ThreadHelper()

{

}



public void start()

{

executorService = Executors.newCachedThreadPool();

futurs = new HashMap>();

started = true;

}



/**

* Check if the thread Helper is Started

*

* @return

*/

public boolean isStart()

{

return started;

}



/**

* Initiates an orderly shutdown in which previously submitted commands are

* executed, but no new commands will be accepted.

*/

public void stop()

{

if (futurs != null)

{

futurs = null;

}



if (executorService != null)

{

executorService.shutdown();

}

}



/**

* Attempts to stop all actively executing commands, halts the processing of

* waiting commands

*/

public void stopNow()

{

if (futurs != null)

{

futurs = null;

}



if (executorService != null)

{

executorService.shutdownNow();

}

}



/**

* This method takes ICommand as input and submit to ThreadPool.

*

* @param command

* @throws Exception

*/

public synchronized void call(ThreadCommand command) throws Exception

{

checkIfStarted();

Future submitted = executorService.submit(command);

futurs.put(command.getId(), submitted);

}



/**

* This method give the output to previously called command. If

* callWhenFetch parameter is set to true then If you haven't call call

* method before get then it will first call call method and then call get

* of that call. If flag is set to false and you tried to call get before

* call method it will throws exception

*

* @param command

* @return

* @throws NotNotifiedException

* @throws InterruptedException

* @throws ExecutionException

*/

public T get(ThreadCommand command)

throws NotNotifiedException,

InterruptedException,

ExecutionException,

Exception

{

checkIfStarted();

Future submitted = null;

try

{

synchronized (futurs)

{



if (!futurs.containsKey(command.getId()))

{

if (!callWhenFetch)

{

throw new NotNotifiedException(

"Object is not called before get.");

}

submitted = executorService.submit(command);

futurs.put(command.getId(), submitted);

}

T object = futurs.get(command.getId()).get();

submitted = futurs.remove(command.getId());



return object;

}

}

finally

{

if (submitted != null)

{

submitted.cancel(false);

submitted = null;

}

}

}



private void checkIfStarted() throws Exception

{

if (!isStart())

{

throw new Exception(

"Please start the thread helper before using it.");

}

}



public int waitingCallCount()

{

return futurs.size();



}

}







ThreadCommand.java



import java.util.concurrent.Callable;



public abstract class ThreadCommand implements Callable

{



private String id;



public ThreadCommand(String id)

{

this.id = id;

}



public T call() throws Exception

{

return execute();

}



public String getId() {

return id;

}



public abstract T execute();



@Override

public String toString()

{

return "ThreadCommand [id=" + id + "]";

}





}




ThreadHelperTest.java



import java.util.ArrayList;

import java.util.List;



import junit.framework.TestCase;






public class ThreadHelperTest extends TestCase

{



public void testSingleThreadImmediateResponseBefoereStart()

{

try

{

ThreadCommand command = new ThreadCommand("Test")

{



@Override

public String execute()

{

// TODO Auto-generated method stub

return "I am just checking how execute is working.";

}

};



ThreadHelper helper = new ThreadHelper();

helper.call(command);

helper.get(command);

fail();

}

catch (Exception e)

{

assertEquals("Please start the thread helper before using it.", e

.getMessage());



}

}



public void testGetBeforeCallWhenCallWhenFetchIsFalse()

{

try

{

ThreadCommand command = new ThreadCommand("Test")

{



@Override

public String execute()

{

// TODO Auto-generated method stub

return "I am just checking how execute is working.";

}

};



ThreadHelper helper = new ThreadHelper(true);

//helper.call(command);

helper.get(command);

fail();

}

catch (Exception e)

{

e.printStackTrace();

assertEquals("Please start the thread helper before using it.", e

.getMessage());



}

}



public void testSingleThreadImmediateResponse()

{

try

{

ThreadCommand command = new ThreadCommand("Test")

{

public String execute()

{

return "I am just checking how execute is working.";

}

};



ThreadHelper helper = new ThreadHelper();

helper.start();

helper.call(command);

String str = helper.get(command);

assertEquals("I am just checking how execute is working.", str);



}

catch (Exception e)

{

fail();



}

}



public void testMutipleThreadImmediateResponse()

{

try

{

ThreadCommand command1 = new ThreadCommand("Test1")

{

public String execute()

{

return "[Test 1]I am just checking how execute is working.";

}

};



ThreadCommand command2 = new ThreadCommand("Test2")

{

public String execute()

{

return "[Test 2]I am just checking how execute is working.";

}

};



ThreadHelper helper = new ThreadHelper();

helper.start();

helper.call(command1);

helper.call(command2);

String str = helper.get(command2);

assertEquals(

"[Test 2]I am just checking how execute is working.",

str);

str = helper.get(command1);

assertEquals(

"[Test 1]I am just checking how execute is working.",

str);



}

catch (Exception e)

{

fail();



}

}



public void testMutipleThreadImmediateResponseForSameCommand()

{

try

{

ThreadCommand command1 = new ThreadCommand("Test1")

{

public String execute()

{

return "[Test 1]I am just checking how execute is working.";

}

};



ThreadHelper helper = new ThreadHelper();

helper.start();

helper.call(command1);

String str = helper.get(command1);

assertEquals(

"[Test 1]I am just checking how execute is working.",

str);

str = helper.get(command1);

fail();



}

catch (Exception e)

{

assertEquals("Object is not called before get.", e.getMessage());



}

}



public void testThreadHelperStatusAfterAllGet()

{

try

{

List> commands = new ArrayList>();

for (int i = 0; i < k =" i;"> command1 = new ThreadCommand(

"Test" + k)

{

public String execute()

{

return "[Test "

+ k

+ "]I am just checking how execute is working.";

}

};

commands.add(command1);

}



ThreadHelper helper = new ThreadHelper();

helper.start();

for (ThreadCommand command : commands)

{

helper.call(command);

}



assertEquals(100, helper.waitingCallCount());



// get all odd number command

for (int i = 1; i < i =" i" i =" 0;"> command : commands)

{

if(i == 100) {

break;

}

assertEquals("[Test "

+ i

+ "]I am just checking how execute is working.", helper

.get(commands.get(i)));

i = i + 2;



}

// get rest of the commands

assertEquals(0, helper.waitingCallCount());



}

catch (Exception e)

{

e.printStackTrace();

fail();



}

}

}