Personal tools
Document Actions

Service Developers' Toolkit Manual

Note: This is the print view with all the Reference Manual pages on one page. The paginated version is available here, if you prefer that.

This guide describes how developers can integrate new application services with GRIA's security and management capabilities.

1. Overview

Describes what the developer kit contains, who this guide is for, and what you need to know before you start.

This package provides documentation and code samples that will help you create web services that work with the GRIA services and clients. You should already have:

  • The GRIA 5.3 client software (required)
  • The GRIA 5.3 service provider management services (if you want to manage usage of the sample service using accounts or SLAs)

Experience of installing and configuring GRIA's basic application services (job and data) will also be useful, although these services are not required in order to follow this guide. A good understanding of programming in Java is required. Understanding of Axis, PBAC, and Hibernate is not necessary, but may be useful.

This developer kit contains:

A sample service

A sample web service that can be deployed into Tomcat. It provides operations to allow resources to be created and destroyed. The resources are very generic, but can be specialised into data stagers, jobs, accounts or whatever your service will be managing.

All resources created by it are protected using PBAC 2.

The service can be used on its own as a 'free' service, or configured using its web-based interface to require an account or SLA (Service Level Agreement) to be presented when creating resources.

A client plugin

The standard GRIA client does not know about the sample service or the resources it creates. This plugin can be dropped into the client's conf/plugins directory to let it use the new service. The plugin also extends the client's Swing user interface to provide menus allowing for use of the sample service.

A common jar containing the shared interfaces

The Java interfaces which define the interaction between the service and the client are contained in a single jar which is used by each of them.

This guide

This document provides an overview of how to use these samples and provides hints for developing the sample service into a full service.

2. Installation

Install and test the sample service.

Installing the service

To save you having to compile anything now, you can download the binary release of the sample service. The service is deployed in much the same way as the core GRIA services:

  1. Ensure that Tomcat 5 is installed. You can either deploy the sample webapp into the same Tomcat instance as your other services, or you can install it into a new copy on a different machine. Either upload sample.war using Tomcat's administrator web-interface, or copy it into the webapps directory and restart Tomcat. If you're not sure about this, consult the installation instructions supplied with your existing GRIA 5 services.

  2. Point your web-browser at http://localhost:8080/sample (or at that address on the machine running Tomcat, if different) and follow the on-screen instructions. The process of configuring the base system (keystore, database and endpoint address) is exactly the same as for configuring the GRIA services.

Once configuration is complete, the sample service will prompt you to configure it by specifying whether an account or SLA is required in order to use it, and which account and SLA services to trust if so. We recommend that you configure it as a "free" service for now; instructions on configuring trusted management services are provided later in this tutorial.

The sample service's web interface

The sample service's web interface

  • Click on the Grid Sample Service link and configure it in the same was as a GRIA job or data service: either make it free or add trusted Account and SLA services to the list using the web interface.

Now that your new service is installed and configured correctly, it's time to test it using the client.

Installing the client plugin

The standard GRIA 5 client doesn't know how to use the sample service. If you try dragging the WSDL link from the admin page to the client you'll find that the popup menu just has a generic "Invoke operation" menu item, which requires you to enter the XML for the SOAP request message manually. Note: if drag-and-drop doesn't do anything, right click on Services and choose Add a service and enter the URL of the WSDL manually. Also, ensure that you have a 5.3 or later version of the client.

Having to enter the request message's XML manually is awkward. We can create a better user interface using the client's plug-in system.

To install the client plugin and test it:

  1. Copy the plugin jar (gria-sample-client-5.3.jar) from the dev-kit package to the client's conf/plugins directory.
  2. Restart the client.

You can now use the popup menu over your sample service to create a new sample resource:

Creating a sample resource

Creating a sample resource using the client

3. Architecture

A brief overview of the architecture of a service.

Interfaces

The UML class diagram below shows the key interfaces used in the sample code. The ones with "Sample" in the name are from the dev-kit; the others are from the standard GRIA libraries.

Key interfaces

Key Java interfaces

The main interfaces are:

SampleService
All service SOAP operations which take no context. For example, operations for creating or discovering resources are defined here.
SampleResource
All service SOAP operations with take a sample resource as their context.

These two interfaces correspond to the two SOAP endpoints. Each endpoint has its own WSDL document.

The RemoveSampleService interface defines some extra helper methods on the SampleService interface, which do not correspond to SOAP operations. There is no corresponding interface for SampleResource as this plugin doesn't add any methods to this.

The interfaces from the core GRIA libraries are:

GridService
Operations to get the service's certificates and resources.
ManagedGridService
A service managed by an account or SLA service.
ReportAPI
A service which can deliver usage reports to an SLA service.
PolicyManagement
A resource whose access control rules can be managed.
WSResourceLifetime
A resource which can be destroyed, following the WS-ResourceLifetime specification.
ResourceMetadata
A resource with a user-set label and other metadata.
IdentifiableResource
A resource which can provide a MatchPattern allowing it to be identified.

All the interfaces are described fully in the on-line JavaDoc reference.

4. Adding a new operation

This tutorial shows how to extend the sample service with a new operation.

Adding a new operation

If you create some resources and then look at the service's administration page (the one where you configured the trusted management services), you will see that each resource is listed along with its PBAC process state. We will now add a SOAP operation called check on each resource, which says how many times it has been called since the resource was created.

There is a single Java interface file, called SampleResource.java, listing public operations on the sample resources. All resources shared by both the client and the service (interfaces and Java beans for any complex types) go in the sample-common project.

  1. Edit gria-devkit-5.3/sample/common/src/java/sample/SampleResource.java and add the new operation to the interface:
      @WebMethod
    int check() throws RemoteException;
  2. Build the jar file by running the command mvn install from the gria-devkit-5.3/sample/common directory.

    Recompiling requires installing Apache Maven version 2.0.9, which you can get from the Maven web-site.

Implementing the operation (client)

There is no need to write any code to implement the interface, as a suitable default implementation will be generated automatically. However, we do need to edit sample/client/src/java/sample/client/SampleClientPluginSwing.java to extend the client's menu with the new operation:

  1. In showPopupMenu(), add a new menu item for items of type SampleConversation:
    } else if (SampleResource.class.isAssignableFrom(itemClass)) {
    // The select object(s) are sample resources...
    menu.add(new Action(browser, items, "Check") {
    public void dispatch(EndpointReferenceType epr, Object sample) throws RemoteException {
    int count = ((SampleResource) sample).check();
    JOptionPane.showMessageDialog(browser, "Calls to check: " + count);
    }
    });
    }

Recompile the client by running the command mvn clean install in the sample/client directory.

To test it, copy the jar file from the sample/client/target directory to the client's conf/plugins directory as before and restart the client. You should find a new choice on the menu:

A new item on the client's menu

The new check operation appears on the menu

Of course, the operation won't actually work yet because it hasn't been added to the service. If you try, you will get this error message:

Operation "check" not in WSDL

Implementing the operation (service)

Before adding the operation itself, we'll need a place to store each resource's counter. We'll do this by editing the sample/service/src/java/sample/SampleResourceBean.java to add a new field in the usual Java way:

private int checkCounter = 0;

public Integer getCheckCounter() {
return checkCounter;
}

public void setCheckCounter(Integer checkCounter) {
if (checkCounter == null)
checkCounter = 0;
this.checkCounter = checkCounter;
}

Note that we use Integer instead of int for the getter and setter methods because we already have some resources in the database without a counter. Hibernate will use null for the counter value of these old resources, which we interpret as zero, allowing a smooth upgrade.

In addition, we need to tell hibernate to store this field in the database, which we do by adding an element to SampleResourceBean.hbm.xml (e.g. just under the label property):

<property name="label"/>
<property name="checkCounter"/>

We will now add the SOAP operation to the service:

  1. Edit sample/service/src/java/sample/impl/SampleResourceImpl.java to add the implementation of the operation:

    public int check() throws RemoteException {
    String conversationID = getConversationFromContext();

    Session session = factory.openSession();
    try {
    SampleResourceBean resource = getResource(session, conversationID);

    Transaction tx = session.beginTransaction();
    int newValue = resource.getCheckCounter() + 1;
    resource.setCheckCounter(newValue);
    tx.commit();

    return newValue;
    } finally {
    session.close();
    }
    }

    Here, we get the ID of the resource from the message context and use it to get the resource's bean from the database. Then we increment the counter, commit the new value to the database, and return the value to the client.

    Note that PBAC locks the resource automatically during the operation, so we don't need to worry about concurrent updates to the counter here.

  2. Recompile with the command mvn install in the top-level gria-devkit-5.3 directory.
  3. Undeploy the old war and deploy the new war/target/sample.war. You can do this using the usual Tomcat mechanism. You'll have to tell it where your configuration directory is again and then restart Tomcat.

Click on the List of services link at the top of the web page and you should see your new operation listed.

In the client, use the File -> Clear WSDL Cache menu item to force the client to reload the WSDL. Attempting to invoke the operation from the client will now display a different error message:

InvalidActionException: Unknown action 'check' for resource type http://SAMPLE/2006/SampleResourceType

We need to update the access control policy to state that the owner of a resource is able to invoke the check operation when the resource is in the ready state:

  1. Go to the Access control page (click on the link at the top of the web page).
  2. Select http://SAMPLE/2006/SampleResourceType and get the policy by clicking on Download Policy.
  3. Edit it, by adding the new operation to the ready state:
    <state name="ready">
    <operation name="check">
    <process-role name="owner"/>
    </operation>
  4. Using the web interface, undeploy the old policy and upload the new version.

Test the new policy using the client. You should now be able to see the counter:

The client displays the number of calls

The new policy is stored in the service's database, so it will still be there when you restart. However, if you undeploy it and then go back to the main screen, the original default will be redeployed. To make the new policy the default for your service, save it as war/src/main/webapp/WEB-INF/classes/sample-policy.xml, overwriting the old one, and rebuild the service war.

Automatic war redeployment

Restarting Tomcat and reconfiguring the service each time you change something can get annoying, so you may like to automate the process. You can use the Maven Cargo plug-in for this.

  1. In your Maven ~/.m2/settings.xml file, enter the login details for a Tomcat user with the manager role, e.g.:

    <settings>
    <profiles>
    <profile>
    <id>tomcat</id>
    <activation>
    <activeByDefault>true</activeByDefault>
    </activation>
    <properties>
    <tomcat-username>admin</tomcat-username>
    <tomcat-password>admin</tomcat-password>
    <tomcat-url>http://localhost:8080/manager</tomcat-url>
    </properties>
    </profile>
    <profiles>
    </settings>
  2. Copy <tomcat dir>/webapps/sample/WEB-INF/classes/service-config.xml into your configuration directory (e.g. as /etc/grid-sample/service-config.xml).
  3. Symlink your configuration directory to war/src/preconfigured.
  4. Now you can build and deploy a pre-configured war with:
    $ cd war
    $ mvn clean install -Predeploy,preconfigured

Note: Tomcat is very bad at freeing memory when undeploying webapps, so you should restart it from time to time, to avoid getting "PermGen space" errors.

5. Managing use of the service

How to restrict who can use the service, constrain how much they can use, and charge for use.

Managing use of the service

The simplest way to deploy the service is to let anyone in the world invoke the createSampleResource operation (this is the default) and not to require any account or SLA. For more advanced setups, the ability to create new resources can be constrained in three ways:

Use a more restrictive access policy

The access control settings for the service can be updated to restrict who can call the various SOAP operations.

Require a trade account

The service can be reconfigured to require a valid trade account when creating a new resource. This requires clients to get their account approved, which depends on the access control policy of the trade account service and on the manual checks performed when approving an account. Usage of the service is recorded and the client can be billed.

Require an SLA

The service can be reconfigured to require a valid SLA when creating a new resource. The SLA can be used to limit how many resources may be created, how much they can be used, and how much the client should be charged for the usage. The client will usually require a trade account in order to get an SLA.

These methods are described in the following sections.


Changing the service access policy

Using the web interface, go to Access control, select http://SAMPLE/2006/SampleServiceResourceType and then select service:sample.SampleService to change the access rules for the service. This can be used to restrict use of the service to a certain list of people, or only to people whose certificates are signed by a particular CA, etc.

When changing the policy using the web interface there are three process roles to which you can control access: the service-admin role controls who can read the Atom feed of events from the service, the managing-service role must be held only by the SLA service (if any) in order to get usage reports, and the world role grants access to all remaining operations. However, we can add new roles to provide more fine-grained access.

To control access to operations which don't take a context, download and edit SampleServiceResourceType (not SampleResourceType, which we edited before). Change the process role needed to invoke createSampleResource to resource-creator and redeploy the service policy (in the same way as you redeployed the resource policy above). The administration interface's drop-down role menu will now include the new process role, which you can use to control who can create resources.

Consult the PBAC administrator's guide for more information on controlling the service policy.


Requiring a trade account

The service can be reconfigured to require a valid trade account when creating a new resource. This requires clients to get their account approved, which depends on the access control policy of the trade account service and on the manual checks performed when approving an account. Usage of the service is recorded and the client can be billed.

Go to the administration page for the sample service and remove the Free entry from the Trusted management service table. Add the endpoint address of your GRIA trade account service in its place (note that the default is to add an SLA service, so you'll need to change SLAService to TradeAccountService in the form). Using the client, you should now find that you need an account to create new resources. You can either add an account to the client, or use a client management service. Consult the GRIA client documentation for details about setting this up.

By default, your account is billed only when creating a new resource, but billing can be added in other places too. For example, to charge the user for calling the new check operation, modify the new check function in sample/service/src/java/sample/impl/SampleResourceImpl.java as follows:

SampleResourceBean resource = getResource(session, conversationID);

Conversation managingConversation = getManagingConversation(resource);
if (managingConversation instanceof TradeAccountConversation) {
TradeAccountConversation account = (TradeAccountConversation) managingConversation;

account.bill(getCurrentUser().toID(),
new BigDecimal(1.0), "EUR",
conversationID,
"Invoked check()");
}


Transaction tx = session.beginTransaction();

Notice that we check whether the managing conversation is a trade account. If the resource was created when the service was free, or using an SLA, then this code will be skipped. The arguments to bill are the name of the user, the amount to charge, and some information for the statement (a message and the sample resource's ID).

The bill method does not check against the user's credit limit, since it is typically used after an expensive operation has already been performed. Use the checkCreditAvailable operation to decide whether to go ahead with something.

The bill method also does not check that the user has access to the account (although this is checked when the sample resource is created, of course). This is because clients with access to an account may delegate to other clients who do not have access (the original client has to pay for the new client's use in this case). Use checkUser if you wish to check whether the invoking client has permission to use the account directly.

After recompiling and deploying the new war, and calling check with the client on a resource managed using a trade account, you should see the usage recorded on your bill:

Charging for status checks

Charging for status checks


Requiring an SLA

Managing the sample resources using a trade account, as described above, provides only limited control of your clients' usage. For maximum flexibility, the service can be managed using an SLA service instead.

To do this, go to the administration page for the sample service and remove any existing entries from the Trusted management service table. Add the endpoint address of your GRIA 5 SLA service in its place. You may be prompted to set up an access control rule to grant the SLA service access. Using the client, you should now find that you need an SLA to use the service. You can either add an SLA to the client directly, or use a client management service. Consult the GRIA client documentation for details about setting this up.

When a client tries to create a resource, the sample service calls startActivity on the SLA provided to decide whether the operation should be permitted (in the checkCreationOK method in sample/service/src/java/sample/impl/SampleServiceImpl.java):

sla.startActivity(epr,
new Constraint[]{
// SLA must allow a new activity
new Constraint(Metric.CURRENT_ACTIVITIES, UsageType.INSTANTANEOUS, Bound.EQ, 1, now),
// SLA must allow a new sample resource
new Constraint(METRIC_SAMPLE_RESOURCES, UsageType.INSTANTANEOUS, Bound.EQ, 1, now),
},
new UsageReport[] {
new UsageReport(epr, Metric.CURRENT_ACTIVITIES, UsageType.INSTANTANEOUS, now, null, 1),
new UsageReport(epr, METRIC_SAMPLE_RESOURCES, UsageType.INSTANTANEOUS, now, null, 1)
});

This says that the new sample resource will require exactly one 'activity' and exactly one 'sample resource'. Although a sample resource is a type of activity, the SLA service doesn't understand this relationship, so we request both. This allows the service administrator to write SLAs that constrain on either.

We also provide a set of usage reports telling the service what we are actually using now. In our case, the current usage (one activity and one sample resource) is the same as the levels set in the constraints. In other cases it may be different. For example, a job service may require "more than ten CPU seconds" available on the SLA, even though none have been used when the job is created.

If the startActivity operation succeeds then we go ahead with any initialisation of the resource.

When a sample resource is destroyed we send similar reports indicating that usage of activities and sample resources has dropped to zero (again, in the context of this single resource) in SampleResourceImpl.destroy:

service.pullPoint.addInstantaneousUsageReport(epr, SampleServiceImpl.METRIC_SAMPLE_RESOURCES, 0.0);
service.pullPoint.addInstantaneousUsageReport(epr, Metric.CURRENT_ACTIVITIES, 0.0);

Note: in fact, only CURRENT_ACTIVITIES needs to be set in this case. The SLA service knows that when this falls to zero the activity no longer exists, and all other usage is also zero.

The messages aren't sent immediately, but are queued. After a couple of minutes, the SLA service will fetch all the messages at once. Note that you must give the SLA service the managing-service role on the service:sample.SampleService service object in order for it to be able to get these messages.

You should configure your service to create a rule in this group for the service provider automatically. This can be done by modifying SampleServiceImpl's ensurePoliciesDeployed() method as follows:

public void ensurePoliciesDeployed() throws GridFailureException {
/* This is called by the web administration code when viewing the main admin page.
* Deploy any PBAC policies and resources we need.
*/
// This policy protects operations in the SampleService interface
pbacUtils.ensureDeployed(SampleService.SAMPLE_SERVICE_RESOURCE_TYPE, "sample-service-policy.xml");

groupUtils.ensureGroupDeployed(TrustedManagementServices.MANAGEMENT_SERVICES_GROUP, new PolicyRule[] {
new PolicyRule(new MatchPattern("special:this-service"), PDP.GROUP_MEMBER_ROLE)});

// This policy protects operations in the Sample interface
pbacUtils.ensureDeployed(SampleResource.SAMPLE_RESOURCE_TYPE, "sample-policy.xml");

// This object represents the service itself. It will be the only resource of this type.
pbacUtils.ensureServiceResource(this, new PolicyRule[] {
new PolicyRule(MatchPattern.createAnyonePattern(), "world"),
new PolicyRule(new MatchPattern(TrustedManagementServices.MANAGEMENT_SERVICES_GROUP),
TrustedManagementServices.MANAGEMENT_ROLE)
});
}

The two metrics used above are very simple as they only take the values of zero or one. The SLA service aggregates them across all activities within the user's SLA to work out the total bill. It may also invoke the destroy operation on sample resources itself if the limits of the SLA are exceeded and the service provider is running out of capacity. The total number of sample resources permitted across all SLAs may be set by adding a METRIC_SAMPLE_RESOURCES metric to the SLA service's capacity XML document. Pricing terms and limits for individual SLAs may be set by creating new SLA templates in the SLA service. Consult the SLA service's documentation for further information.

Often, more complex metrics are needed, and usage rates change during the lifetime of a resource. For example, each GRIA data stager has a metric recording the number of bytes stored within the stager (which changes on each upload or delete operation) and another metric recording the total number of bytes transferred. In this case, additional rate reports are added in the appropriate operations. For example, here is an extract from the data service's save operation:

pullPoint.addCumulativeUsageReport(getEPR(stager), Metric.DATA_TRANSFER, (double) fileBytes);
pullPoint.addInstantaneousUsageReport(getEPR(stager), Metric.DISC, (double) fileBytes);

The difference between these two is that DATA_TRANSFER is charged at the point when it happens (addCumulativeUsageReport increases the current total), whereas DISC sets a new level of usage (addInstantaneousUsageReport); the longer the data is stored, the more the client pays.

6. Other modifications

There are many other changes to the service that you can make. Here are some ideas, along with hints to get you started.
Create additional PBAC states

So far, our resource only has a ready state (besides the special UNINITIALISED-STATE and DESTROYED-STATE states). Create a locked state in the policy in which it is possible to unlock the resource and check its status, but in which the resource cannot be destroyed. You'll need transitions between the states (see the existing examples in the file).

You could then add lock and unlock SOAP operations (as above), which send the signals that cause the transitions; see the destroy operation for an example. However, an easier method is to make your resource (SampleResource) implement the Signallable interface. No other code changes are needed in this case, for either the client plug-in or for the service. You will, however, need to add signal:lock, signal:unlock and getAvailableSignals operations to the policy (sample-policy.xml).

Create additional process roles

To create a new role, simply use a different string in place of owner in the policy for some operation. Create a monitor role that can check the resource's counter but not do anything else. As the service administrator, you can control who gets the new role by clicking on one of the sample resources in the web interface and adding a new rule. See the PBAC administrator's guide for more information.

Add access control operations

Edit sample-policy.xml to grant the owner permission to use the actions getPolicyRules:monitor, addPolicyRule:monitor, and removePolicyRule:monitor. After redeploying the new policy using the web interface, you'll be able to manage access to your new monitor process role using the client.

Note: These operations are unusual in that the access control decision depends on the arguments, not just the method name. For example, we could allow a monitor to manage rules only for other monitors, but allow an owner to manage both types of rule. Therefore, these methods (implemented in the GridServiceLite super-class) have an @AccessControl(disableCheck = true) annotation and do their own access control checks using PDP.check(), appending the process rule to the end of the action (:monitor, etc).

Give the service administrator access to all resources using a group

After createSampleResource's addPolicyRule call to give the owner access to the resource, add another rule giving the service administrator the service-admin process role, and update the policy to let the admin perform additional operations. Including the admin's details in every resource makes updating them all difficult if new admins need to be added later, so just add a match rule requiring membership of the service-admins. See the documentation on the GroupUtils class in PBAC for details.

Add a new service

You can supply a collection of services in the same webapp, so that they share the configuration and database. To do this, create a new interface and implementation (corresponding to SampleService and SampleServiceImpl), add the mapping between them to src/webapp/WEB-INF/classes/implementationfactory.properties, and list the new interface in src/webapp/WEB-INF/server-config.wsdd. You should also copy sample-service-policy.xml for your new service, and give its initial version number ("1") in WEB-INF/classes/pbacpolicyversions.properties.

On the web-site you will find extensive JavaDocs for the sample service and the libraries it uses.

If you have any questions, please email GRIA Support.

7. Upgrading from 5.2

This guide is for people who developed services using the 5.2 developer kit and who wish to update their services to use the 5.3 framework.

GRIA 5.2 to 5.3 developer kit migration guide

This guide is for people who developed services using the 5.2 developer kit and who wish to update their services to use the 5.3 framework.

Note that GRIA is compatible at the SOAP layer between these versions, so there is no need to upgrade just to interoperate with the newer services. However, upgrading may simplify your code or give access to new features.

If your service was developed using the 5.1 developer kit, then please first follow the 5.1 to 5.2 migration guide first.

Update to new client API

REQUIRED

The 5.3 GRIA client API has changed considerably. Whenever your service acts as a client to another service (for example, when contacting an SLA service) it will need to use the new API. See the 5.3 client's release notes for information about these changes.

Web service annotations

REQUIRED

The @WebServiceWSDL annotation has been replaced by the very similar @WebService annotation. This is for consistency with JAX-WS.

PBAC interface

REQUIRED

The PBAC interface has been updated slightly to use PolicyRule instead of MatchRule objects. The service was already using the newer types internally, so this should simplify the code.

Your implementationfactory.properties file must be updated to use the new method for instantiating the PDP. See the example provided.

Client plugins

REQUIRED

The client plugin system has been split into two parts: SampleClientPlugin (for low-level SOAP code) and SampleClientPluginSwing (for extending the client GUI). You will need an extra .Provider file for this. The interface for the Swing plugin has also changed; see the client documentation for details.

The way additional helper methods are defined has also changed. See HelperProxyFactory for details of the new system.

Security handlers

REQUIRED

PBAC now includes the new resource state in each SOAP response. Add ResourceState to the list of elements to sign (see the example server-config.wsdd).

Also, set the additionalSignedHeaders parameter; this is no longer hard coded (again, see the example).

Finally, add this to your web.xml to ensure the SSL handlers are configured:
    <listener-class>uk.ac.soton.itinnovation.grid.service.utils.HTTPSSocketListener</listener-class>

New SOAP operations

OPTIONAL

The new getServiceMatchPattern operation should be added to the PBAC policies for SampleService and SampleResource. This is for the new IdentifiableResource interface.

Powered by Plone CMS, the Open Source Content Management System