Tuesday, August 9, 2005

Extend the ArcGIS ADF with POJOs - Part I

This is the first of a 3 part series where we'll discuss how to add custom GIS functionality to the ADF as POJOs (Plain Old Java Objects). To accomplish this we'll be leveraging the IOC inherent in the ADF discussed earlier. We talked about 3 very important interfaces in the IOC discussion - WebContextIntialize, WebContextObserver and WebLifecycle. Putting these 3 interfaces in practice will be central to the 3 parts respectively. In this part we'll make use of the WebContextInitialize interface.

We'll keep the functionality to be implemented quite simple: Count the number of features of a given layer in the map's current extent.

To implement this scenario our POJO will need a few basic properties and methods - a read-only count property, a read/write layerId property representing the layer whose features are to be counted and a business method doCount() which implements the business task at hand. With this said, the skeleton of the class (we'll call it CountFeatures) will be as such:


public class CountFeatures {

//properties
int count;
int layerId;
public int getCount() { return count; }
public int getLayerId() { return layerId; }
public void setLayerId(int layerId) { this.layerId = layerId; }

//business method
public String doCount() {
...
...
count = ...;
return null;
}
}

You might have noticed that the doCount() method returns a String. This is because the ADF is JSF based and when the user clicks on say a command button on a web page, it results in a call to doCount(). Based on the return value of this method the JSF framework decides which page to navigate to. Returning a null ensures that the webapp stays on the same page.

OK, so now we have the skeleton in place but we also need access to the ArcGIS Server and the underlying ArcObjects to perform the GIS task at hand. This is where the WebContextIntialize comes into the picture:

public class CountFeatures implements WebContextInitialize {

//the context associated with this object
AGSWebContext agsctx;
public void init(WebContext context) {
agsctx = (AGSWebContext)context;
}
...
}

The ADF will call the init(WebContext) method of objects implementing WebContextInitialize immediately after the object is instantiated. This gives the object access to the WebContext. The AGSWebContext (which is the actual implementation of the WebContext that we work with) maintains references to the ArcGIS Server objects and ArcObjects (such as IMapServer, IMapDescription, etc.) as well as to other ADF objects (such as AGSWebMap). This implies that by virtue of gaining access to the AGSWebContext our custom object now has a hook into the whole of ArcObjects as well as the ADF - basically everything that you need to accomplish your GIS task at hand.

With access to everything that our class needs, the business logic can now be implemented in the doCount() method to perform the count operation and set the result to the count variable.

That's it - our Java code ends here. All that is left to do now is to register this object as a managed attribute of the WebContext so that the ADF can automatically instantiate the object on demand as well as call the init(WebContext) method immediately after instantiation. This is accomplished by adding the following lines of XML to managed_context_attributes.xml which you can find in the /WEB-INF/classes folder of your ADF webapp:

<managed-context-attribute>
<name>countFeatures</name>
<attribute-class>custom.CountFeatures</attribute-class>
<description>counts features of a given layer in the current extent...</description>
</managed-context-attribute>

With this done, you can now access our custom object by name (countFeatures in this case).

You can download the full source here. In addition to the Java code, the ZIP file also contains a sample JSP. The JSP has a command button to trigger the business method, a dropdown to choose the layer and a text out to display the count.

In conclusion I'd like to mention that while admittedly the functionality that we have implemented here is trivial, you can essentially follow the same programming model to implement your own functionality as well: POJOs which implement WebContextInitialize

In Part II we'll extend this same object to be an observer of the context and in Part III we'll make this object participate in the ADF lifecycle.

No comments: