Cocoon ArcIms

Major rewrite: -- EricBoisvert - 31 Oct 2005

Introduction

This page documents implementing a wrapper over ArcIMS using Apache Cocoon. For information about Cocoon (if you are not familliar with this technology), see AboutCocoon.

ESRI's ArcIms is more than a interactive web mapping application. The client application running on your browser is communicating with a server side application that generates pictures and data that is used by the application to display map and data. The communication between the client application and the server application is done by exchanging xml documents, the client sends request documents and the server responds with response documents. This is pretty similar to a WFS or a WMS, except that ArcIMS uses a proprietary XML schemas called ArcXML.

To interact with ArcIMS server, oine basically needs to create a proper ArcXML document, send it throught POST protocol to the server and parse the response document. There are many ways to do this, ArcIMS client application uses Javascript, but any language and or framework that can send an XML document over the internet can do.

Cocoon is such a framework, it can send a request to another server and handle the results. Cocoon is good at handling XML document and therefore, this is a perfect job for Cocoon.

This page is more on the nitty-gritty of connecting Cocoon to ArcIMS, CanadaTestBed2 deals with implementing real functionalities.

Use case

In this small example, We want to send an ARCXML to a server and get the response back in plain XML. For this purpose, I created a small html page


<html><head><title>IUGS test</title></head>
<body>
<h2>ArcXML request</h2>
<form action="http://localhost:8080/cocoon/iugs/testbed2" method="POST">
<textarea name="xml:request" cols="40" rows="10">
<ARCXML version="1.1">
   <REQUEST>
      <GET_IMAGE>
         <PROPERTIES>
         </PROPERTIES>
      </GET_IMAGE>
   </REQUEST>
</ARCXML>
</textarea>
<input type="submit"/>
</form>
</body>
</html>

upon clicking on send, I want to get this response from ArcIMS

<?xml version="1.0" encoding="ISO-8859-1"?>
<ARCXML xmlns:h="http://apache.org/cocoon/request/2.0" version="1.1">
  <RESPONSE>
    <IMAGE>
    <ENVELOPE minx="-150" miny="26.375" maxx="-47" maxy="103.625"/>
    <OUTPUT url="http://s5-601-ims3.ess.nrcan.gc.ca/output/GDR_E_S5-601-IMS3329634521618.png"/>
   </IMAGE>
  </RESPONSE>
</ARCXML>

The request and the response are unaltered (this is just a pass-through), in this example, Cocoon merely passes the request to ArcIMS and send the responses back to the client.

Source code

What could be called source code in Cocoon is a master file that defines components and actions to be taken, the sitemap, and a bunch of stylesheets and other xml files that controls what going on. We could see the sitemap like a program that has several parts. The parts we are interested in here is the components parts, where we declare new components and the pipelines part where we declare how these components will be used and when.

Component declaration

To handle POST request, a small component has been developed to overcome small issues with regards on how Cocoon handles request. This component is located in the jar file attached to this page.

I assume that the concept of sitemaps, etc.. is understood. This is just a portion of the sitemap that we are interedted in. My current site map can be read dynamically from http://ngwd-bdnes.cits.rncan.gc.ca/service/iugs/source

note This is a work in progress, the sitemap is likely to change and might not be identical to the small fragments I present on this page.

In the component section, I declare my brand new components (jar file at the bottom of this page)

<map:components>
<map:generators default="file">
   <map:generator label="content" logger="sitemap.generator.request" name="xmlStream" pool-grow="2" pool-max="16" pool-min="2" src="org.apache.cocoon.generation.XmlStreamGenerator"/>
</map:generators>
<map:transformers default="xslt">
<map:transformer name="ArcIms" logger="ngwd.transformer"
   src="org.apache.cocoon.transformation.ArcIms"/>
<map:transformer logger = "ngwd.transformer" name="wfs-frag" pool-grow="2" pool-max="32" pool-min="8" src="org.apache.cocoon.transformation.WfsTransformer">
   <map:xslt-processor-role>xalan</map:xslt-processor-role>
   <map:check-includes>true</map:check-includes>
</map:transformer> 
</map:transformers>
</map:components>

This tells Cocoon that a new transformer locally named ArcIms is available and the class implementing it is located in the package
org.iugs.ArcImsTransformer
. Actually, there are more than one component in the jar file, it also contains a generator called XmlStreamGenerator that deals with sending XML document using HTTP Post. This component had to be hacked to deal with the way Gaia set its content-type.

This segment of the sitemap is read at Cocoon startup and the classes containes in the jar are loaded in memory.

Components

  • xmlStream: based on existing Stream Cocoon component that adds a minor fix to deal with Gaia POST request

  • ArcIms: component is the main component here, that send a XML request to a ArcIMS server and serializes the result to the next component.

  • wfs-frag: component is a xslt transformer based on Nico Verwer's MultiFragmentTraxTransformer. This transformer process large documents structured as a long series of similar fragments (such as FeatureCollection, which is a long series of Features). Regular XSLT engines load the complete document before processing it, because any portion of the document can potentially be needed by the processor. wfs-frag assumes that each fragments are isolated, therefore they can be processed individually. This allow the processor to load only the small fragment, process it and send it to the next component. An alternative to this is to use stx transformer (which is available in Cocoon)

Pipeline

The pipeline is the bit of logic that actually handles the request from the client and invoke the components. The pipelines part of the sitemap tells cocoon what component to call in function of which URI is being called. This is done by providing matching pattern (more in AboutCocoon)

<map:pipeline>
  <map:match pattern="testbed2">
   <map:generate type="request"/>
      <map:transform type="ArcIms">
      <map:parameter name="server" value="http://gdr.ess.nrcan.gc.ca/servlet/com.esri.esrimap.Esrimap?ServiceName=GDR_E"/>
      </map:transform>
   <map:transform src="style/arcimsResponse.xslt"/>
   <map:serialize type="xml"/>
   </map:match>
   </map:pipeline>

This pipeline matches incoming url http://.../cocoon/iugs/testbed2 and return a plain xml.

line by line :

<map:match pattern="testbed2">
when this uri is matched
<map:generate type="request"/>
generate an xml document from the request information (the querystring)
<map:transform type="ArcIms">
      <map:parameter name="server" value="http://gdr.ess.nrcan.gc.ca/servlet/com.esri.esrimap.Esrimap?ServiceName=GDR_E"/>
      </map:transform>
use transform component to extract the ARCXML portion and send it to the server (address of the server passed in parameter)
<map:transform src="style/arcimsResponse.xslt"/>
grab the result and extract the ARCXML portion.
<map:serialize type="xml"/>
serialise the result.

arcimsResponse stylesheet

The ArcImsTransformator passes the request information with the ArcImsResponse, this is not the behavior I originally wanted, but thinking about it.. it might become handy. To extract just the response, I apply this stylesheet

<?xml version="1.0" encoding="ISO-8859-1"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
   <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
   <!-- this stylesheets extracts the xml:request parameter result -->
   <xsl:template match="/">
   <xsl:copy-of select="//ARCXML"/>
   </xsl:template>
</xsl:stylesheet>

Examples of results

Live Example

-- EricBoisvert - 12 Sep 2005

Examples which process the returned xml to produce an html page

-- EricBoisvert - 27 Oct 2005

Implementation for the Canada Testbed 2

CanadaTestBed2 is devoted to the implementation of the agreed on GeoSciML testbed.

-- EricBoisvert - 01 Nov 2005

Cocoon component

This jar iugsArcIms.jar (new version: -- EricBoisvert - 30 Sep 2005) must be saved in cocoon/WEB-INF/lib (or any other place Cocoon can reach a jar file), and Cocoon must be restarted so the component is recognised.

dealing with mixed case.

I wrote a utility component to deal with mixed-case parameter. It works like cocoon's param-request except that it can match any case. Furthermore, it can convert the value to uppercase or lowercase if necessary

example:
<map:parameter name="parameter-selector-test" value="{c-request-param:request||u}"/>
         <map:when test="GETCAPABILITIES">

if you add ||u at the end, the value of the param will be uppercased, ||l with turn it lowercase

so request like this

http://....?request=GetMap
http://....?ReQuest=GetMap
http://....?REQUEST=GetMap

are all matchable by

{c-request-param:request}

if you want to result uppercase, use

{c-request-param:request||u}

so value of the parameter will be converted to GETMAP

if you want the value unchanged, just use

{c-request-param:request}

installation :

save NgwdUtil.jar in cocoon/WEB-INF/Lib edit your cocoon.xconf file and add this

<component-instance class="org.apache.cocoon.components.modules.input.CaseRequestParam" logger="core.modules.input" name="c-request-param"/>
in the input-modules section

should look like

<input-modules>
    <component-instance class="org.apache.cocoon.components.modules.input.URLEncodeModule" logger="core.modules.input" name="url-encode">
      <encoding>UTF-8</encoding>
    </component-instance>
    <component-instance class="org.apache.cocoon.components.modules.input.URLDecodeModule" logger="core.modules.input" name="url-decode">
      <encoding>UTF-8</encoding>
    </component-instance>
    <component-instance class="org.apache.cocoon.components.modules.input.GlobalInputModule" logger="core.modules.input" name="global"/>
    <component-instance class="org.apache.cocoon.components.modules.input.RequestModule" logger="core.modules.input" name="request"/>
    <component-instance class="org.apache.cocoon.components.modules.input.BaseLinkModule" logger="core.modules.input" name="baselink"/>
    <component-instance class="org.apache.cocoon.components.modules.input.SessionModule" logger="core.modules.input" name="session"/>
    <component-instance class="org.apache.cocoon.components.modules.input.RequestParameterModule" logger="core.modules.input" name="request-param"/>
   <!-- your new input module here -->
    <component-instance class="org.apache.cocoon.components.modules.input.CaseRequestParam" logger="core.modules.input" name="c-request-param"/>
  <!-- more stuff... -->
  
 </input-modules>

Restart Tomcat

-- EricBoisvert - 31 Aug 2006
Topic attachments
I Attachment Action Size Date Who Comment
NgwdUtil.jarjar NgwdUtil.jar manage 2.5 K 31 Aug 2006 - 20:17 EricBoisvert c-param-request input module
iugs.jarjar iugs.jar manage 7.7 K 16 Sep 2005 - 21:27 EricBoisvert ArcIms cocoon component
iugsArcIms.jarjar iugsArcIms.jar manage 34.1 K 29 Aug 2006 - 21:41 EricBoisvert fix a bug in PostClient
Topic revision: r15 - 15 Oct 2010, UnknownUser
 

Current license: All material on this collaboration platform is licensed under a Creative Commons Attribution 3.0 Australia Licence (CC BY 3.0).