Introduce comes with helper tools for serializing and deserializing objects.
The example below if the the cagrid transfer service. This example is deserializing an xml file into a TranasferServiceContextResourceProperties object.
Write the object out that we serialized above. This will use the QName of the schema element that this object adheres to and write the object out to the test.xml file.
Introduce generates a client API for the service which is exactly as described within the graphical editing environment (see Auto-boxing/Unboxing or Service Operations). This client API can be used in order to leverage this type of service from another application or service. The API contains four constructors which can use used, each of which is different depending on having a handle or just an address and the need for security to be used.
Once a client handle is constructed each of the operations which were created in the service are available as operations to this newly constructed client instance. Below is an example snippet of code which creates a new client handle to a service called "HelloWorld" and calls the "echo" operation.
The example below shows how a client can print a more detailed stack trace from an exception that is received from the service.
If the Introduce service you are using supports the Notification resource framework option than the client will be enabled for subscribing to that service and listening for particular changes to resource properties. An example below shows how to perform the subscription:
In the above code we have an example client that is subscribing to a service to listen for changes to the TestResourceProperty resource property of the service. If the value of this resource property gets changed in the resource a notification will be sent out to all registered subscribers. Now we need to implement some code so that we receive these notifications. The introduce client generated for this service will automatically be able to listen for these changes. For us to be able to perform our own actions when we receive a notification we must overload the deliver operation in our client:
If you want to use a specific proxy file, other than the locations that Globus will automatically look. The ProxyUtil class can be used to load the proxy and create a GlobusCredential.
When an operation is added in the Introduce GDE and hte save button is clicked. Introduce will add the new stubbed method into the <service package>.service.<service name>Impl.java class. The developer is then responsible for implementing the method prior to deployment. For example the snippet below would be generated in the <service package>.service.<service name>Impl.java if the developer added an add operation to the service with Introduce that took in two integers and returned and integer.
The developer would then have to edit this method to implement the logic the service would execute on invokation.
In the service it is sometime required to get the identity of the caller. The identity is carried in the context of the message which was passed into the service. In order to get this information the following lines will be needed.
Service properties are the properties that the main service can be configured with that all service contexts deployed with that service can access. These properties are configured at deploy time and will likely need to be accessed in the service. Below is example code for how to access these properties where the main service was named HelloWorld and the name of the service property was testing.
When developing in the service context you must remember that the ServiceImpl class is just the service interface but the service should act on the addressed resource as there may be more than one resource instance being used by the service at a time. An example of how to get a handle to the resource which is being addressed by the current caller (exmaple from the counter service context as described in in the section on Utilizing the factory patter):
From the resource there will be getters and setters automatically generated for the resource properties of your service. For instance, if you look at the above example, you can see that the counter service has an "int" resource property. This enables introduce to automatically generated getters and setters for that resource property that the service implementer can use to access and modify values in the resource properties.
Sometimes it is necessary to set a security descriptor on an instance of a resource. This might be to protect resource instances so that only the creator of the resource can use it. In order to do this you have to make sure that when the context was generated in introduce that you had selected the "secure" resource framework option. This will enable the resource to have a setSecurityDescriptor() method enabled on it. When the resource is being created, as in the example below, the security descriptor should be generated and set on the resource. Once set it will be utilized on any access to the resource. Below is an example method from the counter service used in the utilizing a factory patter section. This is the method that Introduce generated to create the counter instance for the caller. You can see in this code that there is a place documenting how to set the security descriptor on the resource.
When the lifetime resource framework option is set on a service, the resources of that service will be able to have tier lifetime managed. From the client side a SetTerminationTime() or Destroy() grid call can be made to the service either setting the time to terminate or destroying the addressed resource. You can also call the set termination time directly on the resource inside the service. An example below, utilizing the same code as above will enable us create counters for users that will only be alive for 5 minutes:
In order to create instances of a resource something must tell the resource home of the service to create them. This is typically done using the factory pattern. A service or method is responsible for telling the resource to create a new instance of a resource type for the service. An example of the factory pattern in action is show it the image above. A factory service, the Counter Grid Service, contains a create operation which is exposed as a grid service method. When the client invokes this method the Counter Grid Service will locate the resource home of the Counter Context Grid Service and ask it to create a new resource instance. Once this resource intance is created the resource home will return a pointer or address to this resource instance called an EndPointReference (EPR). This EPR is then returned from the create method back to the client so that it has a pointer to these new resource that it can act on.
In order to act on this resource it will need to construct a client which can talk to the grid service which represents this resource type. The Counter Context Client will be constructed using the EPR to address the specific resource context for the service. Next the user would like to call the incrementCounter() operation on the Counter Context Grid Service. The client will make the call to the grid service and the service will then look at the EPR sent over by the client. The service will then take the EPR and give it to the resource home and ask for the resource instance back which is represented by the particular EPR. The the service will run it's logic to increment the counter, which in this case is represented as a resource property of the resource.
Below shows an example of the code that would be required to implement the incrementCounter() method in the CounterContextServiceImpl.java.
In Introduce 1.1, the "incrementCounter()" operation shown above is implemented in the service context implementation class (CounterContextImpl). The context operations implemented in this class are called each time a context client calls an operation. If the service context you are writing (as in the counter case above) maintains state for each context instance between calls, then that state can be accessed via the "getResourceHome().getAddressedResource()" call shown above. This call returns the specific instance of a CounterContextResource class that holds the state for the context client. In summary, the CounterContextImpl class is a stateless class that simply implements the context operations. The CounterContextImpl class uses the CounterContextResource instance specific to the EPR used by the client to access and maintain state for the client who is currently calling the context operation. In the above example, the state in the CounterContextResource is the current count retrieved in the impl via the "getIntValue" method (which is a service context property added via Introduce GDE).
Providing an update site is one way to enable Introduce users to be able to obtain your extensions or modified versions of Introduce. An update site is simply an address which points to a web folder that contains a software.xml file adhering to the schema. The software.xml file describes the packages available at the particular update site and the release zip which contains those packages.
When writing an extension, it is first important to chose which type of extension that you want to write. A description of the extension framework and the important extension types is documented here There are extensions that add functionality to introduce and there are extensions designed to add functionality to a service. The most common introduce extension type is the service extension. This type of extension is designed to add functionality to a service. The best place to learn about extension is just to look at one. Here are a list of the service extension that can be found in the cagrid repository that are great places to start.
Every extension revolves around its extension.xml. The extension.xml describes what type the extension is, and what are the classes that need to be invoked in order to execute the extension.
In order to bundle up an extension so that it can be deployed to introduce the extension directory structure must be laid out in the following way:
- <top of zip file>
- lib (any jars required to "execute" your extension
- <your extension name (not display name)>
- extension.xml (the extension.xml describing your extension)
Once this layout is complete, the next step is to create the extension zip file. In order to do this, all that is required is to create a zip file with no top level directory and the lib dir and <your extension name> dir as the only directories in the zip. So for instance, if the zip file were extracted there would be the two directories with no top level directory. Now that you have bundled up your extension so that it can be delivered and used by Introduce.