Posting to a service

At this point, Albums and Performers are displayed, but there's only the test data added by the ServiceRunner. Data can be added to the application by submitting the new data to services. I'll begin to demo this by implementing the create link on the album listing within the performer page, which calls the create operation within the album page, which will submit data to the createAlbum method within the AlbumService.

Checking Permissions

While we want everyone to be able to see the music library data, we only want certain people to be able to edit it. The permission to add an album is enforced at the service level as specified in the service declaration:
@RequiresPermission("modify")
public Album createAlbum(@ParamName("title") String title, @ParamName("performer") Performer performer);
However, it doesn't make much sense to show someone something they can't do, so the create link can be wrapped in a permission check, which only shows the create ability to those that can actually create:
Albums:
<xsl:if test="java:hasPermission($user,'doc.walkthru.AlbumService.modify')"> 
[<a href="album.page?op=create&performer_key={result/@key}&performer_type={result/@type}">create</a>]<p/>
</xsl:if>
Where not logged in, the anonymous user is not shown the create link: When a user with the doc.walkthru.AlbumService.modify permission is logged in, they see the create link: The checking of permissions within page render is good practice for atleast a couple of reasons:

Create the service form

The create operation within the album page can now be created. The method we want to call is:
@RequiresPermission("modify")
public Album createAlbum(@ParamName("title") String title, @ParamName("performer") Performer performer);
within the AlbumService. After requesting the performer from the service for display, the following doCreate template is created to submit data to the method:
<xsl:template match="doCreate"> 
  <form method="post" action="/svc/doc.walkthru.AlbumService">
    <input type="hidden" name="exec" value="createAlbum(String,Performer)"/>
    <input type="hidden" name="redirect" value="?op=edit&key=[result/key]"/>
    <input type="hidden" name="performer_type" value="{result/@type}"/>
    <input type="hidden" name="performer_key" value="{result/@key}"/>
    <table class="format">
    <tr>
      <td align="right">Performer:</td>
      <td>
        <a href="performer.page?key={result/@key}&type={result/@type}">
          <xsl:value-of select="result/name"/>
        </a>
      </td>
    </tr>
    <tr>
      <td align="right">Title:</td>
      <td><input type="text" name="title"/></td>
    </tr>
    <tr>
      <td align="right" colspan="2" style="padding-top: 10px">
        <input type="button" value="Cancel" onClick="window.history.go(-1)"/>
        <input class="default" type="submit" value="Create Album"/>
      </td>
    </tr>
    </table>
  </form> 
</xsl:template>
A form is created which calls /svc/doc.walkthru.AlbumService. This is the web accessible url for the AlbumService. All accessible services may be accessed at /svc/. When accessed with a browser, it presents all available services and methods within a javadoc-like interface. All access to services and methods within /svc/ goes through the security API, so that users can only access what they are intended to access. The form should have a post method.

Specify the method

The form has a hidden exec parameter, which specifies the method to invoke within the destination service (including parameter classes). The full name of the method is specified (including parameter classes). The @ParamName annotation values within the service are used to address the specific parameters.

Specify a redirect

The hidden redirect parameter specifies a url that is to be gone to after the service method is invoked. In this example:
    <input type="hidden" name="redirect" value="?op=edit&key=[result/key]"/>
the edit operation within the submitting page is called. If [result/<fieldName>] is specified, it is replaced with the value of the given field (as declared in the Java class) of method result object. This, of course, can only be used with methods which return something. Using it with a method returning void results in an error. In the example, the method returns an Album and the value of the key field will replace [result/key]. If the method were run and return a new album with a key of 123, the browser would be redirected to ?op=edit&123. If no redirect input were given, the service would redirect to the current page (via the referrer header). If you want to go to another page (or operation within the page), specify a redirect input.

Provide method parameters

The parameters are now specified. The names (or prefixes) of the form inputs must match with the @ParamName annotations as given in the service interface. These @ParamName values are used to identify which parameter is being addressed.

First, the performer parameter is specified, which is a complex object (ie not a simple type, such as String, int, Boolean, etc). There are three ways to submit complex objects:

  1. The easiest way, and the one shown here, is used when referring to an object which exists in the repository. Two parameters are created for the method parameter. The first is [paramName]_type which specifies the Class to be fetched from the repository. The second is [paramName]_key, which is the key of the object (of the given class) to retrieve. The object is then retrieved and used as the value for the parameter.

    The [paramName]_type is not strictly needed. If the method parameter class is an implementation type (ie a non-abstract, non-interface class) that type itself will be used, if no [paramName]_type is given. Note that classes must have a zero parameter constructor (default, public, private or protected) in order to be constructed.

    In this example, the type is specified as performer_type, which is either a GroupImpl or ArtistImpl. The performer parameter class is a Performer, which is an interface (not an implementation), so the performer_type parameter is required. The performer_key holds the key for the performer. When the form is submitted, the performer (be it a GroupImpl or an ArtistImpl) with the given key is fetched from the repository and used as the performer parameter.

  2. The second way of submitting a complex object parameter is to specify a type and fields. The type is specified in the same way, as [paramName]_type. The field values are then specified as [paramName]_[fieldName], where [fieldName] is the name of the field, as declared in the Java class. The string value of the field is converted to a Java object (be it to a String, Date, URL, int, etc) upon submission. When the form is submitted, an object of the given type is instantiated and the given fields are converted to the appropriate representation and assigned to the instance. The instance is then used as the parameter to the method.

    This way is good to use when the object is not in the repository and is small (ie does not contain many fields). It's quick and easy to implement.

  3. The final way to submit a complex parameter is to submit it as XML. The XML will be unmarshalled to the object and the object used as the parameter. It is critical that the XML be formatted appropriately and include the type attribute (for interfaces or abstract class parameters). For example, the complex object Widget could be added via:
    <form method="post" action="/svc/com.some.WidgetService">
      <input type="hidden" name="exec" value="addWidget(Widget)"/>
      <textarea style="display: none" name="widget">
        <widget type="com.some.WidgetImpl">
          <height>10</height>
          <width>20</width>
        </widget>
      </textarea>
      <input type="submit"/>
    </form>
    
    On submission, a Widget would be unmarshalled from the XML in the widget textarea and used as the value for the method parameter. XML submission can be quite useful for more complex situations. We'll see this again when JavaScript is used to call services.
The title for the album is specified simply by naming the input 'title'.

With the title and performer being specified, the submit of the form causes the parameters to be received and instantiated (as required for the method), then the service method to be invoked, the processing done and the result returned.

We now have an empty album done by a performer. The browser has been redirected to album.page?op=edit and the album is ready to have tracks added. In the next section, we'll use JavaScript to populate the album.