Monday, December 4, 2006

LinkedHashMap to implement LRU caches

I must be living under a rock to have not noticed this before. The LinkedHashMap has a 3 argument constructor. The last argument, if true, orders the map according to the access order. It also defines a protected removeEldestEntry() method which returns false by default. One can override this method to return true in certain situations, in which case the map will remove its eldest entry.

The implementation of an LRUMap will look something like this:

public class LRUMap<K,V> extends LinkedHashMap<K,V> {

int maxLimit;

public LRUMap(int maxLimit) {
super(maxLimit * 10/7 + 1, 0.7f, true);
this.maxLimit = maxLimit;
}

@Override
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return size() > maxLimit;
}

}

The removeEldestEntry() method is invoked by the put*() methods. With the above implementation, if the size of the map exceeds the max limit, the eldest entry is removed.

Short and sweet. Albeit some 2 years too late for my unenlightened self.

Sunday, March 26, 2006

ArcGIS Server coding practices

The ESRI dev summit was a great experience. Meeting fellow developers is always good - you talk the same language, exchange ideas, receive feedback, discuss how things can be made better,... I could go on and on.

Ok, so I was asked quite a few server related questions at the summit: What should I keep in mind while coding against the server? Any do's and dont's? Any particular classes / methods I should read more about? etc... So here I'll talk about certain points you should keep in mind while working with the ArcGIS Server, particularly the MapServer:


  • Use the description objects: Sure the MapServer gives you access to the IMap but you don't have to go there. A lot can be accomplished by using the IMapDescription, ILayerDescription, et al.

  • Release the ServerContext: In a pooled environment you are sharing the server object with other users. So it's your responsibility to release the context once you have performed your set of operations. For web applications, this translates to releasing the context after every request. Of course, if you are using the ADF, the ADF does that for you so you need not worry. But even then it's good to keep this in mind.

  • Do not reference server objects after the context has been released: Do you continue to work with the Statement object once the JDBC Connection has been closed? NO. Why? Because a Statement can be executed only while the Connection is live. Similarly, you should not reference ANY server objects once you have released the IServerContext. If you want to persist the state of any server object, you should use the saveObject() method to get a serialized string representation of the object before releasing the context. You can once again rehydrate the object by using the loadObject() method once you have regained access to the context.

  • In case you had forgotten - use the description objects: Did you know you could add serializable custom graphics to the IMapDescription object?

  • Use methods exposed by the MapServer: Look at the javadoc for the MapServer and for all the interfaces that it implements. It can do a lot more than you might think - you can export maps, layouts, do queries, identifies, get feature info, handle SOAP requests, and a whole lot more.

  • Create server objects on the server: You don't create RMI objects on the client. You don't create EJBs on the client. Similarly, you don't create ArcGIS server objects on the client. ALWAYS use the IServerContext.createObject() method to create all and any ArcObject in a server application within your server context at the server.

  • BTW, use the description objects: Did you know you could change layer visibilities, select features, even set definition expressions with the ILayerDescription object?


This by no means is an exhaustive list but something which might help you when working with the server. Oh yeah, in parting, in case you had missed: use the description objects!

Tuesday, March 14, 2006

Using the context control in a multiple page webapp

There have been many forum questions about the correct usage of the context control in a multiple page web application. I will be the first one to admit that this is more of a bug in the ADF than an incorrect usage on the part of the user. Ok, so the short end of it is this question: How do I access the same context across multiple pages of an ADF web application?

Typically, the context is specified on a JSP using the context tag:

<ags:context id="myCtx" resource="myServerObject@myHost"/>

Now, in an ideal world, if you have multiple pages in your webapp, you should have to use the context control in the exact same manner on all other pages and it should work fine. Unfortunately, that's not the case at 9.1. Currently, the way the context tag works is that if you specify the resource attribute of the tag, the first time that the page is accessed, it actually creates a new context. What this means is that if you use the context tag by specifying the resource attribute on different pages of the same webapp, it gives an impression that the context is "resetting" itself - i.e. the current state of your application is lost and the application reverts to the original state of the map server object.

A workaround to this problem is that on subsequent pages of your webapp, you should not specify the resource attribute of the context tag and ensure that the id is same as the id that you specified on the main page:
<ags:context id="myCtx"/>
<!-- resource attribute is not used and id is same as the id of the context on the main page -->

If the resource attribute is not specified, the context tag does not create a new context but instead tries to find an existing context with the same id as specified in the id attribute. So as long as you have the correct id specified, the context that you created on the main page will be accessed on all subsequent pages of the webapp.

This is admittedly confusing and we have addressed it at 9.2 so that the context control can be used in a consistent manner across all pages of the webapp.