Showing posts with label adf. Show all posts
Showing posts with label adf. Show all posts

Sunday, April 15, 2007

SOAP over DCOM in the ArcGIS ADF explained

The ArcGIS Server Java ADF supports accessing the ArcGIS Server over the internet (http) as well as locally (dcom). Internet access uses SOAP. Local access can work with the server objects directly over DCOM or you can also issue SOAP calls over DCOM.

For local access, the ADF gives preference to SOAP over DCOM while it accesses the ArcObjects directly over DCOM only when the functionality is not available thru SOAP. There are 2 primary reasons why SOAP / DCOM is preferred to ArcObjects / DCOM:

  • Performance
  • Code reuse

Performance:

When you work with ArcObjects / DCOM the server gives you access to remote ArcObjects proxies (such as IMapDescription, ILayerDescription, etc.). Every method call on these proxies is a remote method call. So methods such IMapDescription.getName() and ILayerDescription.getID() are both remote method calls.

On the other hand, when you work with SOAP / DCOM, only the methods defined in the WSDLs are remote calls. Once you have made those remote calls, you get access to objects which are local value objects (such as MapDescription, LayerDescription, etc.). So methods such as MapDescription.getName() and ILayerDescription.getLayerID() are both local method calls.

As you can infer, ArcObjects / DCOM elicits more "chattiness" with the server than SOAP / DCOM. And the reduced number of remote calls in case of SOAP / DCOM obviously translates to better performance over the lifetime of your application.

Code reuse:

If you look at various functionalities supported by the ADF such as AGSMapFunctionality, AGSGeocodeFunctionality, etc., the same functionality classes are used for both internet and local access. This was possible because these functionalities were implemented by using the SOAP interface to the server. The transport is HTTP in case of internet access and DCOM in case of local access but the code remains the same allowing us to reuse the same functionality implementation in both cases.

Capabilities such as the EditingTask which are not available with SOAP have obviously been implemented by using ArcObjects / DCOM.

Bottom line:

In summary, if your functionality can be implemented by using the SOAP interface you should use it. The richness of ArcObjects / DCOM is of course always available to you in cases where SOAP does not suffice.

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.

Monday, September 26, 2005

Extend the ArcGIS ADF with POJOs - Part III


The first cardinal rule when working with pooled ArcGIS Server objects in a webapp is that you must release the server context after every web server request. This is because you are sharing the server object with many other users and you need to put it back into the pool so that other users can access it. The second cardinal rule is that you cannot reference any server object after you have released the server context. If you do, it would be like executing a JDBC Statement after closing the Connection. You always need a live connection or a context in a client-server environment to work with objects that are remotely hosted.



So the question then is: How do I persist with the current state of a server object after releasing the context? The answer lies in the saveObject() and loadObject() methods of the IServerContext. You can serialize objects to their string representations with saveObject() and you can deserialize them to their object forms with loadObject(). So calling saveObjects before releasing the context and calling loadObjects on reconnect sets you up well to persist with the current client state while working with pooled objects.


As always, the ADF assists you in making such experiences easy for you. Rather than you having to scratch your heads about when to calls loads and when to calls saves, where to call them, how to keep track of them, et al the ADF provides you a simple interface in WebLifecycle where in you can do all of these tasks and that for only those objects needed for that particular task. The ADF calls relevant methods of the WebLifecycle just prior to releasing the context as well as immediately after a reconnect.


In the CountFeatures class that we have been working on through Parts I and II we may want to persist with the SpatialFilter object. Generally you persist only those objects which have enough client state in them for you to justify the saves and loads. It's obvious that the SpatialFilter doesn't have much state in it to merit justification but the idea here is to showcase how to do it easily so that you can apply it to more pertinent objects such as graphic elements and symbols.


The WebLifecycle defines 3 methods - activate(), passivate() and destroy() - which are called at different stages of a request / session lifecycle by the ADF. The passivate() method is called after a request has been serviced giving you the opportunity to serialize server objects to strings. The activate() method is called before servicing the request where you can deserialize the strings so that they are available as live objects when you perform the business tasks. And finally the destroy() is called when the session is being terminated for you to perform the necessary cleanup.


Below is the code which extends the CountFeatures class to participate in this lifecycle:



public class CountFeatures implements WebContextInitialize, WebContextObserver, WebLifecycle {
...
//serialized SpatialFilter - valid after passivate() and before activate()
String serializedSpatialFilter;
//the spatial filter object - only valid between activate() and passivate()
SpatialFilter filter;

public void init(WebContext webContext) {
...
//create SpatialFilter and set spatial relationship
filter = new SpatialFilter(agsctx.createServerObject(SpatialFilter.getClsid()));
filter.setSpatialRel(esriSpatialRelEnum.esriSpatialRelContains);
}

public void passivate() { //serialize to strings
serializedSpatialFilter = agsctx.saveObject(filter);
}

public void activate() { //deserialize to objects
filter = new SpatialFilter(agsctx.loadObject(serializedSpatialFilter));
}

public void destroy() { //cleanup
filter = null;
serializedSpatialFilter = null;
}

public String doCount() throws Exception {
...
//spatial filter is already created - only need to set geometry to the current extent
filter.setGeometryByRef(agsmap.getFocusMapExtent());
...
return null;
}
}


The complete source code can be downloaded from here.


It's important to note that the loads still create new instances of the objects on the server. So there are no performance benefits to saves and loads versus new instance creations. The benefit to gain is that you don't have to worry about persisting with the state of the object yourself. The server will do that work for you.


That does it for this trilogy (ok so that was blatant abuse of that word - but hey, who's to say... It's my world around here :)

Monday, August 22, 2005

Extend the ArcGIS ADF with POJOs - Part II

In Part I we discussed how you could implement GIS functionalities in POJOs and plug them into the ADF. In this part we'll extend the POJO a little further.

In Part I, the CountFeatures object calculated and updated the feature count on a client interaction such as a button click. Suppose that we now need for this object to recalculate the count automatically whenever the current extent of the map changes or the map refreshes due to some other action.

The ADF provides a very simple way to do this. Objects can register themselves as observers of the WebContext and whenever the context is refreshed (by virtue of the user calling the refresh() method on the context), all observers will be intimated of this event and each observer can act upon it individually. This way we have loosely coupled objects reacting together to the context refresh.

With this background let's now extend our CountFeatures class to implement this behavior.


public class CountFeatures implements WebContextInitialize, WebContextObserver {

public void init(WebContext context) {
...
context.addObserver(this);
}

public void update(WebContext context, Object arg) {
doCount(); //perform the business action on update
}
...
}

First up, all observers of the WebContext need to implement the WebContextObserver interface. Next, they register themselves as observers of the context by calling the addObserver() method on the context. Finally, on every context refresh, the update() method of the WebContextObserver interface is called by the ADF and the object reacts (performs the business action) to the same. In this case we simply call the doCount() method which recalculates the feature count of the updated map. This will ensure that whenever the context refreshes (for example when the user zooms or pans), this object will recalculate and display the new count to the user.

As simple as that. Apart from a few modifications to the Java code, nothing else needs to change from the Part I source code. The JSP as well as the configuration files remain unchanged. You can download all the source code (including the unchanged JSP) for this part from here.

In Part III we'll extend CountFeatures further by implementing the WebLifecycle interface.

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.

Sunday, August 7, 2005

Inversion of Control in the ArcGIS Java ADF

Martin Fowler in a recent blog gave a good short explanation of the inversion of control pattern... In ESRI's ArcGIS Java ADF we employ this approach at a few places.


  1. The WebContextInitialize interface declares an init(WebContext) method. The ADF will call this method on objects which implement this interface and register themselves as attributes of the WebContext. This method will be called immediately after they are registered with the WebContext. Users interested in getting access to the associated WebContext object or want to do some initialization tasks should implement this interface.

  2. The WebLifecycle interface declares methods which will be called by the ADF at various phases of the webapp's lifecycle. Users can implement activation, passivation and destroy logic in these methods. This interface is most relevant when using pooled objects since users may want to rehydrate and dehydrate the states of the server objects when the ADF reconnects and releases its connection to the ArcGIS server on every request.

  3. The WebContextObserver interface declares an update(WebContext webContext, Object args) method. Objects implementing this interface can register themselves as observers of the WebContext by calling the addObserver(WebContextObserver) method. After users perform operations which change the state of the objects that they work with (for example zoom to a different extent, add a graphic element, etc.), they call the refresh() method on the WebContext. When this happens, the ADF iterates thru all the registered observers of the context and calls their update() methods. This ensures loose coupling among the various objects but at the same time gives these loosely coupled objects an opportunity to be in sync with the changed state of the app. This is a classic implementation of the observer pattern with the WebContext acting as the Observable object.


With the advent of JDK 5 annotations, it might be convenient for the users if #1 could be achieved by simply annotating a field or a setter method with an @Resource like annotation. The ADF on encountering this annotation on a WebContext field or setter method could inject the same into the interested object. Further, users can do the initialization tasks in any arbitrary method annotated with the @InjectionComplete annotation and the ADF will call this method immediately after injecting the WebContext. (Both these annotations are proposed by JSR 250 - Common Annotations).

#2 could also be achieved through annotations. Much like how EJB3 is proposing @PostActivate, @PrePassivate, et al; users can annotate the lifecycle methods with annotations such as @OnActivate, @OnPassivate and @OnDestroy. This would alleviate them of having to implement interfaces for lifecycle callbacks and further, they can choose to participate in only those phases of the ADF lifecycle which makes business sense for their objects.

Comments / feedbacks welcome.

Tuesday, August 2, 2005

Adding layers dynamically in the ArcGIS Java ADF

There have been many questions about adding layers dynamically in the ADF... And of course the requirement is that the added layer will reflect not only on the map but also on the TOC, layer drop downs, etc...

When working with non-pooled objects, there's a straightforward way of doing this. Look at the source code below:


AGSWebContext agsCtx = ...; //get hold of the AGSWebContext
AGSWebMap agsMap = (AGSWebMap)agsCtx.getWebMap();

//Step 1
agsCtx.applyDescriptions();

//Step 2
MapServer mapso = new MapServer(agsCtx.getServer());
IMap map = mapso.getMap(agsMap.getFocusMapName());
ILayer layer = ...; //create the layer
map.addLayer(layer);

//Step 3
agsCtx.reloadDescriptions();

Let's discuss the 3 steps now:

  • Step 1: Before making stateful changes to the object graph (like adding a new layer in this case) you want to apply the current state of the MapDescriptions to the object graph. Calling the applyDescriptions() method on the AGSWebContext does exactly that.

  • Step 2: Now that you have the object graph in the current state, you can make the modifications there. In this case we add a new layer to the map.

  • Step 3: Once you have made changes to the object graph you want to reload the MapDescriptions to reflect the changed state and additionally, you also want the web controls to display this new state (in this case display the layer on the map control, on the TOC control as well as on any other components working with layers). A single call to the reloadDescriptions() method on the AGSWebContext will do all of this for you.


And that's about it! This sequence of steps holds true for any stateful changes that you want to make to non-pooled objects. The 3-word mantra is APPLY-CHANGE-RELOAD.

If you wanted to work with dynamic layers (or make any stateful changes) in the pooled context, there's indeed more work to do because you are now sharing the server object with others and you want to return the object back to the pool in the same state that you had received it. You need to get access to the server object, apply the current MapDescriptions to the object graph, make the changes to the object graph, reflect the changes in the MapDescriptions and the web controls, undo the changes to the graph and then return the object back to the pool. You can check out the dynamic layers sample on EDN to see this use case in action.

Friday, July 22, 2005

ArcGIS Server 9.2 (Java): Coming soon to a GIS near you

The ESRI User Conference is around the corner and it's time to talk about what the future holds for our users. Before I delve into the details let me say this: ArcGIS Java users, we have heard you! Here's what the future holds for you:


  • A pure Java experience: No more query interfacing... no proxies to deal with... The Java programmer will get an API s/he lives by - pure Java... diagnostic exception handling... established design patterns... the whole shebang... you name it we got it!

  • A rich SDK experience with Eclipse plug-ins to help you get easily started with using ArcGIS... one click deploy and run of samples... ready to use application templates... code snippets... an integrated and intuitive help system...

  • The web tier is being reworked to leverage state of the art technologies (if you are thinking AJAX you are right, but there's MORE)... how about the ability to work with multiple data sources - ArcGIS, ArcIMS, ArcWeb Services... how about the ability to add your custom source... dynamic layer reordering and grouping... network analysis... the list goes on...

  • Geoprocessing: You'll get a pure Java experience with GP too - tools, Java data types, geoprocessor, exceptions... You can also build your own custom tool / model, use a tool generator to generate a pure Java class for the same and just like that you are ready to incorporate new GP functionality into your Java GIS.

  • OTB EJBs to integrate GIS with your J2EE infrastructure.


All this and more will be discussed at the UC in a couple of sessions:

  • ArcGIS Road Ahead: What's Coming for GIS Developers at 9.2

    • Offering I - Wed 8:30 AM Room: 6A (SDCC)

    • Offering II - Thu 1:30 PM Room: 6A (SDCC)



  • ArcGIS Road Ahead: What's Coming in ArcIMS and ArcGIS Server at 9.2

    • Offering I - Tue 1:30 PM Room: 6A (SDCC)

    • Offering II - Thu 8:30 AM Room: 6A (SDCC)




I'm sure you are rushing to add them to your agenda! Also, here's the UC Q and A for more info...

See ya!