Jul 28 2004

Finding Srinis …

Tags: Rajiv @ 1:56 am UTC

There is significant difference between doing what is asked of you and doing what is needed. Srini, our system administrator, is one of those who understands that. It is very interesting to see him go about solving problems users face. Installing what people want is not the end for him. He goes that extra mile to make sure the solution suits the end-user the best.

Take the simple case of scanning legal documents. When someone approached Srini to get a scanner installed, he did just that. Anyone who has tried scanning documents or any serious number of photographs knows that it is an extremely slow and manual process. Place the document on the bed of the scanner, make sure the edges are aligned properly, carefully put the cover down [to make sure the alignment does not get messed up], start the scan [and wait forever for it to finish], view the output in the software, crop the image, save it in the right format and then email it. A complete pain in the *bleep*, especially when you have to convert large number of documents. He couldn’t bear to see them scan documents at that pathetically slow pace. So his improvisation?! You fax your document to a local extension number, which is connected to his Linux box running the hylafax server. The server receives the fax, converts it to appropriate format and emails it back. No fuss … no mess … neat huh?!

Schematic diagram of the setup

A lot of people do not appreciate the difference between doing what a user wants and doing what the user needs. Identifying such people in an interview is a challenge. One of the things I do is to ask people to write some simple programs, say a program to sort integers or something. If the developer is interested in solving the user’s problem I would expect her to find out more about the problem before jumping to get to the solution. I would like to hear questions like: What kind of integers are these?, How many numbers are you expecting to sort at a time? This way you could eliminate those who do just what they were asked to …. implement QuickSort.

I wonder what others do to identify such people.


Jul 10 2004

The joy of separating style from content!

Tags: , Rajiv @ 9:09 am UTC

I have always had this fascination for CSS. Moving from hard-coding-style-in-HTML-sources to defining-it-in-CSS is like moving from Procedural Programming to Object Oriented Programming. Defining style classes gives a very consistent feel to the whole site [assuming you have defined your style classes properly!] And if you want to change how some of the content looks, all you need to touch is the CSS file. You no longer have to depend on complex search and replace regular expressions.

So, when I wrote the Export to HTML plug-in for Pramati Studio, I made sure the exported HTMLs used common CSS files to set colors and fonts. Changing the colors and fonts in one CSS, changes the appearance of all the sources.

Wanna checkout the layout capabilities of CSS?! How about moving the main content to the right?! Or how about choosing a more tacky theme?!

If you are a firefox user, you can choose an alternate style sheet using it’s style sheet switcher. What’s more, firefox will remember your choice the next time you visit this page!

Screenshot of style sheet switcher

And if you are one of the 30% visitors, who are still not using the firefox browser, give it a spin:

Get Firefox

Apart from the advantages you get from abstracting style into classes, better accessibility is another key benefit of using CSS. The other day I was reading this very neat book on accessibility, called Dive Into Accessibility. I loved the way the content is presented in the context of some "real people" and how they benefit from sites using CSS. In the chapter Presenting your main content first, Mark points out that screen readers read aloud the content of the HTML source from top to bottom. However, most sites have the navigation links at the top of the HTML source followed by the main content. So a screen reader has to read out all the navigation links before reaching the main content. What a pain! One of the advantages of using CSS is the ability to control the layout of HTML elements independent of their location in the HTML source. So, using CSS we could have the main content of the page at the TOP of the HTML source though it is visually layed out at the center of the page.

It hardly takes any time to read and appreciate the book … but trust me, it takes a whole lot longer to implement it and to get it into your blood! … but then I have made a start! Check out how this page would look to a screen reader or in a text browser [needs to be improved].


Jul 05 2004

An update to: Memory leaks with non-static ThreadLocals …

Tags: , Rajiv @ 7:34 pm UTC

Rejeev made some interesting comments on my previous post at javalobby. The memory leak we had faced was with JDK 1.3_02. The java bug database has a couple of bugs reported for the sameBug: 4414045 and Bug: 4455134. As per JDK 1.4 beta release notes they are now fixed.Looking at JDK 1.4 ThreadLocal sources it appears that they have made the threadLocals Map hold weak references … which was as per my expectation

The ThreadLocal should have taken care of cleaning itself up when no user code is referring to it. The way to achieve this is to make the threadLocals map of the Thread class a WeakHashMap.

Second, consider the case where the Object being put in the Map [in this case a CachedObject] has overridden the equals and hashcode methods. Say, the hashcode method returns a different hashcode based on the current fields of the CachedObject class. Now, in the suggested implementation, this CachedObject is being put in a Map. What if the hashcode of the object changes [beacause the value of some field changed], while it is in the Map. We will not be able to remove the object from the Map. The memory leak will remain.

When we decided to make the ThreadLocal a static field from non-static field we changed the value of the ThreadLocal to a Map which holds the instance of CachedObject vs Context. The assumption was that the look up’s to the Map will be based on the instance of the key. However, making the Map an instance of a HashMap broke this assumption, as the lookup’s are based on the hashcode of key. We need a Map implementation that does instance-based lookups. One way to achieve this would be to change the following HashMap methods:

260       static int hash(Object x) {
261           /* 
262           int h = x.hashCode(); 
263
264           h += ~(h << 9); 
265           h ^=  (h >>> 14); 
266           h +=  (h << 4); 
267           h ^=  (h >>> 10); 
268           return h; 
269           */ 
270           return System.identityHashCode(x);
271       }
272
273       static boolean eq(Object x, Object y) {
274           //return x == y || x.equals(y);
275           return x==y;
276       } 

We change the hash method to return the identityHashCode of the object. The identityHashCodeof an Object is constant. We change the eq methodto do an instance check. However, since these are static methods we cannot override them and make these changes. So to achieve this we will have to make a copy of the HashMap and make the changes in it.

Another alternative is to wrap the key being added into the Map. This wrapper would return the identityHashCode of the key as the hashcode and check instance equals of the keys in its equals. So we write the InstanceMap and use an InstanceMap in the ThreadLocalMap instead of a HashMap.

1    import java.util.HashMap;
2
3    public class InstanceMap
4            extends HashMap
5    {
6        private IdentityWrapper lookup = new IdentityWrapper();
7
8        public Object get(Object key)
9        {
10           lookup.key=key;
11           return super.get(key);
12       }
13
14       public Object put(Object key, Object value)
15       {
16           return super.put(new IdentityWrapper(key), value);
17       }
18
19       public boolean containsKey(Object key)
20       {
21           lookup.key=key;
22           return super.containsKey(lookup);
23       }
24       //other overridden method not listed for brevity ...
25       private static class IdentityWrapper
26       {
27           private Object key;
28
29           public IdentityWrapper()
30           {
31           }
32
33           public IdentityWrapper(Object key)
34           {
35               this.key = key;
36           }
37
38           public int hashCode()
39           {
40               return System.identityHashCode(key);
41           }
42
43           public boolean equals(Object obj)
44           {
45               return (obj instanceof IdentityWrapper)?((IdentityWrapper)obj).key==key:false;
46           }
47       }
48   }

Since this class is being used only in our ThreadLocalMap, it will never be accessed by two threads. We have tried to optimize the number of the wrapper instances created by using a single wrapper [the field called lookup] for all the lookup methods like get and containsKey. However, every call to the put method will create a new instance and every remove call will make it GC’able.