It does not know, however, about any specific data types (it does not use any generated data types). The idea here is that the generated data types will be known to your specific clients (see a code example below).
If your are going to write your client this way (and gain many features from the BaseClient class), your class should extend the BaseClient and, at minimum (which is usually sufficient), implement the following three methods:
Additionally, you must call a method to start processing:
- getServiceLocator()
- It returns a container with information what Biomoby service should be called and how to find it.
- fillRequest (MobyJob,MobyPackage)
- Crate data (fill them into a MobyJob) for one job (a query). The data will be sent within given MobyPackage - but it is not yet there, and it is not the task of this method to copy it there. Return true if this MobyJob should be included in the input data (package). Or return false if you already filled enough jobs.
- useResponse (MobyJob,MobyPackage)
- Process data (in MobyJob), representing a response to a single job (a query), returned from the BioMoby service. The other parameter (MobyPackage) is an envelope where the full response (all its jobs) is located; you do not need to do anything with it here unless you wish (e.g. it gives you knowledge about how many jobs are in the full response, or it gives you access to the so-called service notes).
Here is the whole example of a client that calls a Biomoby service Mabuhay:
- process(), or process(int how_many_jobs_to_send)
- It is the main method that packs input data, invokes a BioMoby service and uses its response. This method will call your implementation of the three methods above.
The Biomoby registry says about this service that the service knows:"How to say 'Hello' in many languages. Heavily based on a web resource 'Greetings in more than 800 languages', maintained at http://www.elite.net/~runner/jennifers/hello.htm by Jennifer Runner."
The code for both the client (above) and the service itself is included in jMoby in src/samples and you can compile it by calling Ant:// MabuhayClient.java // package org.jmoby.tutorial.client; import org.biomoby.client.BaseClient; import org.biomoby.shared.MobyException; import org.biomoby.shared.MobyService; import org.biomoby.shared.parser.MobyPackage; import org.biomoby.shared.parser.MobyJob; import org.biomoby.shared.datatypes.MobyObject; import org.biomoby.client.MobyServiceLocator; import org.biomoby.shared.datatypes.*; /** * This is an example of a client. It calls a Biomoby service Mabuhay, * it uses generated data types and it benefits from various features * provided by the BaseClient class. Because it is an example how to * code such client, it intentionally has a very limited command-line * options. * * @author Martin Senger */ public class MabuhayClient extends BaseClient { // from the command-line String serviceEndpoint; Regex regex; /************************************************************************** * Constructor. It expects arguments: language [service-endpoint]. *************************************************************************/ public MabuhayClient (String[] args) { regex = new Regex(); regex.set_regex (args[0]); if (args.length > 1) serviceEndpoint = args[1]; } /************************************************************************** * What service to call and where to find it. *************************************************************************/ public MobyServiceLocator getServiceLocator() { MobyService service = new MobyService ("Mabuhay"); if (serviceEndpoint != null) service.setURL (serviceEndpoint); return new MobyServiceLocator (service); } /************************************************************************** * Set here the input data that will be sent to the service. *************************************************************************/ public boolean fillRequest (MobyJob request, MobyPackage inputContext) throws MobyException { request.setData (regex, "language"); return true; } /************************************************************************** * Use here the data returned from the service. *************************************************************************/ public boolean useResponse (MobyJob response, MobyPackage responseContext) throws MobyException { MobyObject[] hellos = response.getDataSet ("hello"); for (int i = 0; i < hellos.length; i++) System.out.println ("(" + (i+1) + ") " + hellos[i].toString()); return true; } /************************************************************************** * An entry point... *************************************************************************/ public static void main (String [] args) { if (args.length == 0) { System.err.println ("Usage: java MabuhayClient <language> [<service-endpoint>]\n" + "where <language> is a regular expression defining a language."); System.exit (1); } MabuhayClient client = new MabuhayClient (args); try { client.process(); } catch (MobyException e) { System.err.println (e.getMessage()); System.exit (1); } } }
Here is an example how you invoke the client and what it prints. Note that the second parameter redirects the call to a local machine. But if the service is running somewhere else you can omit this parameter, which means that the BaseClient will go first to the default Biomoby registry where it hopefully finds the service URL, and then it will call the service on that URL.ant samples-compile
build/run/run-any-client org.jmoby.tutorial.client.MabuhayClient Czech http://localhost:8080/axis/services/Mabuhay (1) simple_key_value_pair MobyString, Article name: value Value: Dobry den MobyString, Article name: key Value: Czech (Czech Republic) (2) simple_key_value_pair MobyString, Article name: value Value: Nazdar MobyString, Article name: key Value: Czech (Czech Republic) (3) simple_key_value_pair MobyString, Article name: value Value: Ahoj MobyString, Article name: key Value: Czech (Czech Republic) [informal]
This was the most common usage of the features of the BaseClient. But it gives you more. Your implementation can override methods that get you access to the raw XML data (both after they were created but before they were sent, and after they were received from the service but before they were parsed), or it can override methods that process the whole request and/or the whole response (not just the individual jobs), and finally you can override the whole process() method if you need the full control. An example of such rich overriding is actually a BaseCmdLineClient described further below.
To understand better what the standard process() method does here is a flow-diagram:
The options for specifying what service to call and where to find it are:build/run/run-service [<options>]
Option/Parameter Meaning -service <service-name> A service name to call. This is "almost" mandatory parameter. You do not use it only if a -loop option is used. -e <service-endpoint> A service endpoint/URL where it can be reached. It has precedence over going to a registry to find a service URL. -mobye <registry-endpoint> A URL/endpoint of a Biomoby registry where the called service was registered. The client goes there to get the service URL - based on the service name and its authority - if the service endpoint is not given. A default registry is used if this is not specified (and if it is needed). -mobyuri <registry-namespace> A registry namespace (used when the client goes to a registry). A default is used if this is not given. -auth <service-authority> Service name is not enough when a service is looked-up in a Biomoby registry. This gives the service authority that qualifies the service (together with its name) uniquely. -class <class-name Client tries to load the given class and then it calls a method that has the same name as the service name. This allows to call your service implementation locally, before it has been deployed into Tomcat/Axis. -loop Don't call any service. It is good for seeing input data.
Another set of options is for telling what to do with the input:
Option/Parameter Meaning -show Print the input as a string. -showxml Print the input as XML. -asbytes Send the input as byte array (byte[] type). This is for testing whether a service complies with the Biomoby API that dictates that services must accept both strings and byte arrays.
We have not still learned how to create any input, but let us look what can be done with the output:
Option/Parameter Meaning -outstr Show output (response) as a string. This is a default option if no other output option is given. -outxml Show raw response in XML. -noout Don't show output at all. It is useful together with the -loop option if you want to test your input. -o <output-file> An output file where the result (whichever you specify by the -outstr or -outxml options) will be stored.
The main purpose of the BaseCmdLineClient is, of course, to create some meaningful input. The following options are here to do it. The main option is -obj that says what kind of object should be created. The BaseCmdLineClient creates this object from the generated datatypes - and tries to fill also its children - if it can find enough values.
The values come from two sources. Either simply from an option -value, or from the named values in a form name=value. The name here means an article name. In both cases, you can specify more values, separated by comma (or by a separator defined by an option -div), and they will be taken as long as they are needed, and when the pile of values is exhausted (and some values are still needed) the last value will be repeated. The difference between -value and named values is that the client first tries to satisfy the object by its article names from the named values, and only if it cannot find any named value with the name of a wanted article (or if the object definition does not have any article name) then it takes value from the -value option. It sounds complicated but below are examples.
Remember that the values from -values are taken in the order as they are there - and they are used for any kind of objects, string, integers, floats. So it can happen that some string values are not suitable to become integer, and the clients stops. Avoid this situation either by using named values (if you can, if the object has article names) or by swapping the values in the list.
Additional options are for IDs and Namespaces. They can also have a list of values separated with comma (or whatever separator defined in -div), and such values are taken as long as some IDs or Namespaces are needed. There is no equivalent to the named values here.
As said above, the last value in a list is repeated if more values (IDs, Namespaces) are needed. You can avoid it but putting there an empty value (a space). Something like this (see also the examples below):
The remaining options govern how big the input will be. You can specify how many jobs, how many elements in a collection, and how many children in a HAS relationship. Remember that by changing these options the input significantly changes (it can become very big, indeed) but you do not need (unless you want to) to change values, IDs and Namespaces because they are taken as needed (with the last item being repeated as long as needed).-id '4530,4550, '
Option/Parameter Meaning -obj <data-type-name> A name of an object from the Biomoby object ontology. Default is Object. -value[s] <value>[,<value>...] Values that will be used to fill values of primitive types -id <id>[,<id>...] IDs that will be used to fill IDs of Simple and top-level objects. The IDs are not filled to the child types of the top-level object. -ns <ns>[,<ns>...] Namespaces that will be used to fill Namespaces of Simple and top-level objects. The Namespaces are not filled to the child types of the top-level object. <name>=<value>[,<value>...] Named values that will be used to fill objects with article name equals to the <name> given here. -div <divider> A separator that will be used instead commas in the -id, -ns, -values and named values. -name <article-name> An article name that will be given to the Simple or Collection envelope. -xml <input-file> An input XML file. This option overrides all other options for creating input data. -jobs <integer> How many jobs (queries) will be created. Default is 1. -cols <integer> How many elements will be created in a collection. Default is 0 (no collection). -has <integer> How many children in a HAS relationship will be created. Default is 1.
And few remaining options are:
Option/Parameter Meaning -noexit Just return at the end and do not call System.exit(). It is useful when your override this client. -nostack Don't print any stack trace.
"The Thousand and One Nights" stories (or parameters) are finished. Time for examples. The longer lines (after all, this is a command-line client, isn't it?) are shown with backslashes - but this is only for formatting purposes, you type them always as one line. Some examples are followed by the XML input they created.
Let's create a basic input:
build/run/run-service -id 4530 -loop -showxml -noout
Now, make the similar input but containing three jobs (you see that the IDs are repeated, because we want three jobs but provide only two IDs):<?xml version="1.0" encoding="UTF-8"?> <moby:MOBY xmlns:moby="http://www.biomoby.org/moby"> <moby:mobyContent> <moby:mobyData moby:queryID="job_0"> <moby:Simple> <moby:Object moby:id="4530" moby:namespace="" /> </moby:Simple> </moby:mobyData> </moby:mobyContent> </moby:MOBY>
build/run/run-service -id 4530,4513 -loop -showxml -noout
Now we want to have a collection, with two elements, and with an article name "taxons":<?xml version="1.0" encoding="UTF-8"?> <moby:MOBY xmlns:moby="http://www.biomoby.org/moby"> <moby:mobyContent> <moby:mobyData moby:queryID="job_0"> <moby:Simple> <moby:Object moby:id="4530" moby:namespace="" /> </moby:Simple> </moby:mobyData> <moby:mobyData moby:queryID="job_1"> <moby:Simple> <moby:Object moby:id="4513" moby:namespace="" /> </moby:Simple> </moby:mobyData> <moby:mobyData moby:queryID="job_2"> <moby:Simple> <moby:Object moby:id="4513" moby:namespace="" /> </moby:Simple> </moby:mobyData> </moby:mobyContent> </moby:MOBY>
build/run/run-service -id 4530,4513 -loop -showxml -noout -cols 2 -name taxons
Time to create a more complicated object, a GenericSequence. Note that the client finds itself the structure of this object, you just supply named values:<?xml version="1.0" encoding="UTF-8"?> <moby:MOBY xmlns:moby="http://www.biomoby.org/moby"> <moby:mobyContent> <moby:mobyData moby:queryID="job_0"> <moby:Collection moby:articleName="taxons"> <moby:Simple> <moby:Object moby:id="4530" moby:namespace="" /> </moby:Simple> <moby:Simple> <moby:Object moby:id="4513" moby:namespace="" /> </moby:Simple> </moby:Collection> </moby:mobyData> </moby:mobyContent> </moby:MOBY>
build/run/run-service -obj GenericSequence -loop -showxml -noout SequenceString=tatata,gcgcgcg Length=12,65
If you do not know the article names (and you do not want to look into generated data types, or to a registry) you can provide the values as -values. But it's tricky because the values are taken in the order you do not know (me neither, by the way) and if some are integer, you might get an error:<?xml version="1.0" encoding="UTF-8"?> <moby:MOBY xmlns:moby="http://www.biomoby.org/moby"> <moby:mobyContent> <moby:mobyData moby:queryID="job_0"> <moby:Simple> <moby:GenericSequence moby:id="" moby:namespace=""> <moby:Integer moby:id="" moby:namespace="" moby:articleName="Length">12</moby:Integer> <moby:String moby:id="" moby:namespace="" moby:articleName="SequenceString">tatata</moby:String> </moby:GenericSequence> </moby:Simple> </moby:mobyData> </moby:mobyContent> </moby:MOBY>
build/run/run-service -obj GenericSequence -loop -showxml -noout -values tatata,12,gcgcgcg,65
But if you play with the order of values you will get it right at the end:Error by populating MobyObject: org.biomoby.shared.MobyException: Error by setting value: org.biomoby.shared.MobyException: (In object '') Value 'tatata' is not an integer.
build/run/run-service -obj GenericSequence -loop -showxml -noout -values 12,tatata,65,gcgcgcg
In all the examples above we used -loop so no service was invoked. Let's change this now: let's call a real service getTaxChildNodes. It expects an Object in the taxon Namespace:<?xml version="1.0" encoding="UTF-8"?> <moby:MOBY xmlns:moby="http://www.biomoby.org/moby"> <moby:mobyContent> <moby:mobyData moby:queryID="job_0"> <moby:Simple> <moby:GenericSequence moby:id="" moby:namespace=""> <moby:Integer moby:id="" moby:namespace="" moby:articleName="Length">12</moby:Integer> <moby:String moby:id="" moby:namespace="" moby:articleName="SequenceString">tatata</moby:String> </moby:GenericSequence> </moby:Simple> </moby:mobyData> </moby:mobyContent> </moby:MOBY>
We asked for the result in both formats (-outstr and -outxml):build/run/run-service -service getTaxChildNodes -ns taxon -id 4530 -outstr -outxml
Let's try to send to the Mark's service more jobs - more species (and do not show anymore result in XML):<?xml version='1.0' encoding='UTF-8'?><moby:MOBY xmlns:moby='http://www.biomoby.org/moby' xmlns='http://www.biomoby.org/moby'> <moby:mobyContent moby:authority='illuminae.com'> <moby:serviceNotes>This data is provided by <a HREF='http://seqhound.blueprint.org'>SeqHound</a>; <a href='http://www.ncbi.nlm.nih.gov/entrez/query.fcgi?cmd=Retrieve&db=PubMed&list_uids=12401134&dopt=Abstract'>Michalickova K, Bader GD, Dumontier M, Lieu H, Betel D, Isserlin R, Hogue CW. SeqHound: biological sequence and structure database as a platform forbioinformatics research. BMC Bioinformatics. 2002 Oct 25;3(1):32</a></moby:serviceNotes> <moby:mobyData moby:queryID='job_0'> <moby:Collection moby:articleName=''> <moby:Simple><Object namespace='taxon' id='39946'/></moby:Simple> <moby:Simple><Object namespace='taxon' id='39947'/></moby:Simple> </moby:Collection> </moby:mobyData> </moby:mobyContent></moby:MOBY> Authority: illuminae.com Service notes: This data is provided by SeqHound; Michalickova K, Bader GD, Dumontier M, Lieu H, Betel D, Isserlin R, Hogue CW. SeqHound: biological sequence and structure database as a platform forbioinformatics research. BMC Bioinformatics. 2002 Oct 25;3(1):32 Jobs (invocations): (1) Query ID: job_0 Data elements: (Collection) Article name: Data elements: (1) (Simple) Article name: MobyObject, Id: 39946, Namespace: taxon (2) (Simple) Article name: MobyObject, Id: 39947, Namespace: taxon
build/run/run-service -service getTaxChildNodes -ns taxon -id 4530,4513,4577,50455,4565 -jobs 5
The last example is for debugging. You have just wrote a new Biomoby web service in a class net.the-sengers.services.SudokuImpl (by the way, such service really exists - but not in Java but in Perl). You also have a manually prepared input XML file, and you wish to test the service before deploying it (note that the shown output is only similar, this client does not have good output rendering):Jobs (invocations): (1) Query ID: job_0 Data elements: (Collection) Article name: Data elements: (1) (Simple) Article name: MobyObject, Id: 39946, Namespace: taxon (2) (Simple) Article name: MobyObject, Id: 39947, Namespace: taxon (2) Query ID: job_1 Data elements: (Collection) Article name: Data elements: (1) (Simple) Article name: MobyObject, Id: 77009, Namespace: taxon (2) (Simple) Article name: MobyObject, Id: 112509, Namespace: taxon (3) Query ID: job_2 Data elements: (Collection) Article name: Data elements: (1) (Simple) Article name: MobyObject, Id: 4579, Namespace: taxon (2) (Simple) Article name: MobyObject, Id: 76912, Namespace: taxon (3) (Simple) Article name: MobyObject, Id: 112001, Namespace: taxon (4) (Simple) Article name: MobyObject, Id: 186653, Namespace: taxon (5) (Simple) Article name: MobyObject, Id: 334825, Namespace: taxon (4) Query ID: job_3 Data elements: (5) Query ID: job_4 Data elements: (Collection) Article name: Data elements: (1) (Simple) Article name: MobyObject, Id: 58933, Namespace: taxon (2) (Simple) Article name: MobyObject, Id: 69993, Namespace: taxon (3) (Simple) Article name: MobyObject, Id: 69994, Namespace: taxon (4) (Simple) Article name: MobyObject, Id: 77604, Namespace: taxon (5) (Simple) Article name: MobyObject, Id: 231718, Namespace: taxon
build/run-service -service Sudoku -class net.the-sengers.services.SudokuImpl -xml sudoku-input.xml
------------------------------- | . . . | . . . | . 8 . | | . . . | . 3 . | 6 7 . | | 1 . . | 9 . . | . . . | ------------------------------- | . . . | 2 . 3 | . . 4 | | . 7 . | . 8 . | . 3 . | | 5 . . | 7 . 1 | . . . | ------------------------------- | . . . | . . 5 | . . 2 | | . 6 8 | . 7 . | . . . | | . 4 . | . . . | . . . | ------------------------------- Used 1 easy steps to get here: ------------------------------- | . . . | . . . | . 8 . | | . . . | . 3 . | 6 7 . | | 1 . . | 9 . . | . . . | ------------------------------- | . . . | 2 . 3 | . . 4 | | . 7 . | . 8 . | . 3 . | | 5 . . | 7 . 1 | . . . | ------------------------------- | . . . | . . 5 | . . 2 | | . 6 8 | . 7 . | . . . | | . 4 . | . . . | . . . | ------------------------------- Used 890 recursive steps to get here: ------------------------------- | 6 2 3 | 4 1 7 | 5 8 9 | | 4 5 9 | 8 3 2 | 6 7 1 | | 1 8 7 | 9 5 6 | 4 2 3 | ------------------------------- | 8 1 6 | 2 9 3 | 7 5 4 | | 9 7 2 | 5 8 4 | 1 3 6 | | 5 3 4 | 7 6 1 | 2 9 8 | ------------------------------- | 7 9 1 | 3 4 5 | 8 6 2 | | 2 6 8 | 1 7 9 | 3 4 5 | | 3 4 5 | 6 2 8 | 9 1 7 | -------------------------------
In jMoby, in src/samples, is yet another Mabuhay client, named MabuhayClient2, calling the Mabuhay service (see at the beginning of this document what it does). The full source of this client is here:
Here is how it can be invoked:// MabuhayClient2.java // package org.jmoby.tutorial.client; import org.biomoby.client.BaseCmdLineClient; import org.biomoby.shared.MobyException; import org.biomoby.shared.parser.MobyPackage; import org.biomoby.shared.parser.MobyJob; import org.biomoby.shared.datatypes.MobyObject; import org.biomoby.shared.datatypes.*; /** * @author Martin Senger */ public class MabuhayClient2 extends BaseCmdLineClient { /************************************************************************** * Constructor. Pass everything to the parent. *************************************************************************/ public MabuhayClient2 (String[] args) { super (args); } /************************************************************************** * An entry point - again, pass everything to the parent. *************************************************************************/ public static void main (String [] args) { new MabuhayClient2 (args).doEverything(); } /************************************************************************** * Here is your job done: print the result better than the parent. *************************************************************************/ public boolean useResponse (MobyJob response, MobyPackage responseContext) throws MobyException { System.out.println ("Results of job: " + response.getId()); MobyObject[] results = response.getDataSet(); for (int i = 0; i < results.length; i++) { simple_key_value_pair pair = (simple_key_value_pair)results[i]; System.out.println (pair.get_key() + "\t" + pair.get_the_value()); } return true; } }
Which produces:build/run/run-any-client org.jmoby.tutorial.client.MabuhayClient2 \ -name language regex=Czech -obj Regex \ -e http://localhost:8080/axis/services/Mabuhay -service Mabuhay -noout
You noticed a bit surprising and unpleasant parameter -noout which prevents the parent class to print its output. It could be done better. Well, perhaps later...Results of job: job_0 Czech (Czech Republic) Dobry den Czech (Czech Republic) Nazdar Czech (Czech Republic) [informal] Ahoj
Let's finish by invoking the same client again, now telling to create two jobs, one returning Hello's in English and one in languages in Philippines:
Which produces (I replaces the unprintable characters by a dot; let's not start now a talk about Unicode and its support):build/run/run-any-client org.jmoby.tutorial.client.MabuhayClient2 \ -name language 'regex=(?i)english,(?i)philippines' -obj Regex \ -e http://localhost:8080/axis/services/Mabuhay -service Mabuhay -noout
Results of job: job_0 English (America, Australia, UK) Hello English (America, UK) [formal] Good day English (America) [informal] Hi English (Australia) G'day English (Australia) Hiya English (New Zealand) Gidday English (Southern USA) [to more than one] Hi y'all English [Strine dialect] (Australia) Hay gaan English [Strine dialect] (Australia) Hezza gaan English [Texan dialect] (Texas USA) Howdy English [New Oreleans dialect](Lousiana USA) Where ya'at English [Middle English] (old England) Gode dai English [Middle English] (old England) Gode dei English [Old English] (old Britain) Ic grete .e Results of job: job_1 Aklanon (Philippines) Kumasta Batak (Indonesia, Sumatra, Philippines) Horas Batak (Indonesia, Sumatra, Philippines) Horas bah Bicol (Philippines) Kumustas Bicol (Philippines) Kamusta Bicol (Philippines) Marhay na aldaw Bicol (Philippines) Marhay na aldaw po Bicol (Philippines) Marhay na aldaw sa indo gabos Bicol (Philippines) Marhay na aldaw saindong gabos Bicol (Philippines) Magandang umaga po Ilokano (Philippines) [morning] Naimbag nga bigat Ilokano (Philippines) [morning, formal] Naimbag a bigatyo Ilokano (Philippines) [morning, informal] Naimbag a bigatmo Ilokano (Philippines) [at noon] Naimbag nga aldaw Ilokano (Philippines) [noon, formal] Naimbag .ga aldawyo Ilokano (Philippines) [noon, informal] Naimbag .ga aldawmo Ilokano (Philippines) [afternoon] Naimbag nga malem Ilokano (Philippines) [afternoon, formal] Naimbag a malemyo Ilokano (Philippines) [afternoon informal] Naimbag a malemmo Ilokano (Philippines) [evening] Naimbag nga rabii Ilonggo (Philippines) Maayo Ilonggo (Philippines) Maayo nga adlaw Ilonggo (Philippines) Halo Itbayaten (Batanes Philippines) Kapian ka pa nu Dios aschapanderak Ivasayen (Batanes Philippines) Capian ka pa nu Dios si chamavucjas Kapampangan (Philippines) [morning] Mayap ayabak Pangasinan (Philippines) Masant.s a kabuas.n Pangasinan (Philippines) Maabig ya kabuas.n Pangasinan (Philippines) Masant.s ya agew Pangasinan (Philippines) [to a group] Masant.s ya agew ed sikayo Pangasinan (Philippines) [to a group] Maabig ya kabuas.n ed sicayon Tagalog (Philippines) Halo Tagalog (Philippines) Huy Tagalog (Philippines) Magandang hapon po Tagalog (Philippines) Mabuhay Tagalog (Philippines) Kumust. Visayan (Philippines) [where are you going?] Asa ka mo muadto? Visayan (Philippines) Maqayu