Thursday, December 10, 2009

wait(), notify(), and notifyAll()

One of the most misunderstood parts of the Java language is the use of three methods defined in the class “Object” – wait(), notify(), and notifyAll(). These methods are pivotal for Thread synchronization using guarded locks using shared objects. Hopefully, this post will help you understand this a little better.

wait(), notify() and notifyAll() can only be used within a synchronized context – that is  one cannot call these methods without acquiring an exclusive lock on the shared object.

The scenario here in the below example is that of a PUBLISHER->SUBSCRIBER. The Subscriber thread waits for published messages from the Publisher thread, and terminates when it encounters a special “DONE !” message.

The shared object, using which locking is done is defined as a class Message.

package com.threads.sync;

import java.util.logging.Logger;

public class Message {

public static final Logger logger = Logger.getLogger(Message.class.getName());;

protected String message;

//when isEmpty = false -> message is ready to be consumed
protected boolean isEmpty;

public synchronized String getMessage(){
if(isEmpty){
try{
logger.info("Consumer Waiting...");
wait();
}catch(InterruptedException e) {
logger.info(e.getMessage());
}
}
//notification received., isEmpty = false -> toggle status
isEmpty = true;
//notify all that status has changed
notifyAll();
return message;
}

public synchronized void publish(String message){
if(!isEmpty){
try{
logger.info("Producer Waiting...");
wait();
}catch(InterruptedException e){
logger.info(e.getMessage());
}
}
//isEmpty -> true, update message
this.message = message;
//toggle status
isEmpty = false;
notifyAll();
}
}





The above class is pretty self explanatory. The Message class defines a flag called isEmpty which is false when there is a message to be read by the consumer (which in this case is the Subscriber thread). When the Subscriber thread receives a notify() / notifyAll() from the Publisher it reads the message and toggles the isEmpty flag.



The classes Publisher.java and Subscriber.java are defined below:




package com.threads.sync;

import java.util.logging.Logger;

public class Publisher implements Runnable {

public static final Logger logger = Logger.getLogger(Publisher.class.getName());

protected String [] messages;
protected Message msg;

public Publisher(Message msg){
messages = new String [] {
"Lorem", "Ipsum", "Testing Thread Synchronization", "DONE !"
};
//pass in the shared objectS
this.msg = msg;
}

public Publisher(String [] messages, Message msg){
this.messages = messages;
this.msg = msg;
}

@Override
public void run() {
if(messages == null)
return;

for(String message : messages) {
msg.publish(message);
//simulating thread randomness using sleep between 1-5 seconds
int factor = (int)(Math.random() * 4 + 1);
long millis = factor * 1000;
try {
logger.info("Publisher sleeping for " + millis + "ms.");
Thread.sleep(millis);
}catch(InterruptedException e){
logger.info(e.getMessage());
}

}
}
}






package com.threads.sync;

import java.util.logging.Logger;

public class Subscriber implements Runnable {

protected Message msg;
public static final Logger logger = Logger.getLogger(Subscriber.class.getName());

public Subscriber(Message msg){
this.msg = msg;
}

@Override
public void run() {
while(true){
String message = msg.getMessage();
//sometimes the call to getMessage can result in a wait() -> handle 'null' cases
if(message != null)
logger.info("Message recd : " + message);
if("DONE !".equalsIgnoreCase(message))
break;

//simulating thread randomness using sleep between 1-5 seconds
int factor = (int)(Math.random() * 4 + 1);
long millis = factor * 1000;
try {
logger.info("Subscriber sleeping for " + millis + "ms.");
Thread.sleep(millis);
}catch(InterruptedException e){
logger.info(e.getMessage());
}
}
}
}





The ThreadSyncTest.java which is the driver program is defined as -


package com.threads.sync;

public class ThreadSyncTests {
public static void main(String[] args) {
//->Initialize Shared Object
Message msg = new Message();
//->Initialize Publisher
new Thread(new Publisher(msg)).start();
//->Initialize Subscriber
new Thread(new Subscriber(msg)).start();
}
}




Once you get around the small details, the implementation of Thread synchronization using locks is quite simple to understand.

Friday, September 11, 2009

Asynchronous HttpWebRequests

I had a neat KML/KMZ validator that i wanted to expose as a web service so that others could use the service to test their KML outputs.

For this, i allowed folks to enter the URL where the generated KML / KMZ was available; I would use this URL to download the content and then validate it. The initial validator was written in .NET (as Java was not being able to handle multiple name spaces defined in the KML / KMZ -> don't even get me started... ) and therefore I decided to expose this web service using WCF.

I was using the System.Net.HttpWebRequest to download content, and once i was done to my dismay i found that conventional use of HttpWebRequest would place a blocking call and was therefore very expensive (unlike Java's URL.openConnection()). I therefore experimented with .NET delegates and the .NET threading model and finally managed to write a clean wrapper that would make the HttpWebRequest's asynchronously. For those who are interested in doing something similar - i have shared the code.

The AsyncDownload wrapper is defined as :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;
using System.Threading;

namespace AsyncTests
{
//define delegate
public delegate void Download(string url, string path);

class AsyncDownload
{
public bool isComplete { get; set; }
public string url { get; set; }
public string path { get; set; }

Download downloader { get; set; }

public AsyncDownload(string url, string path)
{
isComplete = false;
this.url = url;
this.path = path;
this.downloader = new Download(DownloadUrl);
}

private void DownloadUrl(string url, string path)
{
HttpWebRequest request = null;
BinaryWriter writer = null;
Stream stream = null;
try
{
request = HttpWebRequest.Create(url) as HttpWebRequest;
request.Method = "GET";

IAsyncResult dHandle = request.BeginGetResponse(null, null);
HttpWebResponse resp = request.EndGetResponse(dHandle) as HttpWebResponse;
stream = resp.GetResponseStream();
writer = new BinaryWriter(File.Create(path));
byte[] buffer = new byte[32 * 1024];
int readBytes = 0;
while ((readBytes = stream.Read(buffer, 0, 32 * 1024)) != 0)
{
writer.Write(buffer, 0, readBytes);
}
//flush bytes
writer.Flush();
}
catch (Exception exception)
{
Trace.WriteLine("Exception : {0} ", exception.Message);
if (request != null)
{
Trace.WriteLine("Aborting Request : {0} ", request.ToString());
request.Abort();
}
}
finally
{
if (stream != null)
{
stream.Close();
}
if (writer != null)
{
writer.Close();
}
}
}

public IAsyncResult BeginDownload(AsyncCallback callback, object state)
{
return downloader.BeginInvoke(url, path, callback, state);
}

public void EndDownload(IAsyncResult result)
{
downloader.EndInvoke(result);
//set is complete to true
isComplete = true;
return;
}
}
}


To use the wrapper, you can do the following:



using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Text;
using System.Net;
using System.IO;
using System.Diagnostics;

namespace AsyncTests
{
class Program
{
static void Main(string[] args)
{
string url = "http://...";
string path = "C:/Temp/...";

AsyncDownload downloader = new AsyncDownload(url, path);

AsyncCallback callback = new AsyncCallback(
iar =>
{
Console.WriteLine("Testing Callbacks");
return;
}
);

IAsyncResult handle = downloader.BeginDownload(callback, null);
downloader.EndDownload(handle);

Console.WriteLine("Done Downloading");
Console.Read();
}
}
}

Friday, August 14, 2009

An Early Birthday Gift.

My Birthday is a couple of weeks away and i turn 26. To celebrate, I have gifted myself a Zune HD(pre-ordered at Amazon.com). I can't wait for September 15th, which is when the Zune HD will be officially in my hands. September is going to be an awesome month with the Zune HD release, the Apple Event, the Xbox 360 update and House M.D. (Season 6).

PS :

With all the stuff that i buy from Amazon.com, I think that it would be a lot easier to transfer 5% of what i earn per month to Amazon.com straightaway and avoid a lot of the drama involved with making payments [:)].

If you have not checked out the Zune HD already, you should. Its powered by Nvidia Tegra has a gorgeous OLED screen with an unbelievable user interface.

Tuesday, July 28, 2009

Flash and memory leaks.

Is it just me or have you stopped playing Flash videos on your personal computer? I avoid playing any Flash videos, unless i know that they are really worth it. Why you ask? Flash has a huge problem with memory allocation and deallocation. I have noticed that as i keep playing more videos, my computer starts using more and more RAM and gets slower.

Adobe says that they are aware of the problem and they are planning on fixing these problems. But the real question is, even after 10 major releases of the supposedly most popular video delivery / Rich Internet Applications platform why is there a memory leak?

Yay for Microsoft Silverlight, and lets hope that the success of Silverlight drives Adobe to fix these leaks.

Monday, June 29, 2009

Map2PDF

A lot of people have been wanting to print web maps authored with the ArcGIS JavaScript API. There have also been numerous requests for the ability to print web maps as a PDF. I wrote a simple RESTful service called Map2PDF, that exposes the ability to print web maps as PDF. Check out this sample, that uses the Map2PDF RESTful web service.

There is support for Tiled Map services, Dynamic Map Services as well as Graphic Features (limited support). The sample also includes a PrintMap.js script, that helps developers serialize the 'map state' in a format that the Map2PDF service expects.

The web service uses the Java Advanced Imaging API for mosaicing and overlay of images. Graphic Features are rendered on the server side using java.awt.Graphics2D. I have used the iText PDF engine to generate the PDF's. For source code check the ArcGIS Server Blog.

For more help on how to effectively use Map2PDF check out the API documentation and the services directory page.

Wednesday, June 10, 2009

Life on the Bleeding Edge

Sometimes i wonder if being on the bleeding edge of technology is worth it. Imagine having to pay the highest markup for goods/services and a couple of months later realizing that the stuff you bought back then is not even worth half its value. Look at Nokia, every time they come up with a new phone they price it at $699. Wait for a couple of months and it becomes $399. I realize that every company needs to make profit, but i don't understand the need to arbitrarily mark-up on the price of a commodity and then decreasing it a couple of weeks/ months later. Had you started off with a low price a lot more people would have probably bought it, or at least given it a lot of thought - and done some word of mouth marketing for you.

Another example of this is Apple's new iPhone 3GS. Just to create an arbitrary distinction, they decided not to support voice commands and video recording on the 3G. Apparently the hardware on the 3G is not good enough. Give me a break ! I have been seeing voice commands and video recording on a phone since Pocket PC days.

Also the fact that they charge iPod touch's $9.99 for software updates is just criminal ! Nobody, I mean' nobody' charges for updates on an OS. I guess folks who buy OS X are used to paying for updates, but not me. And then there is the carrier AT&T. Every Windows Mobile 6 + / Android smart phone can do VoIP over 3G but not the iPhone. And then you are expected to pay up another $70.00 (+/-) for tethering after paying for "unlimited data". No MMS support until further notice. What the hell ?

Stuff like this, really makes me wish that Android / Windows Mobile / Symbian would evolve faster. Then i can choose not to put up with all this nonsense. As it stands today, the iPhone is old news as other platforms are getting better. Here's to more choice for the consumer !!

Wednesday, June 3, 2009

My new Laptop

I just bought a new laptop, my pride and joy a Dell Studio XPS 16. It took me some time to set it up because of the sheer amount of Dell 'Bloatware' in it. Corporate greed can ruin even the most wonderful of things. I think these companies have to start getting their act together and stop shoving crap up into our laptops. Otherwise the laptop is pure awesomeness. The RGB LEB back lit screen and the back-lit keyboard is just a dream.

On a side-note, I have ISO's for most of my software setups, and while i could use Virtual CD in Windows XP, for the longest time i could not find a decent ISO mounting software
that was free for Windows Vista. I finally rediscovered Virtual CloneDrive for Windows Vista. It's a free, clean ISO mounting software that i could use hassle free on Vista. It's also extremely efficient in terms of both size on disk and memory used. Check it out !