Banner
Title: GT4 Introductory Practical
Subtitle: How to Build a Java Service Using GT4
Tutors: Rachana Ananthakrishnan, Charles Bacon, Lisa Childers
Authors: Globus Alliance Staff
Table of Contents

GT4 Introductory Practical:
How to build a Java service using GT4

Document Conventions

  • Throughout this tutorial, narrative material appears in green. In the live version of the tutorial such information is delivered by an instructor.
  • For the hands-on exercises:
    • Instructions for the hands-on exercises are written in black
    • The first several exercises requires that you issue commands from two different command windows running on your machine:
    • One window will be used to run Globus Toolkit software and will be referred to as the GT window in these notes. Commands typed in the GT window will be bounded by a box of this color.

      The second window will be used to modify and execute student code and will be referred to as the Exercise window. Exercise commands will be bounded by a box of this color.

      Later in the tutorial a third command window will be required.

      Red command text can be cut and pasted directly into your command window
      Black command text indicates that intelligent replacement is required

      You might consider coloring the backgrounds of your Terminal/Command/Xterm windows to match the colors used in the tutorial. Though not necessary, you may find this helpful in identifying windows.

Brief Introduction to GT4

The Globus Toolkit (GT) is a collection of development tools, libraries, clients and services for building Grid infrastructure. The figure above shows a high-level view of the major components in the current release (version 4). GT4 is the result of over a decade of development. The newer components in the toolkit reflect a focus on service-oriented architectures based on Web Services (WS) standards. Because of its pervasiveness as an implementation language for Web services, many of the newer GT components are written in Java. This tutorial will show how to build a custom Java web service using GT4.

More introductory information on GT4

Hands-On Exercise Overview

This tutorial is organized around a series of exercises in which increasing functionality is added to a skeletal service. The exercises are designed to demonstrate common Grid-based interactions and patterns using GT4. The demonstration code has trivial functionality in order to maintain focus on the mechanics of service writing. Each exercise includes:

  • A discussion of the concepts behind the exercise
  • A discussion of implementation details
  • Hands-on exercise steps

In order to avoid wasting time on syntactic errors, each exercise consists of uncommenting code fragments. A word of warning: it is possible to complete all the exercises by blindly uncommenting and commenting code. However, in order to get the most from this tutorial you should take time to read and understand your edits.

Exercise 1: Deploy a Service

Concepts

The sample code we'll be modifying during the course of this tutorial is a simple web service written in Java. The service is called StickyNote; it stores and returns a text message. The purpose of this first exercise is to build and deploy StickyNote using GT4.

More information about deploying StickyNote

Implementation Details

As part of this exercise we'll install the GT4 binary, and do a test build of the exercise code. We'll then verify that our environment is set up properly by running a couple of clients.

Further reading on development tools needed for building GT4-based services

Exercises

If you haven't yet done so, now is the time to:

Open a command window for running the Globus container (the GT window)

Open a second window for building, modify and executing demonstration code (the Exercise window)

Ok! Time to begin!

1.1 Untar the gt install tarball in the GT window:

Install the Globus software as follows:
$ mkdir /gt4/$USER/
$ mkdir /gt4/$USER/intro/
$ cd /gt4/$USER/intro/
$ wget http://www.gs.unina.it/~gt4/introductory/globus-4.0.2-java-norft.tar.gz
$ tar xzf globus-4.0.2-java-norft.tar.gz

1.2 Set environment variables in the GT window:

$ cd globus-4.0.2-java-norft
$ export GLOBUS_LOCATION=$PWD
$ echo $GLOBUS_LOCATION
/gt4/childers/intro/globus-4.0.2-java-norft

1.3 Untar the tutorial tarball in the Exercise window:

Install the exercise software as follows:
$ cd /gt4/$USER/intro/
$ wget http://www.gs.unina.it/~gt4/introductory/exercise-install.tar.gz
$ tar xzf exercise-install.tar.gz
$ cd exercise-install/stickynote/source

1.4 Set environment variables in the Exercise window:

$ export GLOBUS_LOCATION=[use the value that was echoed earlier in the GT window, not $PWD]

1.5 Build and deploy the tutorial code in the Exercise window:

$ ant deploy
Buildfile: build.xml

init:

bindings:

[... many more build messages...]

BUILD SUCCESSFUL
Total time: 27 seconds

1.6 Verify that the show-note client built successfully by checking that it exists:

$ ls $GLOBUS_LOCATION/bin/show-note
/gt4/childers/intro/globus-4.0.2-java-norft/bin/show-note

If you get an error, see the Exercise 1 troubleshooting section.

1.7 Start a web service container without security in the GT window:

$ bin/globus-start-container -nosec
Starting SOAP server at: http://192.168.123.100:8080/wsrf/services/
With the following services:

[1]: http://192.168.123.100:8080/wsrf/services/InMemoryServiceGroupEntry
[2]: http://192.168.123.100:8080/wsrf/services/TriggerFactoryService
[3]: http://192.168.123.100:8080/wsrf/services/IndexFactoryService
[4]: http://192.168.123.100:8080/wsrf/services/Version
[5]: http://192.168.123.100:8080/wsrf/services/IndexService
[6]: http://192.168.123.100:8080/wsrf/services/NotificationConsumerService
[7]: http://192.168.123.100:8080/wsrf/services/DefaultTriggerServiceEntry
[8]: http://192.168.123.100:8080/wsrf/services/TriggerServiceEntry
[9]: http://192.168.123.100:8080/wsrf/services/IndexServiceEntry
[10]: http://192.168.123.100:8080/wsrf/services/StickyNoteService
[11]: http://192.168.123.100:8080/wsrf/services/AdminService
[12]: http://192.168.123.100:8080/wsrf/services/DefaultIndexService
[13]: http://192.168.123.100:8080/wsrf/services/DefaultIndexServiceEntry
[14]: http://192.168.123.100:8080/wsrf/services/DefaultTriggerService
[15]: http://192.168.123.100:8080/wsrf/services/ShutdownService
[16]: http://192.168.123.100:8080/wsrf/services/ContainerRegistryService
[17]: http://192.168.123.100:8080/wsrf/services/TriggerService
[18]: http://192.168.123.100:8080/wsrf/services/gsi/AuthenticationService
[19]: http://192.168.123.100:8080/wsrf/services/InMemoryServiceGroupFactory
[20]: http://192.168.123.100:8080/wsrf/services/InMemoryServiceGroup
[21]: http://192.168.123.100:8080/wsrf/services/ContainerRegistryEntryService
[22]: http://192.168.123.100:8080/wsrf/services/SubscriptionManagerService

Control will not return to the prompt. This container will stay running until you exit it with Control-C. If you need to start up the container on an alternate port, use the "-p" command-line argument (e.g., "bin\globus-start-container -nosec -p 8888" will cause the container to listen on port 8888)

1.8 In the Exercise window, run the show-note client against your StickyNote service:

$ $GLOBUS_LOCATION/bin/show-note -s http://localhost:8080/wsrf/services/StickyNoteService
<ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">hello.</ns1:noteContent>

In the GT window, you should see a message appear like the following:

2005-01-24 12:48:54,668 INFO stickynote.SingleNoteHome [Thread-2,findSingleton:29] Creating a single StickyNote.

This is just a debugging message printed by your service. See if you can find where in the code this message came from.

1.9 Write a single word (no embedded spaces) on the note from the Exercise window:

$ $GLOBUS_LOCATION/bin/write-note -s http://localhost:8080/wsrf/services/StickyNoteService cheese
Message written.

$ $GLOBUS_LOCATION/bin/show-note -s http://localhost:8080/wsrf/services/StickyNoteService
<ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">cheese</ns1:noteContent>

Troubleshooting for exercise 1:

    ant deploy failures

  • If you get an error during ant deploy like:

    BUILD FAILED
    file:/home/bacon/tmp/playground/java/stickynote/source/build.xml:62: /home/bacon/tmp/install/share/schema not found.

    Check to make sure that GLOBUS_LOCATION is set correctly, and that $GLOBUS_LOCATION/share/schema exists.

  • If you get an error during ant deploy like:

    Error: JAVA_HOME is not defined correctly.
    We cannot execute java

    Make sure you have Java installed, that the javac command is on your PATH, and that JAVA_HOME is set to the directory where Java is installed.

  • If you get an error during ant deploy like:

    Warning: JAVA_HOME environment variable is not set (or not exported).
    If build fails because sun.* classes could not be found
    you will need to set the JAVA_HOME environment variable
    to the installation directory of java.

    Make sure you have JAVA_HOME set to the directory where JAVA is installed on your machine.

  • globus-start-container failures

  • If you get an error during globus-start-container like the following:

    Error: GLOBUS_LOCATION invalid or not set:

    Your GLOBUS_LOCATION is either not set, or set to an incorrect directory. Run echo $GLOBUS_LOCATION to see what the value is set to. If necessary, correct it to the same location you had set when you ran ant deploy. Remember, you have to have this value set in both windows you're using.

  • If you get an error during globus-start-container like the following:

    Failed to start container: Container failed to initialize [Caused by: Secure container requires valid credentials]

    Remember to include the "-nosec" option when starting the container.

  • If you get an error during globus-start-container like the following:

    Failed to start container: Container failed to initialize [Caused by: Address in use]

    You already have a server running on the same port as the container is trying to use. Make sure to either stop that server or start the container on some other port using the -p option.

  • If you get an error during globus-start-container like the following:

    Failed to obtain a list of services from
    'http://140.221.57.104:8080/wsrf/services/ContainerRegistryService' service: ;
    nested exception is: java.net.ConnectException: Connection timed out

    Then your machine probably has a name assigned to it that does not correspond to the IP address you have during this tutorial. Find out your current IP address, and add a line to
    $GLOBUS_LOCATION/etc/globus_wsrf_core/server-config.wsdd under the <globalConfiguration> section:

    <parameter name="logicalHost" value="xxx.xxx.xxx.xxx"/>

    Here's an example of the file with that parameter set:

    <globalConfiguration>
        <parameter name="sendXsiTypes" value="true"/>
        <parameter name="logicalHost" value="192.168.123.104"/>
        <!-- @CONTAINER_SECURITY_DESCRIPTOR@ -->

Exercise 1 complete!

- table of contents -

Exercise 2: Create Resources

Concepts

We begin this chapter with a brief discussion of some GT4 service basics.

Services have operations. Operations can be thought of as a discrete bits of functionality made network-accessible by the system architect. A service's operations can be invoked by standalone clients, applications, or other services. Our StickyNote example currently has two operations defined, write a note and show a note, both of which were used in Exercise 1. In this chapter we will learn how to add a new operation: create a note.

In addition to operations, GT4 web services can provide access to service state. State is service-related data that persists across client invocations. StickyNote's service-related data is note content. In GT4, service state is stored in resources. A resource can be thought of as service-related data that is made network-accessible by the system architect. Our StickyNote service currently contains only a single resource, which means it only supports one instance of state. In this chapter we will learn how to add support for multiple notes/instances.

In GT4, resources are managed by Resource Homes. Resource homes provide an interface for adding and removing resources. In order to support multiple notes, we must change from a resource home that is limited to a single resource, to a resource home that can hold multiple resources.

Additional material:

Further reading on service basics
A sidebar on the factory pattern and GT4 resources

Implementation Details

This exercise, in which we're learning how to create resources, involves changing both service behavior and resource home behavior. In order to do this we will need to modify three different types of files:

  • The WSDL: contains the public interface definition for the service. Public service data and operations are declared here. In this exercise we'll add the create a note operation definition (and its associated messages) to this file.
  • Java files: contain the implementation of the service and its clients. In this exercise we'll add the server-side create-note functionality, as well as a new create-note client.
  • deploy-jndi-comfig.xml: tells the container which type of resource home should be used for the service and where to find the code. Our changes here will enable the switch to a resource home that supports multiple resources.

Additional material:

An intro to WSDL / p. 449 of Programming Java Services
GT4 glossary entry on ResourceHome
The GT4 Java WS Core Design document

Exercises

2.1 Modify files:

You must modify four files in order to successfully complete this exercise. All modifications you need to make to source files today begin with a comment tag of "EXERCISE n: <type of modification needed>" and end with "END OF EXERCISE n <type of modification>". Some files contain more than one set of changes to be made for a given file.

Take care not to edit the files in the build/ subdirectory, as files in the build/ tree are overwritten by the "ant deploy" command. The files you need to edit are at or below the /gt4/$USER/intro/exercise-install/stickynote/source/ directory.

If you are not familiar with commenting out XML or WSDL, the comment tags are
"<!--" and "-->". For instance:

This line is not commented out
<!-- This line is commented out -->
<!--
These lines
are both commented out
-->

The four files you need to modify in this exercise are listed in the following box. Edit these files now, then proceed to step 2.2:

schema/tutorial/stickynote_port_type.wsdl:
Add the create operation definition, and associated message and types. There are a total of three sections to uncomment.

src/org/globus/tutorial/stickynote/StickyNoteService.java:
Implement the create operation. There is one section to uncomment.

src/org/globus/tutorial/stickynote/client/CreateNote.java:
Write the client for the create operation. There is one section to uncomment.

deploy-jndi-config.xml:
Change the ResourceHome to support multiple resources. There is one section to comment out, and one section to uncomment.

2.2 Clean, rebuild and redeploy the tutorial code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

2.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

2.4 Create a new note from the exercise window using the new create-note client:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-1947556620.epr
$ $GLOBUS_LOCATION/bin/show-note -e note-1947556620.epr
<ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">hello.</ns1:noteContent>

Note that your filename will be different than the one shown. Also notice that the message displayed by the container has changed from a SingleNoteHome to the ManyNoteHome:

2005-01-24 13:31:02,139 INFO stickynote.ManyNoteHome [Thread-2,create:52] Creating a new StickyNote.

2.5 Create multiple notes:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-18887662.epr

Write different messages on each note, verifying that they keep independent messages:

$ $GLOBUS_LOCATION/bin/write-note -e note-18887662.epr haggis
Message written.
$ $GLOBUS_LOCATION/bin/write-note -e note-1947556620.epr lutefisk
Message written.
$ $GLOBUS_LOCATION/bin/show-note -e note-1947556620.epr
<ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">lutefisk</ns1:noteContent>
$ $GLOBUS_LOCATION/bin/show-note -e note-18887662.epr
<ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">haggis</ns1:noteContent>

Troubleshooting for exercise 2:

    create-note troubles

  • If you have an error during create-note like:

    Error: java.lang.Exception: [CORE] Operation 'Create' defined in wsdl
    but it's not implemented in the 'StickyNoteService' service.

    Make sure you have stopped and restarted your container after modifying the code. In other words, stop the container by pressing Control-C, and restart it. Your service changes should then take effect.

  • show-note troubles

  • If you have an error during show-note like:

    Error: note-1947556620.epr (No such file or directory)

    Make sure you used the filename returned by your create-note command. You cannot copy and paste the filenames in these instructions, as the filenames are randomly generated.

  • If you have an error during show-note like:

    Error: No scheme found in URI.

    Make sure you used the "-e" option with show-note and the EPR filename, not the "-s" option from the last exercise.

  • If you have an error during show-note like:

    Error: org.oasis.wsrf.properties.ResourceUnknownFaultType

    Your resources exist only as long as the container is still running. If you stop and restart your container, your old EPR files are no longer valid. Make sure you show-note on notes that were created while your current container was running.

Exercise 2 complete!

- table of contents -

Exercise 3: Destroy Resources

Concepts

If we keep creating resources, we'll eventually overwhelm our system. Efficient and appropriate removal of unwanted state is an important topic in distributed computing. The point of this simple exercise is to teach you a fundamental technique for dealing with unwanted state: explicit destruction of resources.

Using EPRs to identify resources

Before we begin destroying resources, it is important to understand how to identify them. We don't want to delete the wrong thing! In web services, End Point References (EPRs) are used to identify endpoints on the network. EPRs are similar to the mailing address for your house: mailing addresses identify an endpoint (your house) on the network (street system). Both services and resources on the Grid can be located using EPRs.

Further reading on EPRs

Implementation Details

The ability to destroy resources is built-in to the GT4 hosting environment. The code that performs destruction is packaged as an operation provider. GT4 Java service developers have the option of using pre-written operation providers, or even writing their own. Any number of operation providers can be loaded at runtime.

So to make StickyNote resources "destroyable", we'll first need to define the destroy operation in the StickyNote WSDL. This is because we want to add new functionality to the service's public interface. We'll also need to add the implementation, but since the container developers have already written a provider for this functionality, we don't need to write any code! We will simply edit the service deployment instructions, specifying that the container should load the DestroyProvider code when StickyNote is activated.

Additional material:

Further reading on operation providers
GT4 glossary entry on WSDD
An overview of WSDD / p. 67 of Programming Java Services

Exercises

3.1 Modify files:

schema/tutorial/stickynote_port_type.wsdl:
Add a Destroy operation

deploy-server.wsdd:
Add the DestroyProvider operation provider

3.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

3.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

3.4 From the Exercise window, create a new note:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-1107777.epr

3.5 Inspect the EPR to see what it looks like:

$ cat note-1107777.epr
<StickyNoteEndpoint xsi:type="ns1:EndpointReferenceType"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:ns1="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<ns1:Address xsi:type="ns1:AttributedURI">
http://192.168.123.106:8080/wsrf/services/StickyNoteService</ns1:Address>
<ns1:ReferenceProperties xsi:type="ns1:ReferencePropertiesType">
<ns1:NoteKey xmlns:ns1="http://sticky.com">233427</ns1:NoteKey>
</ns1:ReferenceProperties>
<ns1:ReferenceParameters xsi:type="ns1:ReferenceParametersType"/>
<StickyNoteEndpoint/>

3.6 Now destroy the note Resource using its EPR:

$ $GLOBUS_LOCATION/bin/wsrf-destroy -e note-1107777.epr
Destroy operation was successful

Note that wsrf-destroy is a command-line client tool distributed with the Globus Toolkit -- it is not demonstration code. For your convenience, a pre-written client is included for each of the pre-written operation providers distributed with GT4!

3.7 Attempt to show the destroyed note:

Execute this command from the Exercise window.

$ $GLOBUS_LOCATION/bin/show-note -e note-1107777.epr
Error: org.oasis.wsrf.properties.ResourceUnknownFaultType

An "unknown resource" fault will be triggered if you've completed this exercise correctly.

WSRF and WSN provide a set of standard faults (WS-BaseFaults) that are derived from a well-defined structure for expressing error conditions. The faults returned by the StickyNote service are from the set of standard faults. We recommend that service developers who wish to define custom faults for their services derive from the BaseFault structure.

Troubleshooting for exercise 3:

    create-note troubles

  • If you have trouble with create-note like:

    Error: java.lang.Exception: [CORE] Operation 'Destroy' defined in wsdl
    but it's not implemented in the 'StickyNoteService' service.

    Remember to stop and restart your container after deploying the updated code. Stop the container with Control-C and restart it for your changes to take effect.

  • If you have trouble with create-note like:

    Error: java.lang.Exception: [CORE] Operation 'GetResourceProperty' defined in wsdl
    but it's not implemented in the 'StickyNoteService' service.

    Remember to uncomment the section containing the operationProviders in your WSDD.

Exercise 3 complete!

- table of contents -

Exercise 4: Add a Resource Property

Concepts

In exercise 2 we learned that GT4 services provide public access to service state via resources, and that management of a service's resources are handled by a resource home. In this exercise we will look further inside GT4 state management infrastructure to see the fine details of service data storage.

Any resource can publicly expose internal state as resource properties. A Resource Property (RP) is defined in the service's WSDL as an XML element contained within a Resource. RPs provide a view on the current state of the resource. Types of information that can be stored in resource properties include service data, metadata about the state, or other data needed by the resource to manage the state. The point of this exercise is to teach you how to add a new RP.

Implementation Details

Actually, we've been interacting with a StickyNote RP since the first exercise. If you look at the stickynote WSDL, you'll find a definition for noteContent, which is the public name of the note's contents. The write-note and show-note clients have been storing and retrieving data from this RP all along.

To add a new RP to stickynote, we must: 1) declare it in the WSDL, and 2) write the server-side code for maintaining its value. Further, we'll modify the show-note client so that it will report the value of the new RP. The RP we'll be adding is "last modified time", which will be updated every time "note content" is modified.

Exercises

4.1 Modify files:

schema/tutorial/stickynote_port_type.wsdl:
Add the definition of the lastModified RP type and add QueryResourceProperties operation

src/org/globus/tutorial/stickynote/client/ShowNote.java:
Have the show-note client report the lastModified time

src/org/globus/tutorial/stickynote/StickyNote.java:
Implement the lastModified behavior

src/org/globus/tutorial/stickynote/StickyNoteConstants.java:
Uncomment the definition of the constant

4.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

4.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

4.4 From the Exercise window, create a new sticky note and inspect its timestamp:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-1947456611.epr
$ $GLOBUS_LOCATION/bin/show-note -e note-1947456611.epr
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-06T18:14:29.153Z</ns1:lastModified>
<ns1:noteContent>hello.</ns1:noteContent>
</ns1:StickyNoteResourceProperties>

4.5 Modify the value of the sticky note, then verify that the timestamp was updated:

$ $GLOBUS_LOCATION/bin/write-note -e note-1947456611.epr spider
Message written.
$ $GLOBUS_LOCATION/bin/show-note -e note-1947456611.epr
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-06T18:24:39.153Z</ns1:lastModified>
<ns1:noteContent>spider</ns1:noteContent>
</ns1:StickyNoteResourceProperties>

You should see that the timestamp has updated since the note was created.

Troubleshooting for exercise 4:

    show-note troubles

  • If you have trouble with show-note like this:

    Error: Tried to invoke method public
    org.oasis.wsrf.properties.QueryResourcePropertiesResponse
    org.globus.wsrf.impl.properties.QueryResourcePropertiesProvider.queryResourceProperties
    (org.oasis.wsrf.properties.QueryResourceProperties_Element)
    throws java.rmi.RemoteException,
    org.oasis.wsrf.properties.InvalidResourcePropertyQNameFaultType,
    org.oasis.wsrf.properties.ResourceUnknownFaultType,
    org.oasis.wsrf.properties.InvalidQueryExpressionFaultType,
    org.oasis.wsrf.properties.QueryEvaluationErrorFaultType,
    org.oasis.wsrf.properties.UnknownQueryExpressionDialectFaultType with arguments
    org.oasis.wsrf.properties.QueryResourceProperties_Element.
    The arguments do not match the signature.;
    nested exception is: java.lang.IllegalArgumentException:
    object is not an instance of declaring class

    Remember to stop and restart your container after deploying the updated code. Stop the container with Control-C and restart it for your changes to take effect.
    Also, this error might appear when the operation provider for this operation is deployed but the operation itself is not defined in WSDL. Ensure WSDL has the QueryResourceProperties operation uncommented.

Exercise 4 complete!

- table of contents -

Exercise 5: Discover a Resource

Concepts

In Grid computing we often need to find a resource that has some property. In our case, a person may want to find a note containing a particular content, such as "VelvetCapezzuto". Discovery is the process of identifying resources on the Grid based on their attributes.

Thus far we've manually kept track of the existence of our individual stickynote resources. In real Grid deployments this is infeasible. GT4 includes an implementation of an index service that can be used to collect available resources. In this chapter we will register our stickynote resources with a local Index. After the resources are registered, we will be able to search the Index for notes matching a particular value. Eureka!

Implementation Details

The Index service will cache the data of the Resources that have registered with it. In order to inspect the data stored in an Index, we could dump its entire contents by using a GT4 command-line client called wsrf-query. However, if many resources have registered, the index will contain a large amount of data. It is therefore often desirable to perform a search on an Index such that only interesting data is returned.

To search the Index, we can run an XPath query against its contents. XPath is a convenient query language for searching XML documents. But since XML query languages are outside the scope of this tutorial, we'll provide you with a convenient client, called search-note, that knows how to search the Index for a text value.

For this exercise you will override the default add() and remove() methods of stickynote's resource home; this is because you must add logic that registers/unregisters with the Index. You'll also add logic to register the stickynote service to the Index. And finally you'll configure the Index to cache noteContent.

Exercises

5.1 Modify files:

src/org/globus/tutorial/stickynote/ManyNoteHome.java:
Override the add/remove methods to invoke registration/unregistration logic in the resource

src/org/globus/tutorial/stickynote/StickyNote.java:
Register and unregister with the index

etc/post-deploy.xml:
Uncomment search-note launch script generation

etc/registration.xml:
Specify data to be cached and other aggregation parameters. Note that only the noteContent RP will be cached

5.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

5.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

5.4 From the Exercise window, create a sticky note and write your name on it:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-8469523889.epr
$ $GLOBUS_LOCATION/bin/write-note -e note-8469523889.epr VelvetCapezzuto
Message written.

5.5 Query the local index to view its entire contents:

$ $GLOBUS_LOCATION/bin/wsrf-query -s http://localhost:8080/wsrf/services/DefaultIndexService '/*'
[... lots of output ...]
<ns1:MemberServiceEPR>
<ns8:Address xmlns:ns8="http://schemas.xmlsoap.org/ws/2004/03/addressing">
http://192.168.123.106:8080/wsrf/services/StickyNoteService</ns8:Address>
<ns9:ReferenceProperties xmlns:ns9="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<ns1:NoteKey xmlns:ns1="http://sticky.com">5550702</ns1:NoteKey>
</ns9:ReferenceProperties>
<ns10:ReferenceParameters xmlns:ns10="http://schemas.xmlsoap.org/ws/2004/03/addressing"/>
</ns1:MemberServiceEPR>
[... more output ...]

Note that wsrf-query is a standard command-line client tool distributed with the Globus Toolkit.

5.6 Verify that the NoteKey value in the above output matches the NoteKey value in your EPR:

$ grep NoteKey note-8469523889.epr
<ns1:NoteKey xmlns:ns1="http://sticky.com">5550702</ns1:NoteKey>

Notice that the value 5550702 matches the value in the index entry above.

5.7 Find a note from the local index based on content:

$ $GLOBUS_LOCATION/bin/search-note -s http://localhost:8080/wsrf/services/DefaultIndexService VelvetCapezzuto
Note 0: <ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">VelvetCapezzuto</ns1:noteContent>

Note that you are discovering the note via its attributes. There is no need for you to know the EPR!

Exercise 5 complete!

- table of contents -

Exercise 6: Register with a Community Index

Concepts

There is a structure that underlies many Grid problem spaces. The structure is referred to as a Virtual Organization (VO). Virtual Organizations drive many of the requirements for Grid technology. Key characteristics of a Virtual Organization include:

* distributed, heterogeneous resources (clusters, storage farms, remote sensors, &c.),
* people, who tend to be distributed as well,
* the people and resources are linked by networks that cross institutional/administrative domains,
* the people share some common goals, motivating a desire to share their resources with each other in a controlled fashion,
* and finally, VOs are dynamic: the people and resources must be able to come and go without disrupting the system

Whew! No shortage of technology challenges here! However, the benefits of meeting these challenges are potentially huge: these large-scale collaborations are trying to solve problems that are beyond the reach of any single organization.

What does this mean to you? Well, as it turns out, during this tutorial we have been building a tiny virtual organization! Each of you has been hosting stickynote resources on your laptop; those stickynotes represent resources in our tutorial VO. A key characteristic of VOs is that resources are shared. In order to share a resource, you must make its existence known to others. The goal of this exercise is to teach you how to advertise the existence of resources in a VO.

The way for a resource to advertise its existence using GT4 is to register with an Index service known to the community. The instructors are hosting a central Index for our little community. The hand-on work of this chapter will be to make the local Index running on each of your laptops register with this community Index.

Additional material:

The Anatomy of the Grid: Enabling Scalable Virtual Organizations

Implementation Details

Since we set up stickynote registration with the local Index in the previous exercise, the task of registering with an upstream Index is a simple matter. So simple that only configuration changes are needed - no need to recompile any code!

In order to advertise our stickynote resources to the community, we will need to configure the local index to register with the community index. The instructors will provide you with a uri for the community index. (If you are going through this tutorial on your own, you can simulate a "community index" by standing up another Index service in a different GLOBUS_LOCATION and using that as your community index.).

We will also adjust the data refresh rate to an artificially high number between the local and community indexes, in order to watch the registrations happen during our artifically-sped up tutorial.

Exercises

6.1 Modify files:

$GLOBUS_LOCATION/etc/globus_wsrf_mds_index/hierarchy.xml:
Uncomment the "<upstream>" parameter, making sure the value corresponds to http://192.167.1.7:8888/wsrf/services/DefaultIndexService (the URI of the community index). Do not uncomment or otherwise touch the "<downstream>" parameter.

$GLOBUS_LOCATION/etc/globus_wsrf_mds_index/upstream.xml:
Change the value of the PollIntervalMillis parameter from 600000 (10 minutes) to 150000 (2 minutes). Do not make this value smaller than 150000 or the community index will become too busy. In a real grid deployment this value would be much higher (20 minutes or more).

6.2 Kill and restart the web service container in the GT window (rebuild not needed):

$ [ctrl-c]
$ bin/globus-start-container -nosec

6.3 From the Exercise window, create a sticky note:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-269523889.epr
$ $GLOBUS_LOCATION/bin/write-note -e note-269523889.epr BraveSirRobin
Message written.

At this point, students should begin to see fish entering the central tank. Each fish in the tank represents a student sticky resource that has successfully registered with the community index. The text on the side of the fish corresponds to the message in the sticky note; the IP address on the side of the fish corresponds to the IP address of the student's host.

6.4 The community index, like the local index, can be inspected manually:

$ $GLOBUS_LOCATION/bin/wsrf-query -s http://192.167.1.7:8888/wsrf/services/DefaultIndexService '/*'
[... lots of output ...]
<ns1:MemberServiceEPR>
<ns8:Address xmlns:ns8="http://schemas.xmlsoap.org/ws/2004/03/addressing">
http://192.168.123.106:8080/wsrf/services/StickyNoteService</ns8:Address>
<ns9:ReferenceProperties xmlns:ns9="http://schemas.xmlsoap.org/ws/2004/03/addressing">
<ns1:NoteKey xmlns:ns1="http://sticky.com">8640702</ns1:NoteKey>
</ns9:ReferenceProperties>
<ns10:ReferenceParameters xmlns:ns10="http://schemas.xmlsoap.org/ws/2004/03/addressing"/>
</ns1:MemberServiceEPR>
[... more output ...]

6.5 Find a note from the community index based on content:

$ $GLOBUS_LOCATION/bin/search-note -s http://192.167.1.7:8888/wsrf/services/DefaultIndexService BraveSirRobin
Note 0: <ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">BraveSirRobin</ns1:noteContent>
Note 1: <ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">BraveSirRobin</ns1:noteContent>
Note 2: <ns1:noteContent xmlns:ns1="http://tutorial.globus.org/stickynote">BraveSirRobin</ns1:noteContent>

Note that the community index is caching the data from the entire VO, and so you are now able to discover resources owned by other participants!

6.6 View the contents of the community index using WebMDS (optional)

If the instructors are not too busy solving problems, a browser-based viewer for the community index will be made available. If it is running you will be able to view the contents of the tutorial index at a url to be supplied by the instructors.

WebMDS is a servlet distributed with GT4 that can be used to display the content of GT4 indexes. The view of the data can be refreshed by hitting the reload button on your browser.

6.7 Note that when you destroy one of your sticky resources, its entry will eventually be removed from the community index:

$ $GLOBUS_LOCATION/bin/wsrf-destroy -e note-269523889.epr
Destroy operation was successful

With the current tutorial configuration, the entry will expire approximately 10 minutes after the resource is destroyed.

Exercise 6 complete!

- table of contents -

Exercise 7: Lease-based Lifetime

Concepts

In Grid computing we need a mechanism to clean up old/unwanted state. So far we've cleaned up after ourselves explicitly using the Destroy operation. Manually destroying our own notes is fine in some contexts, but is not always possible or appropriate. For instance, if a client dies or the network goes down, you would like a mechanism to clean up unused state automatically. Under the lease-based model resources are removed unless kept alive by interested parties.

To illustrate lease-based lifetime management, we will experiment with scheduled destruction of stickynotes. Under scheduled destruction, when the lifetime lease expires, the resource will be destroyed. Interested parties must renew the lease to keep the resource alive.

Implementation Details

The GT4 Java container handles the vast majority of the lifetime management logic for us. We simply need to add a standard RP and pre-written operation provider to our service. Look! No coding!

Important notes:

  1. explicit destruction is required for resources with a termination time of nil; default value is nil
  2. immediate removal of a destroyed resource is not guaranteed; the container performs such garbage collection in periodic sweeps

Exercises

7.1 Modify files:

schema/tutorial/stickynote_port_type.wsdl:
Add a SetTerminationTime operation

src/org/globus/tutorial/stickynote/StickyNote.java:
Add the ResourceLifetime property to control termination time

deploy-server.wsdd:
Add the SetTerminationTimeProvider

7.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

7.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

7.4 From the Exercise window, create a sticky note:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-1234.epr

7.5 Check the termination time of the sticky note:

$ $GLOBUS_LOCATION/bin/show-note -e note-1234.epr
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote"
xmlns:ns2="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:lastModified>2005-01-24T22:45:33.053Z</ns1:lastModified>
<ns1:CurrentTime
xmlns:ns1="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd">
2005-01-24T22:46:10.503Z</ns1:CurrentTime>
<ns1:TerminationTime xsi:nil="true"
xmlns:ns1="http://docs.oasis-open.org/wsrf/2004/06/wsrf-WS-ResourceLifetime-1.2-draft-01.xsd"/>
<ns1:noteContent>hello.</ns1:noteContent>
</ns1:StickyNoteResourceProperties>

Observe that the terminationTime resource property is set to nil - this means that the newly created note has no termination time (and so will not be automatically destroyed). This means that this note will behave like the notes created in previous exercises.

7.5 Set the termination time of the sticky note:

$ $GLOBUS_LOCATION/bin/wsrf-set-termination-time -e note-1234.epr 100
requested: Mon Jan 24 16:50:14 CST 2005
scheduled: Mon Jan 24 16:50:14 CST 2005

This will set the termination time of the resource to 100 seconds in the future.

Now wait a few minutes, using 'show-note' every minute or so and observe the current time approach and pass beyond the termination time. Eventually, the resource will be destroyed automatically and you will get the following error:

$ $GLOBUS_LOCATION/bin/show-note -e note-1234.epr
Error: org.oasis.wsrf.properties.ResourceUnknownFaultType

This indicates that the Sticky Note resource no longer exists, and has been destroyed automatically.

Exercise 7 complete!

- table of contents -

Exercise 8: Resource as Notification Producer

Concepts

Right now we have the ability to make a one-off query to retrieve RP values (i.e., "pull mode"), an example being the show-note client. GT4 also supports a "push mode" model in which notifications are received when the RP value changes.

Notification vocabulary:

  • We can subscribe to received notifications
  • Subscriptions are for a particular topic
  • Notifications go from a Producer to a Consumer
  • Each topic has a name
  • Topics can be
    • changes in RP values
    • resource destruction
    • service group changes
    • other interesting things...

Each subscription is represented as a new kind of resource. We manage the subscription through this resource (e.g., cancel subscription by destroying subscription resource.)

Implementation Details



The watch-note client is actually a small container. For more information see section 5.2.1.2, Setting up and receiving notifications, from the Java WS Core Developer's Guide

Exercises

8.1 Modify files:

schema/tutorial/stickynote_port_type.wsdl:
Add Subscribe operation

src/org/globus/tutorial/stickynote/StickyNote.java:
Add TopicList code

deploy-server.wsdd:
Add the SubscribeProvider

etc/post-deploy.xml:
Uncomment watch-note launch script generation

8.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

8.3 Kill and restart the web service container in the GT window:

$ [ctrl-c]
$ bin/globus-start-container -nosec

8.4 From the Exercise window, create a sticky note:

$ $GLOBUS_LOCATION/bin/create-note -s http://localhost:8080/wsrf/services/StickyNoteService
new note created...
EPR written to file: note-27772.epr

8.5 Create a third window for watching the sticky note:

$ cd /gt4/$USER/intro/exercise-install/stickynote/source
$ export GLOBUS_LOCATION=[your GL here]

Ensure that JAVA_HOME and GLOBUS_LOCATION are correctly set in the new window.

8.5 Launch the sticky note watcher:

$ $GLOBUS_LOCATION/bin/watch-note -e note-27772.epr
Waiting for notification. Ctrl-C to end.

Control will not return to the prompt. The container hosting the watch client will stay running until you exit with Control-C.

8.6 Modify the value of the sticky note:

$ $GLOBUS_LOCATION/bin/write-note -e note-27772.epr coconut
Message written.

You have successfully completed this exercise when watch-note shows the new value of the note.

You can write to the note some more times, and watch-note will display the updated value each time.

8.7 Kill the watch-note container:

$ [ctrl-c]
$

We will not use the third command window again.

Troubleshooting for exercise 8

  • If your watch-note client does not receive change notifications, and you were previously compelled to set the logicalHost parameter in server-config.wsdd:
  • Add the same parameter to $GLOBUS_LOCATION/etc/globus_wsrf_core/client-server-config.wsdd, under the <globalConfiguration> section:

    <parameter name="logicalHost" value="xxx.xxx.xxx.xxx"/>

Exercise 8 complete!
Note! You may now jump to the advanced practical (though you are also free to complete the security chapters if you wish.)

- table of contents -

Exercise 9: Service-Level Security

Concepts

An excellent overview of the key concepts behind GT4 security can be found on the Globus security: key concepts page.

Each service can be configured with security properties like authentication scheme that is required to access the service, authorization policy enforced by the service, credentials used by the service and so on.

Implementation Details

The purpose of this exercise is to give you a first look at securing a GT4 service. Our example demonstrates how to configure transport-level security and gridmap authorization. An excellent write-up of the technical details behind this and other approaches for securing GT4 services can be found in Chapters 15-22 of Programming Java Services. A subset of the material in print is available for free online.

Exercises

9.1 Modify files:

deploy-server.wsdd:
Add securityDescriptor parameter

etc/grid-mapfile:
Add your certificate subject. Do not edit this file until you create a proxy in step 9.3.

etc/security-config.xml:
No modification needed

$HOME/.globus
This directory contains certificates that were generated for you. Don't change any file in this directory.

9.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

9.3 Generating proxy certificate

Create a proxy from the certificates stored in $HOME/.globus. The passphrase for your certificate private key is ISCHIA (in capital letters)

$ bin/grid-proxy-init -globus
Your identity: /C=US/O=Globus Alliance/OU=User/CN=101ca1dcd9b.8029cef5
Enter GRID pass phrase for this identity:
Creating proxy ................................. Done
Proxy verify OK
Your proxy is valid until Tue Feb 01 03:40:22 CST 2005

Copy the value following "Your identity:" into the file stickynote/source/etc/grid-mapfile
The identity should be in quotes and followed by your username. For example:

"/C=US/O=Globus Alliance/OU=User/CN=101497d3dcd.3dcd5aef" ranantha
"/C=US/O=Globus Alliance/OU=User/CN=10187397229.feeb1182" lcc
"/C=US/O=Globus Alliance/OU=User/CN=101ca1dcd9b.8029cef5" bacon

After making the edits, run ant deploy again. The copy of the grid-mapfile being stored with your source code will be copied into $GLOBUS_LOCATION where it will be used by your service.

9.4 Start a web service container with security in the GT window:

$ bin/globus-start-container
[... more container output ...]
[21]: https://192.168.123.100:8443/wsrf/services/ContainerRegistryEntryService
[22]: https://192.168.123.100:8443/wsrf/services/SubscriptionManagerService

9.5 From the Exercise window, run the client:

$ $GLOBUS_LOCATION/bin/create-note -s https://localhost:8443/wsrf/services/StickyNoteService -z none
new note created...
EPR written to file: note-672627063.epr
$ $GLOBUS_LOCATION/bin/create-note -s https://localhost:8443/wsrf/services/StickyNoteService -z self
new note created...
EPR written to file: note-807655197.epr

Notice two things. First of all, because we started the container with security, the URLs start with "https://" instead of "http://". Secondly, because our client derives from the base client, we have a -z option that lets us specify what kind of authorization the client will attempt to perform.

9.6 Now that we have created a note, only those in the grid-mapfile can edit it:

$ $GLOBUS_LOCATION/bin/write-note -e note-807655197.epr -z none cheese
Message written.
$ $GLOBUS_LOCATION/bin/show-note -e note-807655197.epr -z none
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-10T22:02:26.564Z</ns1:lastModified>
<ns1:noteContent>cheese</ns1:noteContent></ns1:StickyNoteResourceProperties>

9.7 Any user, including an anonymous user can read the note.

$GLOBUS_LOCATION/bin/show-note -e note-807655197.epr -z none -a
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-10T22:02:36.564Z</ns1:lastModified>
<ns1:noteContent>cheese</ns1:noteContent></ns1:StickyNoteResourceProperties>

The -a option sets up the client invocation to be anonymous. Note that the anonymous user cannot create a new note, because that requires gridmap authorization.

Troubleshooting for exercise 9

  • If you see an error like the following:

    Error: org.globus.wsrf.impl.security.authorization.exceptions.AuthorizationException:
    "/C=US/O=Globus Alliance/OU=User/CN=1014e44b14c.fd334f58" is not authorized to use operation:
    {http://tutorial.globus.org/stickynote}create on this service

    Then the identity of your client is not in the grid-mapfile for the service you are trying to access.

  • The following error:

    Error: org.globus.wsrf.impl.security.authorization.exceptions.AuthorizationException:
    "/C=US/O=Globus Alliance/OU=User/CN=101497d3dcd.3dcd5aef" is not authorized to use operation:
    {http://tutorial.globus.org/stickynote}write on this service

    represents an attempt to invoke the write operation when the client is not present in the grid-mapfile

  • If you are getting strange "CA not found" errors even if you think certificates are in the correct directories, check to see if you have a ~/.globus/cog.properties file which is overriding the default directory search order.

  • If the following error occurs:

    Error: GSI Transport authentication required for "{http://tutorial.globus.org/stickynote}create" operation.

    Remember to run a secure container, and to use port 8443 (and an https:// URI).

Exercise 9 complete!

- table of contents -

Exercise 10: Resource-Level Security

Exercises

Security configuration can be done at the resource level and it takes precedence over service level settings. But unlike service security setting, programmatic altering of a resource is required to secure a resource. The resource needs to implement the org.globus.wsrf.security.SecureResource interface.

In the previous exercise every user in the service level gridmap was able to write to all notes. In this exercise, resource security is used to restrict write access to only the creator of the sticky note. This is done by creating a resource level gridmap at the time of resource creation and populating it with the identity of the creator.

10.1 Modify files:

src/org/globus/tutorial/stickynote/StickyNote.java:
Add resource security descriptor that sets up a grid-map file with only the identity of the client creating the note

10.2 Clean, rebuild and redeploy the StickyNote code:

Execute these commands from the /gt4/$USER/intro/exercise-install/stickynote/source/ directory of the Exercise window.

$ ant clean
$ ant deploy

10.3 Start a web service container with security in the GT window:

$ bin/globus-start-container
[... more container output ...]
[21]: https://192.168.123.100:8443/wsrf/services/ContainerRegistryEntryService
[22]: https://192.168.123.100:8443/wsrf/services/SubscriptionManagerService

10.4 From the Exercise window, run the client:

$ $GLOBUS_LOCATION/bin/create-note -s https://localhost:8443/wsrf/services/StickyNoteService -z none
new note created...
EPR written to file: note-672627063.epr
$ $GLOBUS_LOCATION/bin/create-note -s https://localhost:8443/wsrf/services/StickyNoteService -z self
new note created...
EPR written to file: note-807655197.epr

10.5 Now only the creator of the note has permission to edit it:

$ $GLOBUS_LOCATION/bin/write-note -e note-807655197.epr -z none cheese
Message written.
$ $GLOBUS_LOCATION/bin/show-note -e note-807655197.epr -z none
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-10T22:02:26.564Z</ns1:lastModified>
<ns1:noteContent>cheese</ns1:noteContent></ns1:StickyNoteResourceProperties>

10.6 Any user, including an anonymous user can read the note:

$GLOBUS_LOCATION/bin/show-note -e note-807655197.epr -z none -a
<ns1:StickyNoteResourceProperties xmlns:ns1="http://tutorial.globus.org/stickynote">
<ns1:lastModified>2005-01-10T22:02:36.564Z</ns1:lastModified>
<ns1:noteContent>cheese</ns1:noteContent></ns1:StickyNoteResourceProperties>

The -a option sets up the client invocation to be anonymous.

Exercise 10 complete!

- table of contents -

Credits

Robot pix by sam brown, explodingdog

If you reuse the "Build a Service" material please give credit to the Globus Alliance and the original authors:

  • Rachana Ananthakrishnan
  • Charles Bacon
  • Lisa Childers
  • Ben Clifford
  • Jarek Gawor
  • Joe Insley

All tutorial code is open source and covered by the apache license. The official home of the tutorial is here. Questions regarding this tutorial can be directed to Lisa Childers.

Thanks much for your interest in GT4! Goodbye for now!

Top