Creating Service Pages

by Andrew Taylor <andy@benow.ca>
Static pages are nice, but dynamic pages are nicer. In this tutorial, dynamic page creation using services will be demonstrated. Services provide an easy way to access processing and data. The BeNOW Web Framework facilitates the requesting of data from services and the display of the resulting data. After this tutorial, you should be able to create rich dynamic pages.
Contents
  1. Creating Services
  2. Calling a Service and Formatting Results
  3. Calling a Service if User Has Permission
  4. Posting Data to a Service
  5. Calling a Service from Javascript
  6. Developing Service Pages using the Web Development Page
Creating Services
To create services, please see the Creating Services tutorial. We will use the TutorialService in the subsequent steps. Ensure that services are locked down via the service security mechanisms. Once the service is showing up on the service listing (localhost:8080) it is ready to be used.
Calling a Service and Formatting Results
Calling a service from a page is a matter of using the svc tag. Open up html/tutorial.page and find the template that matches service:
<xsl:template match="service">
  <svc op="displayPeople" sig="test.org.benow.service.TutorialSecureService.getPeople()"/>
</xsl:template>
This will call getPeople() from test.org.benow.service.TutorialSecureService and use the template displayPeople to display the results. The results are returned as XML for input to the template that matches the value given for the op attribute:
<xsl:template match="displayPeople">
  1) The full result from <code>
     <a href="/svc/test.org.benow.service.TutorialSecureService">test.org.benow.service.TutorialSecureService.getPeople()</a></code>
     is wrapped by <code>displayPeople</code> and is:<br/>
     <textarea rows="20" cols="120"><xsl:copy-of select="."/></textarea>
     <br/>
  2) Without the page data, the <code>getPeople()</code> method <code>result</code> is<br/> 
     <textarea rows="20" cols="120"><xsl:copy-of select="result"/></textarea><p/>
  3) The following people are known:
     <div class="result">
     <xsl:for-each select="result/item">
       <xsl:sort select="last-name"/>
       <xsl:sort select="first-name"/>
       <xsl:value-of select="last-name"/>, <xsl:value-of select="first-name"/> 
       from <xsl:value-of select="address"/>, <xsl:value-of select="city"/> 
        <xsl:value-of select="postal"/><br/>
     </xsl:for-each>
     </div>
</xsl:template>
The page can be viewed at localhost:8080/tutorial.page. The XML, returned from the call: In this case, getPeople() returns a list of Persons. The XML representation of a list is <items><item>...</items>. The XML representation of the returned objects is generated by the XML api, and annotations can be used to tailor XML (see Using the XML API tutorial). The displayPeople template displays the returned XML and formats and displays the name and address of the known people.

So, the XML produced by the method is reformatted to the desired output. This separation of processing and formatting is one of the strenghts of XSL, as the coders can focus on processing well (good java code) and presenting well (good html code)... the XSL merely reformats. The disadvantage is that things can get a bit messy. There's often quite a bit of XML produced by service methods and used as input to the matching templates. The XML produced by services can be seen in the XML browser by choosing the XML link in the top left of the page or hit the Alt-X shortcut (hold down alt, shift and x).

Calling a Service if User Has Permission
Through the use of java functions within pages, it is possible to selectively supress html production, which can be useful with service calls. Each page has a <xsl:param name="user"/> which is a java User object, representing the current user requesting the page (or the anonymous user, if the visitor has not signed on). An example of this is the template matching permissionedService in html/tutorial.page:
<xsl:template match="permissionedService">
  Calling <code>getPeopleOfHeight(Integer)</code> if <code><xsl:value-of select="java:getName($user)"/></code>
  has permission: <code>test.org.benow.service.TutorialService.invoke</code>
  <xsl:choose>
    <xsl:when test="java:hasPermission($user,'test.org.benow.service.TutorialService.invoke')">
      <svc op="displayPermissionedPeople" 
              sig="test.org.benow.service.TutorialSecureService.getPeopleOfHeight(Integer)">
        <heightInCM>180</heightInCM>
      </svc>
    </xsl:when>
    <xsl:otherwise>
      No <i>test.org.benow.service.TutorialService.invoke</i> permission 
      for <xsl:value-of select="java:getName($user)"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>
This example can be run by hitting http://localhost:8080/tutorial.page?op=permissionedService.

If the user has the test.org.benow.service.TutorialService.invoke permission then the services will be called to get known people over 180cm. The full signature of the method is used test.org.benow.service.TutorialSecureService.getPeopleOfHeight(Integer) with the parameter being passed in as named, heightInCM. The service invoker will take care of converting the given parameter string ('180') into the appropriate representation (an integer), and the service method will be invoked, returning the results. If the user does not have sufficient permission, a message will be displayed, instead of the service being invoked.

Using java:hasPermission($user,perm) can be useful to not display service results or links to service templates if the user does not have permission. In this way, the unpermissioned user is not even aware that extra functionality exists, which lessens the ability to discover things no of concern, and keeps the returned html cleaner. If a service is called to which the user has no permission, the service api will assert the permission and an error will be returned, which is ugly outside of debugging. Be reassured that if they do find out the existence of a service and method, they won't be able to invoke it if they do not have permission.

Posting Data to a Service
Data may also be posted to services. This is useful when adding data to data application, among other situations. An example of posting to a service can be seen within the html/tutorial.page in the template matching postingToServices and can be run by hitting tutorial.page?op=postingToServices. It contains:
<form action="/svc/test.org.benow.service.TutorialSecureService" method="post">
    <input name="exec" type="hidden" value="addPerson(String,String,String,String,String,Integer)"/>
    <input name="redirect" type="hidden" value="?op=service"/>
    First Name: <input name="firstName"/><br/>
    Last Name: <input name="lastName"/><br/>
    Address: <input name="address"/><br/>
    City: <input name="city"/><br/>
    Postal: <input name="postal"/><br/>
    Height in CM: <input name="heightInCM"/><br/>
    <input class="buttonDefault" type="submit" value="Submit New Person"/>
</form>
The action of the form points to the service url, /svc/test.org.benow.service.TutorialSecureService and has a method of post (get will also work, but is not great for large posts). The hidden exec input specifies the method to invoke, addPerson(String,String,String,String,String,Integer). The hidden redirect parameter indicates a url to go to after the invocation. In this case we want to show the results via the service operation of this page (it parses the referrer url to know what the calling page was). The parameters for the method are form inputs with names matching the parameters in the method: firstName, lastName, address, city, postal and heightInCM. The submit button simply submits the form.

When the service url is hit in this manner, the method is determined, the arguments are extracted and converted from string (when appropriate) and the method is invoked. After the method is invoked, the redirect url (if given) is redirected to. If no redirect parameter is given, the referring page is displayed again.

After values are supplied and the form posted, a new Person is added and the list of people is displayed via the template which matches service. You should see your added person at the bottom of the page.

In more advanced use cases, the parameters for complex objects can be specified as XML. The service handler will parse the XML into java objects before invoking the method. Files may also be posted to services methods with java.lang.File parameters using a multi-part form request. They will be received and stored temporarily and the method invoked.

Form submission provides an easy way to get stuff into the web application.

Calling a Service from Javascript
Javascript calling of services is another feature of the Web API. It allows for easy and natural javascript calls to services, without the heavy(ish) lifting of AJAX. A demo of javacript calling can be found in the template matching javascriptService in html/tutorial.page, and may be run by hitting /tutorial.page?op=javascriptService (localhost:8080). The page portion is:
  <script src="/js/org.benow.util.Request.js">;</script>
  <script src="/js/org.benow.util.DOM.js">;</script>
  <script src="/svc/test.org.benow.service.TutorialSecureService.js">;</script>
  Minimum height of people to fetch
  <input id="height" size="4" type="text" value="180"/><br/>
  <input type="button" value="Fetch People By Height" 
     onClick="var height=document.getElementById('height').value; getPeopleOfHeight(height)"/>
  <input type="button" value="Fetch All People" onClick="getPeople()"/>
  <div id="response"/>
This includes the /js/org.benow.util.Request.js, which is required for service method invocation. /js/org.benow.util.DOM.js is included for the getChildText(element,string) method used in result display, and contains many helper methods for working with XML. The service javascript /svc/test.org.benow.service.TutorialSecureService.js is also included. By pointing a browser to /svc/test.org.benow.service.TutorialSecureService.js (localhost:8080) you can see the javascript which has been created. This javascript creates the AJAX request and response handling, and exposes the service methods as simple javascript functions, which can be seen in the above example. When the user clicks on the 'Fetch People By Height' button the height is fetched and getPeopleOfHeight(int) is called. The method signature is exactly the same as in the java service definition. Calling it is as simple as calling the method from java.

The javascript functions which receive and display the results are found in html/tutorial.js, which is automatically included at page delivery time.

function onGetPeople(url,http_request) {
	showPeople("getPeople()",http_request);
}

function onGetPeopleOfHeight(url,http_request) {
	showPeople("getPeopleOfHeight(int)",http_request);
}

function showPeople(method,httpRequest) {
	var result;
	alert('got xml from: '+method+'\n'+httpRequest.responseText);
	var items=responseXML.getElementsByTagName('person');
	if (items.length==0) 
		result='No people found.';
	else {
		result=items.length+' '+(items.length>1?'people':'person')+' found.<br/>';
		for (var i=0;i<items.length;i++) {
			var currE=items[i];
			var lastName=getChildText(currE,'last-name');
			var firstName=getChildText(currE,'first-name');
			var height=getChildText(currE,'height-inc-ms');
			result+=lastName+', '+firstName+' '+height+'cm<br/>';
		}
	}
			
	var respDiv=document.getElementById('response');
	respDiv.innerHTML=result;
}
When a request is made to the method methodName in the service and results are expected, a function onMethodName(url,http_request) should be defined (ie function onGetPeople(url,http_request) to handle the getPeople() response). The url parameter is the url of the service that was called, and the http_request is the AJAX httpRequest object. The XML of the response is found as http_request.responseXML and can be used for display or processing of service method results. This can be seen in the above example as the for loop, where all <person> elements are retrieved and the first name, last name and height combined and displayed as the response.

The javascript interface to the services is quite refined. It is fast and very easy to use. The lessened overhead of implementing javascript ajax calls means that more time can be put into making things look and work well. It's less tedious, and easier to maintain than standard AJAX.

Developing Service Pages using the Web Development Page
The task of creating a page which calls services is made easier by the Web Development Page (localhost:8080). When editing a page, there is a generate feature which can create the template for calling services in various ways (ie via the <svc> tag, by form submission and via javascript). It is still early days for this page, but it might make your life a bit easier.
The BeNOW tutorials are a work in progress. If you have any comments or suggestions, please email <andy@benow.ca>.