Difference between revisions of "The Development Cycle"
(→The Deployment Descriptor) |
(→Building & Deploying) |
||
(18 intermediate revisions by 2 users not shown) | |||
Line 59: | Line 59: | ||
* Another <code>Software</code> element identifies the secondary build artefact of the service, its own stubs. The <code>Main</code> package depends always upon its stubs and this dependency must be explicit. | * Another <code>Software</code> element identifies the secondary build artefact of the service, its own stubs. The <code>Main</code> package depends always upon its stubs and this dependency must be explicit. | ||
− | + | [[Image:blue.jpg]] If the notion of build artefact is not obvious to you, bide your time for a little longer or [[#Building & Deploying |jump ahead]]. | |
Save the profile in a file called '''profile.xml''' and place it in the <code>etc</code> folder under the service location: | Save the profile in a file called '''profile.xml''' and place it in the <code>etc</code> folder under the service location: | ||
Line 76: | Line 76: | ||
== A Tiny JNDI == | == A Tiny JNDI == | ||
− | Next we move to the configuration of the code itself, for which we use [http://java.sun.com/products/jndi/ JNDI] technology. Again, the details and granularity of JNDI configuration are discussed [[ | + | Next we move to the configuration of the code itself, for which we use [http://java.sun.com/products/jndi/ JNDI] technology. Again, the details and granularity of JNDI configuration are discussed [[Configuration_Components#The_JNDI_Configuration|elsewhere]]. Here we simply notice that, while the JNDI configuration might contain any information which the code needs to consult at runtime, the gCF raises precise requirements against the existence and shape of some of that information. In particular, the configuration should '''at least''' contain the following information: |
<pre> | <pre> | ||
Line 90: | Line 90: | ||
− | Essentially, the configuration must have a <code>service</code> element entirely dedicated to <code>SampleService</code>, rather than more specific components yet to come. Furthermore, the element | + | [[Image:red.jpg]] Essentially, the configuration must have a <code>service</code> element entirely dedicated to <code>SampleService</code>, rather than more specific components yet to come. Furthermore, the element: |
− | + | [[Image:red.jpg]] '''must''' specify the <code>name</code> of the service. This is not particularly constrained but must distinguish <code>SampleService</code> from any other which may be deployed on the same gHN. As we already do with Java and URI namespaces, we can approach uniqueness through a hierarchical structure, here of ''/''-separated strings. In our case, the name <code>acme/sample</code> will suffice as a unique identifier. | |
− | + | [[Image:red.jpg]] '''must''' include an <code>environment</code> element which specifies the absolute path to the configuration folder of <code>SampleService</code>. Contrary to expectations, this is not our <code>etc</code> folder under the service location. It is in fact another configuration folder allocated to <code>SampleService</code> at the point of service deployment. This will become clearer [[The Development Cycle#Building & Deploying the Service|later]]. For the time being, we use the wildcard <code>@config.dir@</code> to abstract over the precise location of this yet-to-be folder, and leave to gCore the task of resolving it at the point of deployment. | |
− | + | [[Image:blue.jpg]] The wildcard <code>@config.dir@</code> is surely convenient at this stage. It is in fact necessary for any real gCube service. This is because the service may be dynamically deployed on any gHN and the absolute path of the configuration folder depends on the file system of that gHN. During service development, that location is an absolute unknown. | |
So far, there is little in our configuration which is specific to <code>SampleService</code>. Indeed, the JNDI above can be considered as boilerplate for any gCube service. We will see shortly how to inject more information into the JNDI configuration, but until that moment this minimal configuration will suffice. | So far, there is little in our configuration which is specific to <code>SampleService</code>. Indeed, the JNDI above can be considered as boilerplate for any gCube service. We will see shortly how to inject more information into the JNDI configuration, but until that moment this minimal configuration will suffice. | ||
Line 106: | Line 106: | ||
|--etc | |--etc | ||
|---profile.xml | |---profile.xml | ||
− | |---deploy-jndi-config.xml | + | |---deploy-jndi-config.xml [new] |
|--src | |--src | ||
|--schema | |--schema | ||
Line 146: | Line 146: | ||
* the <code>name</code> of the WSDL definitions does not play much of a role during development. Use anything that looks appropriate. Given that there is a one-to-one correspondence between WSDLs and port-types, we suggest you decide upon some name for the port-type and use it here. | * the <code>name</code> of the WSDL definitions does not play much of a role during development. Use anything that looks appropriate. Given that there is a one-to-one correspondence between WSDLs and port-types, we suggest you decide upon some name for the port-type and use it here. | ||
− | + | [[Image:red.jpg]] the <code>name</code> of the <code>portType</code> element also refers to the port-type, though this ''has'' a very important role during the build of the service. In particular, it '''must''' end in <code>'portType'</code>. We suggest you append this suffix to the name of the port-type you have chosen one bullet above. | |
The WSDL of our first port-type illustrates these conventions: | The WSDL of our first port-type illustrates these conventions: | ||
Line 191: | Line 191: | ||
Hard to imagine a simpler port-type, really. Now the last important convention: | Hard to imagine a simpler port-type, really. Now the last important convention: | ||
− | + | [[Image:red.jpg]] The file in which you save the interface '''must''' have the same name as the port-type. | |
Accordingly, save the interface in a file '''Stateless.wsdl''' in the <code>schema</code> folder under the service location: | Accordingly, save the interface in a file '''Stateless.wsdl''' in the <code>schema</code> folder under the service location: | ||
Line 204: | Line 204: | ||
| | | | ||
|--schema | |--schema | ||
− | |---Stateless.wsdl | + | |---Stateless.wsdl [new] |
| | | | ||
|-Dependencies | |-Dependencies | ||
Line 235: | Line 235: | ||
* let the inherited code know how to act on your behalf by implementing <code>getJNDIName()</code> and returning the name of the <code>service</code> element dedicated to the service in the [[#A Tiny JNDI |JNDI configuration file]]. | * let the inherited code know how to act on your behalf by implementing <code>getJNDIName()</code> and returning the name of the <code>service</code> element dedicated to the service in the [[#A Tiny JNDI |JNDI configuration file]]. | ||
− | + | ||
+ | [[Image:blue.jpg]] This is a first instance of the ''template pattern'': you complete the interface by exposing service-specific information which the inherited code uses to customise the work it carries out for you. It is simple, and it's used a lot within gCF. | ||
+ | |||
The rest of the implementation could use the service context by instantiating <code>ServiceContext</code>. For efficiency and data sharing, however, it is more appealing to force the use of a single <code>ServiceContext</code> object throughout the implementation. A simple way of doing this is as follows: | The rest of the implementation could use the service context by instantiating <code>ServiceContext</code>. For efficiency and data sharing, however, it is more appealing to force the use of a single <code>ServiceContext</code> object throughout the implementation. A simple way of doing this is as follows: | ||
Line 254: | Line 256: | ||
* the object is available on demand (see <code>getContext()</code>). | * the object is available on demand (see <code>getContext()</code>). | ||
− | + | ||
+ | [[Image:blue.jpg]] This is an instance of the ''singleton pattern'' and gCF cannot 'force' it as a contract upon your code without taking away too much of its simplicity. It just recommends it. | ||
+ | |||
We will go back to the service context in the rest of the Primer, as its feature will be needed. Even If we didn't, it would already carry out important work during service startup, as you will see [[#Starting the gHN|later on]]. More information on the role and features of a service context in gCF can be found [[Contexts#Service Contexts|here]]. | We will go back to the service context in the rest of the Primer, as its feature will be needed. Even If we didn't, it would already carry out important work during service startup, as you will see [[#Starting the gHN|later on]]. More information on the role and features of a service context in gCF can be found [[Contexts#Service Contexts|here]]. | ||
Line 278: | Line 282: | ||
but do please note: | but do please note: | ||
− | + | [[Image:red.jpg]] the implementation extends <code>GCUBEPortType</code>, which ensures the correct initialisation of the port-type at service startup. All the port-type implementations of your services ought to do the same. | |
− | + | [[Image:blue.jpg]] the name and class of the service as defined in the [[#My First Profile|service profile]] can be obtained from the service context, as can any piece of information in the profile that proves useful for dynamic introspection. | |
By now the service location should look as follows: | By now the service location should look as follows: | ||
Line 294: | Line 298: | ||
|----acme | |----acme | ||
|-----sample | |-----sample | ||
− | |------ServiceContext.java | + | |------ServiceContext.java [new] |
− | |------stateless | + | |------stateless |
− | |-------Stateless.java | + | |-------Stateless.java [new] |
| | | | ||
|--schema | |--schema | ||
Line 315: | Line 319: | ||
Now, assume for a moment we have somehow generated the stub code. Would we be done then? Well, we would have to compile both stub and service code, of course. And then we would have to hand things over to the gHN, i.e. ''deploy'' them in key locations under the gCore location. Code generation, packaging, and deployment are the main steps of an overall process of ''build'' which makes the service operational within the gHN. | Now, assume for a moment we have somehow generated the stub code. Would we be done then? Well, we would have to compile both stub and service code, of course. And then we would have to hand things over to the gHN, i.e. ''deploy'' them in key locations under the gCore location. Code generation, packaging, and deployment are the main steps of an overall process of ''build'' which makes the service operational within the gHN. | ||
− | In web service development, the build process is far from trivial and requires the coordinated use of multiple utilities for code generation, compilation, packaging, and deployment. Fortunately, we can simplify the execution of the build process using [http://ant.apache.org/ Ant] as our build technology. Ant lets us define the build process declaratively in a ''buildfile'', and to outsource its execution to the generic ANT engine. Even better news, there is a pre-defined buildfile which ships with gCore (<code>$GLOBUS_LOCATION/share/gcore_tools/ | + | In web service development, the build process is far from trivial and requires the coordinated use of multiple utilities for code generation, compilation, packaging, and deployment. Fortunately, we can simplify the execution of the build process using [http://ant.apache.org/ Ant] as our build technology. Ant lets us define the build process declaratively in a ''buildfile'', and to outsource its execution to the generic ANT engine. Even better news, there is a pre-defined buildfile which ships with gCore (<code>$GLOBUS_LOCATION/share/gcore_tools/sample-build.xml</code>) and has been designed to work seamlessly for any gCube service which follows the structure recommended [[The_ACME_Project#Structuring the Service|above]]. |
All you need to do is configure it, of course. | All you need to do is configure it, of course. | ||
Line 375: | Line 379: | ||
|---profile.xml | |---profile.xml | ||
|---deploy-jndi-config.xml | |---deploy-jndi-config.xml | ||
− | |---deploy-server.wsdd | + | |---deploy-server.wsdd [new] |
| | | | ||
|--src | |--src | ||
Line 435: | Line 439: | ||
|---deploy-jndi-config.xml | |---deploy-jndi-config.xml | ||
|---deploy-server.wsdd | |---deploy-server.wsdd | ||
− | |---build.properties | + | |---build.properties [new] |
| | | | ||
|--src | |--src | ||
Line 468: | Line 472: | ||
**<code>${BUILD_LOCATION}/Dependencies/SampleService</code>, where it can be found as a compile-time dependency of the service. This is why we specified the custom dependency location in the [[#The Build Properties|build properties]]. | **<code>${BUILD_LOCATION}/Dependencies/SampleService</code>, where it can be found as a compile-time dependency of the service. This is why we specified the custom dependency location in the [[#The Build Properties|build properties]]. | ||
− | + | [[Image:blue.jpg]] The build process generates also three additional port-type interfaces from the original one: <code>Stateless_bindings.wsdl</code>, <code>Stateless_flattened.wsdl</code>, and <code>Stateless_service.wsdl</code>. For the time being, you do not need to be overly concerned with these, but simply notice that the latter, <code>Stateless_service.wsdl</code>, is the port-type interface pointed at from the [[#The Deployment Descriptor|deployment descriptor]] of the service. | |
− | + | [[Image:blue.jpg]] The build process creates a ''build'' folder under the build location, clearing it if it already exists. It then uses this folder to organise all the elements required for the build (code, configuration, interfaces). It copies most elements from the service location, of course, but it complements them with other elements which are cross-service and ship with gCore. In most cases, you should not concern yourself with the contents of the ''build'' folder but it is good to know who creates it and why. | |
− | + | [[Image:blue.jpg]] Sometimes you might want to generate the stubs or go as far as to package them, but not deploy them. Say you want to make sure they are generated correctly, and perhaps have a peek inside the jar file, but are not ready to replace the stub library previously deployed. In these cases, you can invoke the <code>buildStubs</code> or the <code>jarStubs</code> targets: | |
ant -f SampleService/build.xml ''buildStubs'' ...generates and compile stubs but does not package them... | ant -f SampleService/build.xml ''buildStubs'' ...generates and compile stubs but does not package them... | ||
ant -f SampleService/build.xml ''jarStubs'' ...generates and packages stubs but does not deploy them... | ant -f SampleService/build.xml ''jarStubs'' ...generates and packages stubs but does not deploy them... | ||
− | + | [[Image:red.jpg]] When we will [[#Building & Deploying the Service|build the service]], the buildfile will automatically resolve the dependency of <code>SampleService</code> to its own stubs from the custom dependency folder. Eclipse needs instead to be told explicitly. For this, you should add <code>$BUILD__LOCATION/Dependencies/SampleService/org.acme.sample.stubs.jar</code> to the build path of your Eclipse project (''Properties -> Java Build Path -> Libraries -> Add Jars...''). The same is true for ''any'' other custom dependency, of course. For stubs, however, the good news is that we do not have to manually copy the library into the custom dependency folder. | |
− | + | [[Image:blue.jpg]] When need arises, you can undeploy the stubs by typing: | |
ant -f SampleService/build.xml undeployStubs | ant -f SampleService/build.xml undeployStubs | ||
Line 499: | Line 503: | ||
− | + | [[Image:blue.jpg]] As with the stubs (and in fact more commonly), sometimes you might want to compile the service implementation, perhaps package it, and even go as far as to produce the GAR file, but not deploy it. In these cases, you can invoke the <code>buildService</code>, <code>jarService</code>, or <code>garService</code> targets: | |
ant -f SampleService/build.xml ''buildService'' ...compiles the service implementation, but does not package it | ant -f SampleService/build.xml ''buildService'' ...compiles the service implementation, but does not package it | ||
Line 509: | Line 513: | ||
gcore-deploy-service SampleService/org.acme.sample.gar | gcore-deploy-service SampleService/org.acme.sample.gar | ||
− | + | [[Image:blue.jpg]] When need arises, you can also undeploy the service by typing: | |
ant -f SampleService/build.xml undeployService. | ant -f SampleService/build.xml undeployService. | ||
Line 674: | Line 678: | ||
|-------Stateless.java | |-------Stateless.java | ||
|------tests | |------tests | ||
− | |-------StatelessTest.java | + | |-------StatelessTest.java [new] |
| | | | ||
|--schema | |--schema | ||
Line 688: | Line 692: | ||
Next you have to configure the ''run configuration'' of your client by specifying the gCore location both as the value of the <code>GLOBUS_LOCATION</code> environment variable and as a classpath element. The most reusable way of approaching this task is to: | Next you have to configure the ''run configuration'' of your client by specifying the gCore location both as the value of the <code>GLOBUS_LOCATION</code> environment variable and as a classpath element. The most reusable way of approaching this task is to: | ||
− | * define once and for all a ''string variable'' <code>GLOBUS_LOCATION</code> at the workspace level and set it to the gCore location | + | * define once and for all a ''string variable'' <code>GLOBUS_LOCATION</code> at the workspace level and set it to the gCore location. Menu-wise: |
− | + | ||
− | + | ||
+ | <pre>Preferences->Run/Debug->String Substitution->New...</pre> | ||
+ | |||
+ | * add an environment variable <code>GLOBUS_LOCATION</code> to the run configuration of ''all'' your clients and set it to the string variable just defined. Menu-wise: | ||
+ | |||
+ | <pre>Open Run Dialog...->Environment->New...->Variables</pre> | ||
+ | |||
+ | * add the value of the string variable as a classpath element to the run configuration of ''all'' your clients. Menu-wise: | ||
+ | |||
+ | <pre>Open Run Dialog...->Classpath->User Entries->Advanced...->Add Variable String</pre> | ||
Whether you are running the client from the shell or from within Eclipse, the output to expect (under default gCore settings) is the following: | Whether you are running the client from the shell or from within Eclipse, the output to expect (under default gCore settings) is the following: | ||
Line 698: | Line 709: | ||
Hello John Doe, you have invoked service SampleService (Samples) | Hello John Doe, you have invoked service SampleService (Samples) | ||
</pre> | </pre> | ||
+ | |||
+ | |||
+ | == Using the Eclipse IDE for for debugging and/or profiling your service == | ||
+ | |||
+ | === Degugging === | ||
+ | Starting the gHN with: | ||
+ | |||
+ | gcore-start-container-debug | ||
+ | |||
+ | allows you to connect your eclipse debugger with the container following a few easy steps. | ||
+ | |||
+ | * Open your Eclipse IDE and choose from the main menu: Run -> Debug Configurations | ||
+ | * In the "Debug Configurations" window add a new configuration for debugging a "Remote Java Application". | ||
+ | ** Give the configuration an appropriate name | ||
+ | ** Have it pointing to the project you wish to debug | ||
+ | ** Set the connection type to "Standard (Soket attach)" | ||
+ | ** Set the host to be the host name where the server runs on and port 8787 | ||
+ | ** Press Debug | ||
+ | * Open the Debug perspective and you are ready to start the Debugging | ||
+ | [[Image:DbgWin.png|frame|none||Example of Debug Configuration]] | ||
+ | |||
+ | === Profiling === | ||
+ | Starting the gHN with: | ||
+ | |||
+ | gcore-start-container-profile | ||
+ | |||
+ | allows you to connect your eclipse profiler with the container. | ||
+ | |||
+ | The process described process is tested with Eclipse Ganymede and the [http://www.eclipse.org/tptp/ "Eclipse Test and Performance Tools Platform"] (TPTP). After installing the TPTP plugin you have to follow the path of profiling a remote java application. | ||
+ | |||
+ | First, you should manually start the agent that will be picking up messages from the java VM. This agent will have to be in the same system where the container runs. This is done with a command like: | ||
+ | |||
+ | /home/myusername/.Programs/eclipse/plugins/org.eclipse.tptp.platform.ac.linux_ia32_4.4.100.v200902101418/agent_controller/bin/ACStart.sh | ||
+ | |||
+ | Be sure that there is only one ACServer running, the one created by your call to ACStart.sh script. | ||
+ | You can find more info on setting up this agent [http://help.eclipse.org/help32/index.jsp?topic=/org.eclipse.tptp.platform.doc.user/tasks/teprofsa.htm here] | ||
+ | |||
+ | |||
+ | As soon ACServer and gCore are running | ||
+ | * Open your Eclipse IDE and choose from the main menu: Run -> Profile Configurations | ||
+ | * In the "Profile Configurations" window add a new configuration from category "Attach to Agent". | ||
+ | ** Give the configuration an appropriate name | ||
+ | ** Have it pointing to the host you run the container along with the agent. | ||
+ | ** Set port 10003 (unless you pick a different port from the agents configurations) | ||
+ | ** In the Agents tab select what you want to profile | ||
+ | ** Press Profile | ||
+ | The Profiling perspective should open and you should be able to profile your service. | ||
+ | |||
+ | [[Image:ProfHost.png|frame|none||Example of Profiling Configuration]] | ||
+ | [[Image:ProfAgent.png|frame|none||Set what you want to profile]] | ||
+ | [[Image:ProfWork.png|frame|none||Profiling in action]] |
Latest revision as of 17:14, 21 April 2010
Let us go through a quick tour of development with SampleService.
Contents
My First Profile
A good starting point with service development is its configuration. And a good starting point when configuring a service is the definition of its profile. The role and structure of gCube service profiles is described in detail elsewhere. Here we just show one of the smallest and yet perfectly well-formed profile a service may have:
<?xml version="1.0" encoding="UTF-8"?> <Resource xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"> <ID/> <Type>Service</Type> <Profile> <Description>A very simple gCube Service</Description> <Class>Samples</Class> <Name>SampleService</Name> <Version>1.0.0</Version> <Packages> <Main> <Name>SampleService-service</Name> <Version>1.0.0</Version> <Dependencies> <Dependency> <Service> <Class>Samples</Class> <Name>SampleService</Name> <Version>1.0.0</Version> </Service> <Package>SampleService-stubs</Package> <Version>1.0.0</Version> <Scope level="GHN"/> <Optional>false</Optional> </Dependency> </Dependencies> <GARArchive>org.acme.sample.gar</GARArchive> <PortType> <Name>acme/sample/stateless</Name> <WSDL/> </PortType> </Main> <Software> <Description>Describes port-type stubs</Description> <Name>SampleService-stubs</Name> <Version>1.0.0</Version> <Files><File>org.acme.sample.stubs.jar</File></Files> </Software> </Packages> </Profile> </Resource>
By necessity, this profile reveals things yet to come. For the time being, be happy with a conceptual summary:
-
SampleService
is a a particular type of gCubeResource
, one of typeService
. Like all gCube Resources has anID
, which it receives when it is formally registered with the infrastructure. Until then,SampleService
does not have a proper identifier. No worries though, a temporary one will be generated by gCF at runtime.
- The profile of
SampleService
specifies itsName
and specifies its membership to aClass
of functionally related gCube services. Classes are not pre-defined, and here we settle onSamples
. A free-formDescription
and aVersion
complement the preliminary description ofSampleService
.
-
SampleService
is physically comprised ofPackages
. AMain
package identifies the main artefact to emerge from the build of the service, aGARArchive
, and provides information about the service port-types, including their WSDLs (here omitted for mercy). The profile unveils our initial plans for a single stateless port-type, which we name by identifying the relative part of the URI at which it will be accessible on the gHN (more on this later).
- Another
Software
element identifies the secondary build artefact of the service, its own stubs. TheMain
package depends always upon its stubs and this dependency must be explicit.
If the notion of build artefact is not obvious to you, bide your time for a little longer or jump ahead.
Save the profile in a file called profile.xml and place it in the etc
folder under the service location:
|-SampleService |--etc |---profile.xml |--src |--schema | |-Dependencies |--SampleService
A Tiny JNDI
Next we move to the configuration of the code itself, for which we use JNDI technology. Again, the details and granularity of JNDI configuration are discussed elsewhere. Here we simply notice that, while the JNDI configuration might contain any information which the code needs to consult at runtime, the gCF raises precise requirements against the existence and shape of some of that information. In particular, the configuration should at least contain the following information:
<?xml version="1.0" encoding="UTF-8"?> <jndiConfig xmlns="http://wsrf.globus.org/jndi/config"> <service name="acme/sample"> <environment name="configDir" value="@config.dir@" type="java.lang.String" override="false" /> </service> </jndiConfig>
Essentially, the configuration must have a service
element entirely dedicated to SampleService
, rather than more specific components yet to come. Furthermore, the element:
must specify the name
of the service. This is not particularly constrained but must distinguish SampleService
from any other which may be deployed on the same gHN. As we already do with Java and URI namespaces, we can approach uniqueness through a hierarchical structure, here of /-separated strings. In our case, the name acme/sample
will suffice as a unique identifier.
must include an environment
element which specifies the absolute path to the configuration folder of SampleService
. Contrary to expectations, this is not our etc
folder under the service location. It is in fact another configuration folder allocated to SampleService
at the point of service deployment. This will become clearer later. For the time being, we use the wildcard @config.dir@
to abstract over the precise location of this yet-to-be folder, and leave to gCore the task of resolving it at the point of deployment.
The wildcard @config.dir@
is surely convenient at this stage. It is in fact necessary for any real gCube service. This is because the service may be dynamically deployed on any gHN and the absolute path of the configuration folder depends on the file system of that gHN. During service development, that location is an absolute unknown.
So far, there is little in our configuration which is specific to SampleService
. Indeed, the JNDI above can be considered as boilerplate for any gCube service. We will see shortly how to inject more information into the JNDI configuration, but until that moment this minimal configuration will suffice.
Save the configuration in a file called deploy-jndi-config.xml and place it in the etc
folder under the service location:
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml [new] |--src |--schema | |-Dependencies |--SampleService
A PortType Interface
Enough configuration for now. Let us move to the definition of the interface for the first port-type we plan to add to SampleService
.
The general expectation is that all your interfaces will have this high-level structure (up to the choice of namespace prefixes and other stylistic matters of course):
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns:tns="<PORT-TYPE NAMESPACE>" targetNamespace="<PORT-TYPE NAMESPACE>" name="<PORT-TYPE NAME>" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <types> <xsd:schema targetNamespace="....."> <!-- REQUEST & RESPONSE TYPE DEFINITIONS --> </xsd:schema> </types> <!-- MESSAGE DEFINITIONS --> <portType name="<PORT_TYPE NAME>portType"> <!--OPERATIONS DEFINITIONS --> </portType> </definitions>
All pretty standard WSDL, of course. And yet notice what follows:
- we dedicate the WSDL to a single port-type (even though multiple port-types are admitted by the WSDL specs). This simplifies our building configuration and suggests than any form of sharing across port-types (type definition, messages, faults, etc.) be achieved through imports/includes (as it really should).
- the
name
of the WSDL definitions does not play much of a role during development. Use anything that looks appropriate. Given that there is a one-to-one correspondence between WSDLs and port-types, we suggest you decide upon some name for the port-type and use it here.
the name
of the portType
element also refers to the port-type, though this has a very important role during the build of the service. In particular, it must end in 'portType'
. We suggest you append this suffix to the name of the port-type you have chosen one bullet above.
The WSDL of our first port-type illustrates these conventions:
<?xml version="1.0" encoding="UTF-8"?> <definitions xmlns:tns="http://acme.org/sample" name="Stateless" targetNamespace="http://acme.org/sample" xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" > <types> <xsd:schema targetNamespace="http://acme.org/sample"> <xsd:element name="about" type="xsd:string"/> <xsd:element name="aboutResponse" type="xsd:string"/> </xsd:schema> </types> <message name="aboutInputMessage"> <part name="request" element="tns:about"/> </message> <message name="aboutOutputMessage"> <part name="response" element="tns:aboutResponse"/> </message> <portType name="StatelessPortType"> <operation name="about"> <input message="tns:aboutInputMessage"/> <output message="tns:aboutOutputMessage"/> </operation> </portType> </definitions>
As you can see:
- we have used the namespace
http://acme.org/sample
for our port-type. - we have settled on the name
Stateless
for it, and used it twice. The first time to name the whole interface definition and a second time in the nameStatelessPortType
of theportType
element. - we have defined a single operation
about
forStateless
, which takes and returns a string. The idea is to expect the name of the caller as input and produce some information about the port-type as output.
Hard to imagine a simpler port-type, really. Now the last important convention:
The file in which you save the interface must have the same name as the port-type.
Accordingly, save the interface in a file Stateless.wsdl in the schema
folder under the service location:
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml | |--src | |--schema |---Stateless.wsdl [new] | |-Dependencies |--SampleService
A Simple Implementation
So far, the development process has been dominated by simple but numerous conventions upon the use of not necessarily simple but equally numerous formats. Frankly, all very unexciting and error-prone. Welcome (or welcome back) to the grim reality of web service development. gCF cannot take much of this pain away, unfortunately. In fact, gCF has only added to those conventions with no visible returns. Delving into the implementation of SampleService
hopefully will give you the first glimpse of rewards to come.
A natural place to start the implementation is right at its centre, with a service context. Among other things, a service context manages configuration, scoping, security, and lifetime on behalf of the rest of the code. In gCF, you can have your own wih a few lines of code:
package org.acme.sample; import ... public class ServiceContext extends GCUBEServiceContext { /** {@inheritDoc} */ protected String getJNDIName() { return "acme/sample"; } }
As you can see, it is enough to:
- inherit your service context from
GCUBEServiceContext
. - let the inherited code know how to act on your behalf by implementing
getJNDIName()
and returning the name of theservice
element dedicated to the service in the JNDI configuration file.
This is a first instance of the template pattern: you complete the interface by exposing service-specific information which the inherited code uses to customise the work it carries out for you. It is simple, and it's used a lot within gCF.
The rest of the implementation could use the service context by instantiating ServiceContext
. For efficiency and data sharing, however, it is more appealing to force the use of a single ServiceContext
object throughout the implementation. A simple way of doing this is as follows:
/** Single context instance, created eagerly */ private static ServiceContext singleton = new ServiceContext(); /** Prevents accidental creation of more instances */ private ServiceContext(){}; /** Returns cached instance */ public static ServiceContext getContext() {return singleton;}
- a single context object is created the first time the class is loaded (see
singleton
field declaration). - no other such object can be created by clients (see private constructor).
- the object is available on demand (see
getContext()
).
This is an instance of the singleton pattern and gCF cannot 'force' it as a contract upon your code without taking away too much of its simplicity. It just recommends it.
We will go back to the service context in the rest of the Primer, as its feature will be needed. Even If we didn't, it would already carry out important work during service startup, as you will see later on. More information on the role and features of a service context in gCF can be found here.
Next, the implementation of the port-type interface. This is also straightforward:
package org.acme.sample.stateless; import ... public class Stateless extends GCUBEPortType { /** {@inheritDoc} */ protected GCUBEServiceContext getServiceContext() {return ServiceContext.getContext();} public String about(String name) { return ("Hello " + name + ", you have invoked service ")+this.getServiceContext().getName() + " ("+this.getServiceContext().getServiceClass() + ")"; } }
but do please note:
the implementation extends GCUBEPortType
, which ensures the correct initialisation of the port-type at service startup. All the port-type implementations of your services ought to do the same.
the name and class of the service as defined in the service profile can be obtained from the service context, as can any piece of information in the profile that proves useful for dynamic introspection.
By now the service location should look as follows:
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml | |--src |---org |----acme |-----sample |------ServiceContext.java [new] |------stateless |-------Stateless.java [new] | |--schema |---Stateless.wsdl | |-Dependencies |--SampleService
Building & Deploying
The definition of the service is nearly complete: most configuration, interfaces, and implementation components are in place.
Some Java code is still missing however. This is the code that we wish to generate automatically from the port-type interface so as to hide away the low-level details of communication between service and its clients. In a word, we need stubs for our service.
Stubs are key offerings for clients, which can bind to instances of our service and invoke its methods with the simplicity (but not costs!) of conventional method calls. Stubs are key for the implementation of the service as well, which can also use them as high-level models of inputs and outputs. Of course, this is not the case for our Stateless
port-type, which manipulates plain strings and so does not need to depend on its own stubs. The port-types we will consider later on, however, will illustrate this dependency more obviously.
Now, assume for a moment we have somehow generated the stub code. Would we be done then? Well, we would have to compile both stub and service code, of course. And then we would have to hand things over to the gHN, i.e. deploy them in key locations under the gCore location. Code generation, packaging, and deployment are the main steps of an overall process of build which makes the service operational within the gHN.
In web service development, the build process is far from trivial and requires the coordinated use of multiple utilities for code generation, compilation, packaging, and deployment. Fortunately, we can simplify the execution of the build process using Ant as our build technology. Ant lets us define the build process declaratively in a buildfile, and to outsource its execution to the generic ANT engine. Even better news, there is a pre-defined buildfile which ships with gCore ($GLOBUS_LOCATION/share/gcore_tools/sample-build.xml
) and has been designed to work seamlessly for any gCube service which follows the structure recommended above.
All you need to do is configure it, of course.
The Deployment Descriptor
A first piece of configuration to produce concerns deployment. We configure deployment so as to tell the gHN how to manage a Running Instance of the service. In particular, we need to tell the gHN how to instantiate the port-type implementations and how to recognise and correctly dispatch calls to them. The result is a deployment descriptor. It will be embedded in the build artefacts to be deployed in the gHN.
Here is a deployment descriptor for SampleService
:
<?xml version="1.0" encoding="UTF-8"?> <deployment name="defaultServerConfig" xmlns="http://xml.apache.org/axis/wsdd/" xmlns:java="http://xml.apache.org/axis/wsdd/providers/java" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <service name="acme/sample/stateless" provider="Handler" use="literal" style="document"> <parameter name="className" value="org.acme.sample.stateless.Stateless"/> <wsdlFile>share/schema/org.acme.sample/Stateless_service.wsdl</wsdlFile> <parameter name="allowedMethods" value="*"/> <parameter name="handlerClass" value="org.globus.axis.providers.RPCProvider"/> <parameter name="scope" value="Application"/> <parameter name="loadOnStartup" value="true"/> </service> </deployment>
The deployment descriptor must
include a service
element for each port-type.
The following is worth noticing:
- the
name
attribute tells the gHN how to distinguish calls to the port-type from calls to other port-types. It is the relative part of the URI at which the port-type is accessible, its endpoint. In particular, the port-type endpoint is formed by concatenating the value of thename
parameter, i.e. its relative endpoint, with the endpoint of the gHN itself. Given that the endpoint of the gHN has always the following form:
http:// <hostname>:<port>/wsrf/services
then the endpoint of the Stateless
port-type will be the following:
http://<hostname>:<port>/wsrf/services/acme/sample/stateless
- the
className
parameter names the class which implements the port-type. This is used by the gHN to create an instance of that class and forward to it all the calls for the port-type that it receives.
- the
wsdlFile
parameter specifies the location of the port-type interface after deployment. We will be able to explain its value later on.
- the
scope
parameter tells the gHN how many port-type instances it ought to create in order to serve requests. A value ofApplication
indicates that we wish all requests to be handled by single object of the implementation class. Alternatively, a value ofRequest
indicates that we want a new port-type instance for each request.
- the
loadOnStartup
parameter indicates that the gHN should create the port-type instance as soon as it starts up, rather than when it receives the first request for it.
Now, notice the following rules:
the name
of the service
element must match that in the PortType
element of the service profile.
loadOnStartup
must be set to true
for at least one port-type.
Save the deployment descriptor in a file called deploy-server.wsdd under the etc
folder of the service location:
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml |---deploy-server.wsdd [new] | |--src |---org |----acme |-----sample |------ServiceContext.java |------stateless |-------Stateless.java | |--schema |---Stateless.wsdl | |-Dependencies |--SampleService
The Build Properties
Once you have copied the pre-defined buildfile ($GLOBUS_LOCATION/share/gcore_tools/build-sample.xml
) into the service location and renamed it to the classic build.xml
, you have to configure it.
First of all, the buildfile needs to know the absolute path to the location from which you will build the artefacts, the build location. This is expected as the value of the environment variable BUILD_LOCATION
. For convenience, we use the location of the Eclipse workspace as our build location:
export BUILD_LOCATION = ...workspace location...
Second, the buildfile requires you to specify a small set of build properties:
package = org.acme.sample lib.dir = Dependencies/SampleService wsdl.1 = Stateless namespace.1=http://acme.org/sample
where:
-
package
is the 'root package' of the service implementation, i.e. the first package in the implementation hierarchy which identifies the implementation unambiguously. The buildfile uses this property to guarantee unique names to the artefacts it produces. As we shall see, this guarantees the they can be safely deployed without any clashes with other service implementations. Let us assume thatorg.acme.sample
identifies uniquely the implementation ofSampleService
within ACME and, through Java package conventions, pretty much everywhere.
-
lib.dir
is the path to the custom dependency folder, relative to the build location. The buildfile needs to know where are your custom dependencies so as to put them on the classpath during compilation.
-
wsdl.1
is the name of the file which containes the WSDL interface of the first port-type. The buildfile uses this property to locate the interface under the service location, process it, and generate stubs from it.
-
namespace.1
is the namespace of the port-type, as specified in its WSDL. The buildfile uses this property to determine the Java package of the stubs generated from the WSDL interface.
If you had many port-types, you would list them all in arbitrary order (wsdl.2,wsdl.3,...). If you had different namespaces across WSDLs and/or auxiliary XSDs, you would also list them all in arbitrary order.
By default, all namespaces are mapped to ${package}/stubs
. In our case, they would be placed org.gcube.sample.stubs
. If you wish to map different namespaces onto different packages, you can do so with optional properties package.<n>
. The buildfile will consider its value when generating stubs for interface elements in the namspace namespace.<n>
. For example, if you specify the property:
package.1=stateless
then the buildfile would create stubs for the Stateless
port-type under org.ample.stubs.stateless
.
Save the configuration in a build.properties file and place it in the etc
folder under the service location.
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml |---deploy-server.wsdd |---build.properties [new] | |--src |---org |----acme |-----sample |------ServiceContext.java |------stateless |-------Stateless.java | |--schema |---Stateless.wsdl | | |--build.xml | |-Dependencies |--SampleService
Building & Deploying Stubs
Build time. From the build location, type:
ant -f SampleService/build.xml stubs
This invokes ANT and tells it to process the buildfile of SampleService
, specifically the instructions for building the stub library which are grouped under the target stubs
.
If all goes according to plans, the buildfile:
- generates stubs source code from the port-type interfaces, compiles it, and packages it in a
org.acme.sample.stubs.jar
file. - deploys
org.acme.sample.stubs.jar
in two locations:${GLOBUS_LOCATION}/lib
under the gCore location, where it will be found as a run-time dependency of the service. Everything in here is on the classpath when the gHN is running.${BUILD_LOCATION}/Dependencies/SampleService
, where it can be found as a compile-time dependency of the service. This is why we specified the custom dependency location in the build properties.
The build process generates also three additional port-type interfaces from the original one: Stateless_bindings.wsdl
, Stateless_flattened.wsdl
, and Stateless_service.wsdl
. For the time being, you do not need to be overly concerned with these, but simply notice that the latter, Stateless_service.wsdl
, is the port-type interface pointed at from the deployment descriptor of the service.
The build process creates a build folder under the build location, clearing it if it already exists. It then uses this folder to organise all the elements required for the build (code, configuration, interfaces). It copies most elements from the service location, of course, but it complements them with other elements which are cross-service and ship with gCore. In most cases, you should not concern yourself with the contents of the build folder but it is good to know who creates it and why.
Sometimes you might want to generate the stubs or go as far as to package them, but not deploy them. Say you want to make sure they are generated correctly, and perhaps have a peek inside the jar file, but are not ready to replace the stub library previously deployed. In these cases, you can invoke the buildStubs
or the jarStubs
targets:
ant -f SampleService/build.xml buildStubs ...generates and compile stubs but does not package them... ant -f SampleService/build.xml jarStubs ...generates and packages stubs but does not deploy them...
When we will build the service, the buildfile will automatically resolve the dependency of SampleService
to its own stubs from the custom dependency folder. Eclipse needs instead to be told explicitly. For this, you should add $BUILD__LOCATION/Dependencies/SampleService/org.acme.sample.stubs.jar
to the build path of your Eclipse project (Properties -> Java Build Path -> Libraries -> Add Jars...). The same is true for any other custom dependency, of course. For stubs, however, the good news is that we do not have to manually copy the library into the custom dependency folder.
When need arises, you can undeploy the stubs by typing:
ant -f SampleService/build.xml undeployStubs
Building & Deploying the Service
From the build location, type:
ant -f SampleService/build.xml
which points implicitly to the build instructions grouped under the target deployService
. If all goes according to plans, the buildfile:
- compiles the service implementation and packages it in a
org.acme.sample.jar
file - further packages
org.acme.sample.jar
along with service configuration and port-type interfaces - both manually and automatically generated - in aorg.acme.sample.gar
file. - deploys
org.acme.sample.gar
in the gHN, primarily by copying:-
org.acme.sample.jar
- and any other dependency$BUILD_LOCATION/Dependencies/SampleService
, but not the service's own stub library - in the$GLOBUS_LOCATION/lib
folder under the gCore location. - all the service configuration under the service location in a
$GLOBUS_LOCATION/etc/org.acme.sample
folder under the gCore location. - all the port-type interfaces in the GAR file in a
$GLOBUS_LOCATION/share/schema/org.acme.sample
folder under the gCore location.
-
As with the stubs (and in fact more commonly), sometimes you might want to compile the service implementation, perhaps package it, and even go as far as to produce the GAR file, but not deploy it. In these cases, you can invoke the buildService
, jarService
, or garService
targets:
ant -f SampleService/build.xml buildService ...compiles the service implementation, but does not package it ant -f SampleService/build.xml jarService ...compiles and packages the service implementation but does create a GAR ant -f SampleService/build.xml garService ...compiles and packages the service implementation, creates a GAR but does not deploy it
If you then want to deploy a still undeployed GAR, you can pass it to a dedicated gCore script. From the build location, type:
gcore-deploy-service SampleService/org.acme.sample.gar
When need arises, you can also undeploy the service by typing:
ant -f SampleService/build.xml undeployService.
Alternatively, you can undeploy the service by passing the name of its GAR to a dedicated gCore script:
gcore-undeploy-service org.acme.sample
Starting the gHN
Believe it or not, our 'quick' tour of service development is over.
We can now start the gHN and make sure our SampleService
is alive and kicking in it.
Making sure of it means to look into the logs emitted by the gHN and the code which is deployed in it.
There are essentially two logfiles you should be prepared to look into:
-
nohup.out
offers a brief report of the endpoints of the services which are managed by the gHN and highlights any early startup problem.SampleService
's single endpoint ought to appear in the list and no obvious error message should be visible.
-
container.log
is the main gHN log and contains a trace of the activities of all the services and components which have cared to leave it. Now, by inheriting from core gCF classes, the implementation ofSampleService
will leave a trace of its main activities in the gHN log. However, you need to configure logging in order to show it.
gCore uses Log4j as its logging technology and expects its configuration in $GLOBUS_LOCATION/container-log4j.properties
. For now, you need only to specify that all the logs which are produced by code in the org.acme.sample
package ought to be revealed. This is easily done:
# Display any warnings generated by our code log4j.category.org.globus=WARN log4j.category.org.gcube=DEBUG ... log4j.category.org.acme.sample=DEBUG
To start the gHN, now type:
gcore-start-container
or, to enable full log in case of early startup problems:
gcore-start-container -debug
In both cases, any previous running instance of the gHN will automatically killed.
If all goes well, your nohup.out
log should match this template:
Starting SOAP server at: http://<hostname>:<port>/wsrf/services/ With the following services: [1] .... ... ... [...]: http://<hostname>:<port>/wsrf/services/acme/sample/stateless ... ...
In particular, it should show the full endpoint of the Stateless
port-type of SampleService
.
Similarly, container.log
should include the trace of the startup activities of SampleService
:
2008-04-11 16:25:41,562 INFO contexts.GHNContext [main,info:108] GHNContext: Initialising GHN 2008-04-11 16:25:41,588 INFO contexts.GHNContext [main,info:108] GHNContext: Creating the resource 2008-04-11 16:25:41,616 DEBUG contexts.GHNContext [main,debug:84] GHNContext: Parsing GHN from /Users/fabio/workspace/gcube/GCORE_DEV/config/GHNProfile.xml 2008-04-11 16:25:41,794 DEBUG builders.GHNBuilder [main,debug:84] GHNBuilder: Updating GHN 2008-04-11 16:25:41,795 DEBUG contexts.GHNContext [main,debug:84] GHNContext: Analysing memory status... ... ... 2008-04-11 16:25:43,146 INFO sample.ServiceContext [main,info:108] SampleService: Initialising Running Instance 2008-04-11 16:25:43,149 DEBUG sample.ServiceContext [main,debug:84] SampleService: Parsing RI from ....(omitted) 2008-04-11 16:25:43,155 DEBUG builders.RIBuilder [main,debug:84] SampleService: Updating Running instance 2008-04-11 16:25:43,164 INFO sample.ServiceContext [main,info:108] SampleService: Running Instance has been updated 2008-04-11 16:25:43,165 INFO sample.ServiceContext [main,info:108] SampleService: Managing security with a GCUBEServiceSecurityManagerImpl 2008-04-11 16:25:43,166 INFO contexts.GHNContext [main,info:108] GHNContext: Registering service Samples SampleService... ... ... 2008-04-11 16:25:43,168 INFO sample.ServiceContext [main,info:108] SampleService: Running Instance has moved to status: initialised 2008-04-11 16:25:43,179 INFO sample.ServiceContext [Thread-24,info:108] SampleService: Running Instance has been updated ... ... 2008-04-11 16:25:43,271 INFO sample.ServiceContext [Thread-27,info:108] SampleService: ready event invoked on SampleService 2008-04-11 16:25:43,272 INFO sample.ServiceContext [Thread-27,info:108] SampleService: Running Instance has moved to status: ready 2008-04-11 16:25:43,281 INFO sample.ServiceContext [Thread-27,info:108] SampleService: Running Instance has been updated ... ...
Without going unnecessarily into details, the logs show the various stages of initialisation undergone by SampleService
, from its initialisation and activation to the point in which the service is ready to receive requests at its port-types. Incidentally, this log shows the amount of work which gCF has already carried out on your behalf.
A Minimal Client
We conclude the tour with an invocation of SampleService
. The simple client below exemplifies the use of stubs to interact with a Running Instance of SampleService
and invoke the about()
method of its Stateless
port-type:
package org.acme.sample.tests; import ... public class StatelessTest { public static void main(String[] args) throws Exception { EndpointReferenceType endpoint = new EndpointReferenceType(); endpoint.setAddress(new AttributedURI(args[0])); StatelessPortType stub = new StatelessServiceAddressingLocator().getStatelessPortTypePort(endpoint); stub=GCUBERemotePortTypeContext.getProxy(stub,GCUBEScope.getScope(args[1])); System.out.println(stub.about(args[2])); } }
Most code in StatelessTest
is fairly standard, but we comment it here in order to highlight what is instead less conventional.
- We first model the endpoint of the
Stateless
port-type of a particular Running Instance ofSampleService
:
EndpointReferenceType endpoint = new EndpointReferenceType(); endpoint.setAddress(new AttributedURI(args[0]));
EndpointReferenceType
is our model of the endpoint and we expect it to be initialised with a command-line argument of the form:
http://<hostname>:<port>/wsrf/services/acme/sample/stateless
- Next, we obtain a stub of the remote port-type at that endpoint:
StatelessPortType stub = new StatelessServiceAddressingLocator().getStatelessPortTypePort(endpoint);
StatelessPortType
and ServiceAddressingLocator
are both part of the stubs library of SampleService
. The second returns instances of the first bound to specific endpoints.
- Next, we uses gCF facilities to obtain a proxy for the stub which knows how to make gCube calls, i.e. annotate calls with information which identifies the target service of future calls and the scope in which they will be made:
stub=GCUBERemotePortTypeContext.getProxy(stub,GCUBEScope.getScope(args[1]));
This is an unusual task in web service development, and it is offered in gCF as a transparency over gCube-specific requirements. All you need to know at this stage is that the resulting proxy is indistinguishable from the original stub, just more 'intelligent' in its behaviour because it can identify the target service from information included in the stub library. The intended scope is of course something we need to specify and we do so with a GCUBEScope
object obtained by parsing a further command-line argument (e.g. /gcube/devsec
).
- Finally, we invoke the
about()
method of the remote port-type against the stub proxy, using a third command-line argument for the expected input (e.g.John Doe
) and displaying the oputput.
System.out.println(stub.about(args[2]));
StatelessTest
depends on standard gCore libraries (e.g. for EndpointReferenceType
, GCUBERemotePortTypeContext
, and GCUBEScope
) as well as on the stub library of SampleService
. Of course, these dependencies must be on the classpath during compilation and execution. As extra requirements for client, gCF requires that the gCore location be also on the classpath and that the environment variable GLOBUS_LOCATION
be set to it. How you accommodate these requirement depends on whether you wish to compile and run StatelessTest
from the shell or from within Eclipse.
If you wish to compile and run StatelessTest
from the shell - and you are working with the gHN which hosts SampleService
- then all you need to do is to 'source' a gCore script which does it for you:
source $GLOBUS_LOCATION/bin/gcore-load-env
If instead you wish to compile and run from an Eclipse project, then things get a bit more convoluted. First of all, you need to make sure the project has dependencies to the user libraries GCORELIBS
and SAMPLESERVICEDEPS
defined above. Of course, you can always place StatelessTest
under the service location, where dependencies to those libraries are already defined:
|-SampleService |--etc |---profile.xml |---deploy-jndi-config.xml |---deploy-server.wsdd |---build.properties | |--src |---org |----acme |-----sample |------ServiceContext.java |------stateless |-------Stateless.java |------tests |-------StatelessTest.java [new] | |--schema |---Stateless.wsdl | | |--build.xml | |-Dependencies |--SampleService
Next you have to configure the run configuration of your client by specifying the gCore location both as the value of the GLOBUS_LOCATION
environment variable and as a classpath element. The most reusable way of approaching this task is to:
- define once and for all a string variable
GLOBUS_LOCATION
at the workspace level and set it to the gCore location. Menu-wise:
Preferences->Run/Debug->String Substitution->New...
- add an environment variable
GLOBUS_LOCATION
to the run configuration of all your clients and set it to the string variable just defined. Menu-wise:
Open Run Dialog...->Environment->New...->Variables
- add the value of the string variable as a classpath element to the run configuration of all your clients. Menu-wise:
Open Run Dialog...->Classpath->User Entries->Advanced...->Add Variable String
Whether you are running the client from the shell or from within Eclipse, the output to expect (under default gCore settings) is the following:
Hello John Doe, you have invoked service SampleService (Samples)
Using the Eclipse IDE for for debugging and/or profiling your service
Degugging
Starting the gHN with:
gcore-start-container-debug
allows you to connect your eclipse debugger with the container following a few easy steps.
- Open your Eclipse IDE and choose from the main menu: Run -> Debug Configurations
- In the "Debug Configurations" window add a new configuration for debugging a "Remote Java Application".
- Give the configuration an appropriate name
- Have it pointing to the project you wish to debug
- Set the connection type to "Standard (Soket attach)"
- Set the host to be the host name where the server runs on and port 8787
- Press Debug
- Open the Debug perspective and you are ready to start the Debugging
Profiling
Starting the gHN with:
gcore-start-container-profile
allows you to connect your eclipse profiler with the container.
The process described process is tested with Eclipse Ganymede and the "Eclipse Test and Performance Tools Platform" (TPTP). After installing the TPTP plugin you have to follow the path of profiling a remote java application.
First, you should manually start the agent that will be picking up messages from the java VM. This agent will have to be in the same system where the container runs. This is done with a command like:
/home/myusername/.Programs/eclipse/plugins/org.eclipse.tptp.platform.ac.linux_ia32_4.4.100.v200902101418/agent_controller/bin/ACStart.sh
Be sure that there is only one ACServer running, the one created by your call to ACStart.sh script. You can find more info on setting up this agent here
As soon ACServer and gCore are running
- Open your Eclipse IDE and choose from the main menu: Run -> Profile Configurations
- In the "Profile Configurations" window add a new configuration from category "Attach to Agent".
- Give the configuration an appropriate name
- Have it pointing to the host you run the container along with the agent.
- Set port 10003 (unless you pick a different port from the agents configurations)
- In the Agents tab select what you want to profile
- Press Profile
The Profiling perspective should open and you should be able to profile your service.