Calling services from a page

You now know how to create a page, but the pages so far have been simple, and far from dynamic. The services for the music library have also been created. We just now need to request data from the services and display that data in the page. In such a way, the page content becomes dynamic, and the application starts to emerge.

The <svc> Tag

As mentioned in the page creation step, there is a post processing step in the assembly of the HTML (step 5). In this step, the HTML assembled to this point is scanned for tags which mean something to the web framework. One of those tags is the <svc> tag.

When an <svc> tag is encountered in the intermediary HTML, the tag is parsed, and the specified service method is called, with the result returning to a specified template.

Lets first create the walkthru/index.page, which will show an overview of albums by artists, and within that page, make a call to AlbumService.getAlbums(): Take a look at the svc tag within the template matching index:

<svc sig="doc.walkthru.AlbumService.getAlbums()" op="dumpAlbums"/>
The sig attribute is the full signature of the method to call. It is prefixed by the full class name of the service and the full name of the method (including parameters, if any). In this case there are none, but if we were to call createAlbum, for example, the signature would be doc.walkthru.AlbumService.createAlbum(String,Performer). If the method requires parameters, the parameters can be specified as elements within the svc tag. For example:
<svc op="dumpAlbum" sig="doc.walkthru.AlbumService.getAlbumByKey(Object)">
  <key>1234</key>
</svc>
Calls a getAlbumByKey within the AlbumService with one parameter (named 'key' as given in the @ParamName annotation of the service), with a value of 1234.

The op attribute of the svc tag specifies the template to be used to transform the results of the method call. The result object of the method, if any, is marshalled to XML and becomes the <result> element within the new input XML. The XML marshalling is performed automatically by the XML API and requires no intervention, but the objects may be tailored for XML display via annotations. See the Using the XML API tutorial for details.

Handling the Result

So, the call will be made to the service, and the service will return the result, which is marshalled to XML which becomes the input to the specified template. In the above page, I've specified that the result is to be handled by the dumpAlbums template, which does nothing but display the input XML: In this case the XML is dumped within a textarea by using the <xsl:copy-of> function. The XML can also be viewed, as before, by clicking on the XML link and choosing the appropriate service call from the dropdown: The full input XML for the dumpAlbums template is:
<dumpAlbums uri="/walkthru/index.page">
  <params>
    <pagePath>walkthru/index.page</pagePath>
  </params>
  <url>http://localhost:8420/walkthru/index.page</url>
  <headers>
    <Host>localhost:8420</Host>
    <User-Agent>Mozilla/5.0 (X11; U; Linux x86_64; en-US; rv:1.9.1.7pre) Gecko/20091210 Ubuntu/9.10 (karmic) Shiretoko/3.5.7pre</User-Agent>
    <Accept>text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</Accept>
    <Accept-Language>en-us,en;q=0.5</Accept-Language>
    <Accept-Encoding>gzip,deflate</Accept-Encoding>
    <Accept-Charset>ISO-8859-1,utf-8;q=0.7,*;q=0.7</Accept-Charset>
    <Keep-Alive>300</Keep-Alive>
    <Connection>keep-alive</Connection>
    <Referer>http://localhost:8420/admin/dev/index.page?op=view&type=page&item=walkthru/index.page</Referer>
    <Cookie>userInfo=admin  92668751; userInfo=admin  92668751; wdk_pref_cookie_0=eJyNkctKQzEQhl/F7uXQikIrzbLo5mjBLhSRME3GdmiSCZlp63l7o1WoR7wsB/7L9zOuoMekBEFsBpE9F2/w+ua+PZOL29l4f3clk+nAccycqvCRvAm8ovTUbAVLgojGO43TwY6ElgELZhZSLoRiLkfn48mpj9Zj5JpyVNZzQ86BHChxasA5lBpGgbRrZM1F3VYT7Gj1LsAEtcib55qDv1khqOKL9vQuUIVIqHXrJvDBaaydtfPFw2Lezqw9+crq2S1BKurnkJ8rN9gtGYr/H+1W128dH6fnCPSd5NjhSXKArnGh/oqcGQ2HfdRDBsasnf2jrrfqFRPUx5M.; JSESSIONID=1rqtxca19c9ez</Cookie>
    <Cache-Control>max-age=0</Cache-Control>
  </headers>
  <session>
    <headers>{}</headers>
    <addCookies>[javax.servlet.http.Cookie@13d3f62]</addCookies>
    <pagexml>[org.benow.web.path.page.XMLChunk@9f9761]</pagexml>
    <xslParams>{}</xslParams>
    <params>pagePath{walkthru/index.page} </params>
    <user key="1" type="org.benow.repository.security.UserImpl">
      <name>admin</name>
      <description><![CDATA[Administrative user.]]></description>
      <validated>true</validated>
      <validation-code>1260832613816</validation-code>
      <needs-password>false</needs-password>
      <confirmed>true</confirmed>
    </user>
    <url>http://localhost:8420/walkthru/index.page</url>
    <userStamp>Thu Dec 17 18:55:04 MST 2009</userStamp>
  </session>
  <method signature="getAlbums()">
    <name><![CDATA[getAlbums]]></name>
    <in-class>doc.walkthru.AlbumService</in-class>
    <return-argument is-simple="false" required="true" type="org.benow.java.spec.argument.Argument" unary="false">
      <name type="java.util.ArrayList">
        <item>return</item>
      </name>
      <type>java.util.List</type>
      <is-collection>true</is-collection>
      <contained-class>doc.walkthru.Album</contained-class>
    </return-argument>
  </method>
  <result type="org.benow.repository.util.PersistentArrayList">
    <items end-index="1" from="0" super-count="2" type="org.benow.repository.util.PersistentArrayList">
      <size>2</size>
      <item key="1" type="doc.walkthru.AlbumImpl">
        <performer key="1" type="doc.walkthru.ArtistImpl">
          <name>Aphex Twin</name>
        </performer>
        <title>Selected Ambient Works 85-92</title>
      </item>
      <item key="2" type="doc.walkthru.AlbumImpl">
        <performer key="1" type="doc.walkthru.GroupImpl">
          <name>DATacide</name>
        </performer>
        <title>Flowerhead</title>
      </item>
    </items>
  </result>
</dumpAlbums>
        
The <dumpAlbums> element causes the template matching dumpElement to be called. Within the the input XML is dumpAlbums/result which is the XML representation of the object returned the service method, in this case, a list of albums (as previously populated in the Calling services from Java section):
<result type="org.benow.repository.util.PersistentArrayList">
  <items end-index="1" from="0" super-count="2" type="org.benow.repository.util.PersistentArrayList">
    <size>2</size>
    <item key="1" type="doc.walkthru.AlbumImpl">
      <performer key="1" type="doc.walkthru.ArtistImpl">
        <name>Aphex Twin</name>
      </performer>
      <title>Selected Ambient Works 85-92</title>
    </item>
    <item key="2" type="doc.walkthru.AlbumImpl">
      <performer key="1" type="doc.walkthru.GroupImpl">
        <name>DATacide</name>
      </performer>
      <title>Flowerhead</title>
    </item>
  </items>
</result>
Each result/item is an album, with a performer and a title. So, now we have a result from the service, and realize how it is represented in the input, we can use XSL to format it: For each album (result/item), ordered by performer name, the performer name is output in bold and then the title: That's it. The XML representation of the object returned by the service method is transformed into nice HTML.

Linking the Pages

We can now make this into a real web app by linking things together: So, the performer name links to the performer page using the performer key and type and the album links to the album page using the album key, but, there are no performer and album pages. Clicking on the links would typically cause a 404, document not found, but we now run into another feature of the web framework. If the current user is a web developer (ie has the WebDevelopmentService.invoke permission), or is the admin (who automatically has all permissions), then the following helper page is shown: Hitting 'Yes' causes the referenced page to be automatically created (as it would have been if the development page were used directly). If the user were not a web developer, then they would have received a 404. This auto-create facility is another tool to speed development.

Similarly, if you were to define a call to a service method which did not exist, it would create it. I called this from album.page

<svc op="dumpAlbum" sig="doc.walkthru.AlbumService.getAlbumByKey(Object)">
  <key><xsl:value-of select="@key"/></key>
</svc>
without AlbumService.getAlbumByKey(Object) having been declared, and the following page is presented: When 'Yes' is clicked, it will create the method. If it determines that the service (AlbumService in this case) is an interface, it will create the method in the interface and the implementation (AlbumServiceImpl), if it is found. After create, the following now exists and can be implemented. In AlbumService:
  public void getAlbumByKey(
    @ParamName("pagePath")
    Object pagePath);
and in AlbumServiceImpl:
public void getAlbumByKey(
  Object pagePath) {
 throw new org.benow.util.NotImplementedException("getAlbumByKey(Object)");
} 
The code needs tuning and implementation, but it speeds development.

So, with the pages linked up, it's now more of a web application. Albums and Performers are presented, using calls to the services, which make requests of the repository. It's pretty, but not very useful without the ability to add information. I'll now add information via posting to a service.