Back to index page

How to write a simple client for Greenstone 3 web services

We'll be going through an example of how to write a simple client for Greenstone 3's Query, Browse and Retrieval web services provided by QBRSOAPServer.

In this document,

Sections

A General information on writing a client for Greenstone 3 web services

  1. First we need Greenstone 3 to deploy the web services in order for our client to have access to its operations.
    Go to $GSDLHOME and type in an X-term:
    ant soap-deploy-site
    It will ask you for the If you go to the page
    http://HOST:PORT/greenstone3/services
    you will see links to various wsdl files. The one for the QBRSOAPServer we just deployed for "localsite" is the one our example program will be using to communicate with Greenstone 3's QBRSOAPServer functionality. The localsite web service has several query, browse and retrieve operations as well as describe operations which will describe the details of these Greenstone 3 services.
  2. Now you can invoke the localsite process method by writing a client. The basics of invoking a web service operation in Java when using Apache Axis, is to
    1. import Axis' Call and Service objects:
      import org.apache.axis.client.Call;
      import org.apache.axis.client.Service;
      import javax.xml.namespace.QName;
      import javax.xml.rpc.ServiceException;
      
    2. Then write a constructor that will create the Service object using the url for the web service's wsdl file and the (namespace-qualified) name of the web service.
      try {
          	service = new Service(wsdlURLName, new QName(namespace, serviceName));
          } catch(ServiceException e) {
          	System.err.println("Unable to connect to web services.\nExiting...");
          	throw(e);
          }
      
    3. In order to invoke a web service operation, use that Service object to create a Call object for the operation. You need the (namespace-qualified) portName of the web service, which can be found in the wsdl file. Similarly, you need the name of the operation, also found in the wsdl file:
      Call call = (Call)this.service.createCall(
      		new QName(namespace, portName), 
      		new QName(namespace, OPERATION_NAME)
      		);
      
    4. Finally, invoke the operation using that call object by
      • putting the operation's parameters in the right order into an array of Objects and passing that to Call's invoke() method.
      • by casting the return value to the expected return type.
        MyReturnType response = (MyReturnType) call.invoke(new Object[]{
        	paramObj1, paramObj2, ..., paramObjN});
        
  3. To get a Greenstone client (or web service) to compile or run, you may need to include some jar files in the compile/execution classpath. Look in You may also need $GSDLHOME/web/applet/xml-apis.jar
    If you ever need to use servlets, add in $GSDLHOME/packages/tomcat/common/lib/servlet-api.jar

B An actual code example

We're going to write a simple client for the QBRSOAPServer.
  1. Deploy the QBRSOAPServer in order to have our client access it. Go to $GSDLHOME and type in an X-term:
    ant soap-deploy-site
    For our example:
  2. The following GreenstoneConnection class is a simple client for QBRSOAPServer web service that merely uses its query and describeCollectionService web service operations in order to execute a FieldQuery on the gs2mgppdemo collection:
    import org.apache.axis.client.Call;
    import org.apache.axis.client.Service;
    import javax.xml.namespace.QName;
    import javax.xml.rpc.ServiceException;
    
    import java.util.Map;
    import java.util.HashMap;
    
    /** The class that connects to Greenstone 3's QBRSoapServerLocalsite web service
     *	For this to work, you need to have the QBRSOAPServer web services deployed for
     *	localsite. Once deployed, the wsdl file of QBRSOAPServer describing its web 
     *	services can be found off the page http://HOST:PORT/greenstone3/services/
    */
    public class GreenstoneConnection {
    	static final String HOST = "localhost";
    	static final String PORT = "9090";
    	
    	// Name of web service methods we are going to invoke. 
    	// We can get these from wsdl file. 
    	static final String QUERY_METHOD = "query"; 
    	static final String DESCRIBE_COLLECTION_SERVICE_METHOD 
    		= "describeCollectionService";
    	
    	
    	/** Axis Service object for connecting to Greenstone 3's 'QBRSOAPServer' Web Services
    	 * using its WSDL file, in order to invoke these web services */
    	protected Service service; 
    	
    	// Inspect the wsdl file to find, at the very bottom, the service name, port name and soap address
    	//endpoint locations:
    	// <wsdl:service name="QBRSOAPServerlocalsiteService">
    	//	 <wsdl:port binding="impl:QBRSOAPServerlocalsiteSoapBinding" name="QBRSOAPServerlocalsite">
    	//    <wsdlsoap:address location="http://HOST:PORT/greenstone3/services/QBRSOAPServerlocalsite"/>
    	//   </wsdl:port>
    	// </wsdl:service>
    	// Using the above, we can create the axis Service and Call
    	// objects. At present, I am hard coding the values here as 
    	// an example but you would usually discover the names of the 
    	// xml tags programmatically, by either letting the Service
    	// object parse the wsdl for you or by parsing it yourself.
    	//	 Read-only instance variables
    	final String wsdlURLName;
    	final String namespace = "http://"+HOST+":"+PORT+"/greenstone3/services/QBRSOAPServerlocalsite";
    	final String serviceName = "QBRSOAPServerlocalsiteService";
    	final String portName = "QBRSOAPServerlocalsite";
    	
    
    	// Constructor, takes a String representing the URL of the web service's 
    	// wsdl file. This can be found off the Greenstone 3 services page
    	// http://HOST:PORT/greenstone3/services/
    	public GreenstoneConnection(String wsdlURLName) 
        	throws Exception
        {	
        	this.wsdlURLName = wsdlURLName;
        	// create the service object needed to invoke the web service later on
        	try {
        		service = new Service(wsdlURLName, new QName(namespace, serviceName));
        	} catch(ServiceException e) {
        		System.err.println("Unable to connect to web services.\nExiting...");
        		throw(e);
        	}
    	}
    
    	/** See http://ws.apache.org/axis/java/apiDocs/org/apache/axis/client/Service.html#createCall(javax.xml.namespace.QName,%20javax.xml.namespace.QName)
    	 *	public Call createCall(QName portName,QName operationName)throws ServiceException
    	 *	"Creates a new Call object - will prefill as much info from the 
    	 *  WSDL as it can. Right now it's target URL, SOAPAction, Parameter types, 
    	 *  and return type of the Web Service."
    	 *  This query method invokes the query web service operation.
    	 *  One way you can find the parameters required by the query method
    	 *	is by inspecting the wsdl file.
    	 *	<wsdl:message name="queryRequest">
    	 *	 <wsdl:part name="collection" type="xsd:string"/>
    	 *	 <wsdl:part name="service" type="xsd:string"/>
    	 *	 <wsdl:part name="lang" type="xsd:string"/>
    	 *	 <wsdl:part name="nameToValsMap" type="apachesoap:Map"/>
    	 *	 </wsdl:message>
    	 *
    	 *	 From the wsdl file, we also know that the return type is String:
    	 *	 <wsdl:message name="queryResponse">
    	 *	 <wsdl:part name="queryReturn" type="xsd:string"/>
    	 *	 </wsdl:message>
    	*/
    	public String query(String collection, String service, String lang, 
    			Map nameToValsMap)
    	{
    		try {
    			// try invoking the process() method
    			Call call = (Call)this.service.createCall(
    				new QName(namespace, portName), 
    				new QName(namespace, QUERY_METHOD)
    			);
    			
    			// Now call the web service and pass it all the parameters as part
    			// of an array of Objects.
    			// Knowing that the return type is String, we cast the Object
    			// returned into String prior to returning it.
    			String response = (String) call.invoke(new Object[]{
    					collection, service, lang, nameToValsMap});
    			
    			// later cast the return type to the correct format and return it:
    			return response;
    			
    			// Note that for more complicated return types (like certain collection
    			// types or complex types), you may have to find out what the class
    			// of the return value is. For instance, when a group of elements are 
    			// returned, the return type may be Array or ArrayList or something similar.
    			// In such cases, you need to find the type of each element in the array. 
    			// This is necessary in order to cast the return value into the correct type.
    			// System.err.println("Return type is: " + response.getClass().getName());
    			// later cast the return type to the correct format and return it:
    			// return (Element[])response;
    		} catch(Exception e) {
    			System.err.println("ERROR trying to invoke web service" + e);
    		} 
    		return "";
    	}
    	
    	/** Sending a describe request to the FieldQuery service in order to
    	 * find out what parameters a Greenstone 3 FieldQuery operation requires. 
    	 * This method invokes the web service operation describeCollectionService() 
    	 * for the FieldQuery service of gs2mgppdemo. */
    	public String describeFieldQueryService(String collection, String lang) {
    		// One of the query services available for collections gs2mgppdemo 
    		// and gs2mgdemo
    		final String FIELD_QUERY_SERVICE = "FieldQuery";
    		try {
    			// try invoking the process() method
    			Call call = (Call)this.service.createCall(
    				new QName(namespace, portName), 
    				new QName(namespace, DESCRIBE_COLLECTION_SERVICE_METHOD)
    			);
    			
    			// We know that the describeService returns a String, so we
    			// cast the return value appropriately.
    			// From the wsdl, the describeCollectionService web service  
    			// operation takes 4 String parameters: collection, service,
    			// language and subsetOption. 
    			// We'll pass "" for that last one, since we don't want to 
    			// retrieve a subset of the describeCollService XML response,
    			// but all of the response. 
    			String response = (String) call.invoke(new Object[]{
    					collection, FIELD_QUERY_SERVICE, lang, ""});
    			
    			// later cast the return type to the correct format and return it:
    			return response;
    			
    			// Note that for more complicated return types (like certain collection
    			// types or complex types), you may have to find out what the class
    			// of the return value is. For instance, when a group of elements are 
    			// returned, the return type may be Array or ArrayList or something similar.
    			// In such cases, you need to find the type of each element in the array. 
    			// This is necessary in order to cast the return value into the correct type.
    			// System.err.println("Return type is: " + response.getClass().getName());
    			// later cast the return type to the correct format and return it:
    			// return (Element[])response;
    		} catch(Exception e) {
    			System.err.println("ERROR trying to invoke web service" + e);
    		} 
    		return "";
    	}
    	
    
    	public static void main(String[] args) {
    		try {
    			// Constructor requires wsdl url of the localsite's web services.
    			// Once the QBRSOAPServer web services have been deployed, we can
    			// find this url off greenstone's services page
    			// http://HOST:PORT/greenstone3/services/
    			GreenstoneConnection gCon = new GreenstoneConnection(
    				"http://"+HOST+":"+PORT+"/greenstone3/services/QBRSOAPServerlocalsite?wsdl");
    						
    			final String language = "en"; // for English
    			String fieldQueryDescription = gCon.describeFieldQueryService(
    					"gs2mgppdemo", language);
    			
    			// Inspect the output in order to move on to the next step.
    			System.out.println("THE DESCRIPTION RETURNED FOR THE FieldQuery" 
    				+ " SERVICE IS: "  + fieldQueryDescription);
    			
    			// Now that we sent a describe message to the FieldQuery Service to 
    			// tell us the parameters required by the FieldQuery web service 
    			// operation, the XML response told us all the parameters required.
    			// Let's use the gs2mgppdemo collection's FieldQuery service to 
    			// query for "snails". The query web service operation takes a 
    			// Map of (name, value) pairs where name is the name of the query
    			// parameter (e.g. which indexed field to search in, 
    			// the search terms themselves or whether casefolding is on or off)
    			HashMap map = new HashMap();
    			map.put("maxDocs", "100");
    			map.put("level", "Sec");
    			map.put("accent", "1");
    			map.put("matchMode", "some");
    			map.put("fqf", "ZZ,ZZ,ZZ,ZZ");
    			map.put("case", "1");
    			map.put("sortBy", "1");
    			map.put("fqv", "snail,water,,");
    			
    			String queryResult = gCon.query("gs2mgppdemo", "FieldQuery", language, map);
    			
    			// print out the response to the query request
    			System.out.println("RESULT OF EXECUTING THE QUERY: " + queryResult);
    			
    		}catch (Exception e) {
    			System.err.println("In main. Exception: " + e);
    		}
    	}
    }
    
  3. In order to compile the above client code, the required jar files are
  4. To get above client code to run you need to have the necessary jar files in the run path as well. (If you try to run the application, it will throw errors telling you which jars are missing.) For running the above example code, you need to add the following jar files to the execution classpath: All of these can be found in the $GSDLHOME/web/WEB-INF/lib folder.
  5. Try executing the code in 2 above. My output is:
    THE DESCRIPTION RETURNED FOR THE FieldQuery SERVICE IS: 
    <message>
      <response from="gs2mgppdemo/FieldQuery" type="describe">
        <service name="FieldQuery" type="query">
          <displayItem name="name">Form Search</displayItem>
    
          <displayItem name="submit">Search</displayItem>
    
          <displayItem name="description">Simple fielded search</displayItem>
    
          <paramList>
            <param default="Sec" name="level" type="enum_single">
              <displayItem name="name">Granularity to search at</displayItem>
    
              <option name="Sec">
                <displayItem name="name">Section</displayItem>
              </option>
    
              <option name="Doc">
                <displayItem name="name">Document</displayItem>
              </option>
            </param>
    
            <param default="1" name="case" type="boolean">
              <displayItem name="name">Turn casefolding </displayItem>
    
              <option name="0">
                <displayItem name="name">off</displayItem>
              </option>
    
              <option name="1">
                <displayItem name="name">on</displayItem>
              </option>
            </param>
    
            <param default="1" name="stem" type="boolean">
              <displayItem name="name">Turn stemming </displayItem>
    
              <option name="0">
                <displayItem name="name">off</displayItem>
              </option>
    
              <option name="1">
                <displayItem name="name">on</displayItem>
              </option>
            </param>
    
            <param default="1" name="accent" type="boolean">
              <displayItem name="name">Turn accentfolding </displayItem>
    
              <option name="0">
                <displayItem name="name">off</displayItem>
              </option>
    
              <option name="1">
                <displayItem name="name">on</displayItem>
              </option>
            </param>
    
            <param default="some" name="matchMode" type="enum_single">
              <displayItem name="name">Match</displayItem>
    
              <option name="some">
                <displayItem name="name">some</displayItem>
              </option>
    
              <option name="all">
                <displayItem name="name">all</displayItem>
              </option>
            </param>
    
            <param default="1" name="sortBy" type="enum_single">
              <displayItem name="name">Document display order</displayItem>
    
              <option name="1">
                <displayItem name="name">ranked</displayItem>
              </option>
    
              <option name="0">
                <displayItem name="name">natural</displayItem>
              </option>
            </param>
    
            <param default="100" name="maxDocs" type="integer">
              <displayItem name="name">Maximum hits to return</displayItem>
            </param>
    
            <param name="simpleField" occurs="4" type="multi">
              <displayItem name="name"></displayItem>
    
              <param name="fqv" type="string">
                <displayItem name="name">Word or phrase </displayItem>
              </param>
    
              <param default="ZZ" name="fqf" type="enum_single">
                <displayItem name="name">in field</displayItem>
    
                <option name="ZZ">
                  <displayItem name="name">all fields</displayItem>
                </option>
    
                <option name="TX">
                  <displayItem name="name">text</displayItem>
                </option>
    
                <option name="DL">
                  <displayItem name="name"> titles</displayItem>
                </option>
    
                <option name="DS">
                  <displayItem name="name">subjects</displayItem>
                </option>
    
                <option name="DO">
                  <displayItem name="name">organisations</displayItem>
                </option>
              </param>
            </param>
          </paramList>
        </service>
      </response>
    </message>
    
    RESULT OF EXECUTING THE QUERY: 
    <message>
      <response from="gs2mgppdemo/FieldQuery" type="process">
        <metadataList>
          <metadata name="numDocsMatched">140</metadata>
    
          <metadata name="numDocsReturned">100</metadata>
    
          <metadata name="query"> snail water</metadata>
        </metadataList>
    
        <documentNodeList>
          <documentNode docType="hierarchy" nodeID="HASH011291f4f0794f8fcd324a43.13" nodeType="leaf" rank="74.75892" />
          <documentNode docType="hierarchy" nodeID="HASH0119e312094355aebcb59a77.7.1" nodeType="leaf" rank="40.254803" />
          ...
          <documentNode docType="hierarchy" nodeID="HASH01e7ccf5c76312e6b4caaaab.7.5" nodeType="leaf" rank="0.01744299" /></documentNodeList>
    
        <termList>
          <term field="" freq="58" name="snail" numDocsMatch="21" stem="5">
            <equivTermList>
              <term freq="" name="Snail" numDocsMatch="" />
              <term freq="" name="snail" numDocsMatch="" /></equivTermList>
          </term>
    
          <term field="" freq="585" name="water" numDocsMatch="120" stem="5">
            <equivTermList>
              <term freq="" name="Water" numDocsMatch="" />
              <term freq="" name="water" numDocsMatch="" /></equivTermList>
          </term>
        </termList>
      </response>
    </message>