source: other-projects/trunk/gs3-webservices-democlient/src/GS3DemoClient/org/greenstone/gs3services/QBRSOAPServer.java@ 16761

Last change on this file since 16761 was 16761, checked in by ak19, 16 years ago

Changed processInternal(), keeping it in sync with older change made to official QBRSOAPServer.java.in in Greenstone3 checkout. Now this method does not return a prettyString version of the response, but instead trims the response so there's no whitespace (illegal in XML) and prepends the official xml tag to the response

File size: 39.5 KB
Line 
1/**
2 *#########################################################################
3 * QBRSOAPServer.java - class that provides basic Query, Browse and
4 * Retrieve web service operations for Greenstone 3 and is part of the
5 * Greenstone digital library suite from the New Zealand Digital Library
6 * Project at the * University of Waikato, New Zealand.
7 * <BR><BR>
8 * Copyright (C) 2008 New Zealand Digital Library Project
9 * <BR><BR>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 * <BR><BR>
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
19 *########################################################################
20 */
21
22package org.greenstone.gs3services;
23
24import java.io.File;
25import java.io.InputStream;
26
27import java.util.Properties;
28import java.util.Enumeration;
29import java.util.Map;
30import java.util.Map.Entry;
31import java.util.HashMap;
32import java.util.Set;
33import java.util.Iterator;
34
35import org.w3c.dom.Document;
36import org.w3c.dom.Element;
37import org.w3c.dom.NodeList;
38import org.w3c.dom.Node;
39
40import org.greenstone.gsdl3.core.MessageRouter;
41import org.greenstone.gsdl3.util.GlobalProperties;
42import org.greenstone.gsdl3.util.GSFile;
43import org.greenstone.gsdl3.util.GSXML;
44import org.greenstone.gsdl3.util.XMLConverter;
45
46import org.apache.log4j.Logger; // Import log4j classes
47
48/*
49 * Add to $GSDLHOME/web/WEB-INF/server-config.wsdd:
50 * <service name="GS3WebServices" provider="java:RPC">
51 * <parameter name="allowedMethods" value="*"/>
52 * <parameter name="className" value="org.greenstone.gs3services.GS3WebServices"/>
53 * </service>
54*/
55
56/** Class that provides the basic Query, Browse and Retrieve (QBR) web service
57 * operations for Greenstone 3.
58 * It contains a MessageRouter that carries out all the tasks by calling the
59 * appropriate Greenstone functionality for each request message passed to it,
60 * and returning a response message.
61 *
62 * All response messages are returned from the MessageRouter to clients invoking
63 * the web services. All return values are strings that represent XML messages.
64 *
65 * Method help() reads from the file QBRWebServicesHelp.properties to list the web
66 * service operations available. Method helpWithMethod(String methodName)
67 * reads from the same file to display a description of the requested operation.
68 * (These method descriptions are mostly the same as those in the Javadoc
69 * comments.)
70 *
71 * NOTE: to run this class, the folder containing this web service class'
72 * properties helpFile should be on the classpath.
73 * @author ak19
74*/
75public class QBRSOAPServer {
76 /** The Logger for this class */
77 private static Logger LOG = Logger.getLogger(QBRSOAPServer.class);
78
79 /** Error message loading helpFile. Remains at "" if everything is fine */
80 protected static String helpErrormessage = "";
81 /** Properties map with mappings from methodname to help
82 * description string. */
83 protected static Properties properties;
84 /** The help properties file describing the web service operations */
85 protected static String helpFile = "QBRWebServicesHelp.properties";
86
87 // static code block to initialise the help Properties from the helpFile
88 static {
89 properties = new Properties();
90 // load the properties file from a location with respect to the
91 // the Web Service .class file
92 InputStream input = null;
93 try {
94 // load the properties file from a location with respect to the
95 // the Web Service .class file
96 input = QBRSOAPServer.class.getClassLoader().getResourceAsStream(
97 helpFile);
98 if(input == null) {
99 helpErrormessage = "Cannot find file " + helpFile + " to load.";
100 LOG.warn(helpErrormessage);
101 } else {
102 properties.load(input);
103 input.close();
104 }
105 } catch(Exception e) {
106 helpErrormessage = "Exception loading properties from help file "
107 + helpFile + "\n" + e.getMessage();
108 LOG.warn("Exception loading properties from help file "
109 + helpFile + "\n" + e.getMessage());
110 }
111 }
112
113 /** site_name the MessageRouter works with, here set to "localsite" */
114 protected String site_name = "localsite";
115
116 /** Message Router object to pass requests messages to and which
117 * will process them.*/
118 protected MessageRouter mr = null;
119
120 /** Container Document to create XML Nodes */
121 protected Document doc=null;
122 /** A converter class to parse XML and create Docs */
123 protected XMLConverter converter=null;
124
125 /* Describe subset options for the various Greenstone3 modules */
126 protected static final String mrSubsetOptions = // messageRouter
127 "collectionList serviceClusterList serviceList siteList";
128 protected static final String csSubsetOptions = // collections and serviceClusters
129 "metadataList serviceList displayItemList";
130 protected static final String serviceSubsetOptions = // services
131 "paramList displayItemList";
132 protected static final String docStructureOptions =
133 "entire ancestors parent siblings children descendants"; // note the spelling
134 protected static final String docStructureInfo =
135 "numSiblings siblingPosition numChildren";
136 protected static final String browseStructureOptions =
137 "ancestors parent siblings children descendants"; // note the spelling
138
139
140 /** Constructor that initializes the web services' MessageRouter object
141 * Reads from GlobalProperties to get gsdl3_home and set the sitename. */
142 public QBRSOAPServer() {
143 String gsdl3_home = GlobalProperties.getGSDL3Home();
144 if (gsdl3_home == null || gsdl3_home.equals("")) {
145 LOG.error(
146 "Couldn't access GSDL3Home from GlobalProperties.getGSDL3HOME,"
147 + "can't initialize the SOAP Server.");
148 return;
149 }
150
151 String site_home = GSFile.siteHome(gsdl3_home, this.site_name);
152
153 File site_file = new File(site_home);
154 if (!site_file.isDirectory()) {
155 LOG.error("The site directory "+site_file.getPath()
156 +" doesn't exist. Can't initialize the SOAP Server.");
157 return;
158 }
159 this.converter = new XMLConverter();
160 this.doc = this.converter.newDOM();
161
162 mr = new MessageRouter();
163 mr.setSiteName(this.site_name);
164 mr.configure();
165 }
166
167 /* (1) DESCRIBE MESSAGES, manual pages 35-41 */
168 /** Sends a describe message to the MessageRouter.
169 * @param lang is the language of the display content in the response.
170 * @param subsetOption are the requested list of items to return in the
171 * response. For the Message Router this can be collectionList,
172 * serviceClusterList, serviceList, siteList
173 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
174 */
175 public String describe(String lang, String subsetOption)
176 {
177 return describe("", lang, subsetOption, mrSubsetOptions);
178 }
179
180 /** For sending Describe messages to ServiceClusters.
181 * @param serviceCluster is the name of the Service Cluster that this describe
182 * request is sent to.
183 * @param lang is the language of the display content in the response
184 * @param subsetOption is the requested list of items to return in the response
185 * For Service Clusters this can be metadataList, serviceList, displayItemList.
186 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
187 */
188 public String describeServiceCluster(
189 String serviceCluster, String lang, String subsetOption)
190 {
191 return describe(serviceCluster, lang, subsetOption, csSubsetOptions);
192 }
193
194 /** For sending Describe messages to Collections.
195 * @param collection is the name of the Collection that this describe request
196 * is sent to.
197 * @param lang is the language of the display content in the response
198 * @param subsetOption is the requested list of items to return in the response
199 * For Collections this can be metadataList, serviceList and displayItemList.
200 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
201 */
202 public String describeCollection(
203 String collection, String lang, String subsetOption)
204 {
205 return describe(collection, lang, subsetOption, csSubsetOptions);
206 }
207
208 /**
209 * For sending a describe message to a Collection's Service.
210 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
211 * @param collection is the name of the Collection whose service
212 * this describe request is sent to.
213 * @param service is the name of the Service (of that collection) to
214 * which this describe request is sent.
215 * @param lang is the language of the display content in the response
216 * @param subsetOption is the requested list of items to return in the response
217 * For Services this can be paramList, displayItemList */
218 public String describeCollectionService(String collection, String service,
219 String lang, String subsetOption)
220 {
221 return describe(collection + "/" + service,
222 lang, subsetOption, serviceSubsetOptions);
223 }
224
225 /**
226 * For sending a describe message to a Service hosted by the Message Router
227 * (no collection).
228 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
229 * @param service is the name of the MessageRouter's Service to which this
230 * describe request is sent.
231 * @param lang is the language of the display content in the response
232 * @param subsetOption is the requested list of items to return in the response
233 * For Services this can be paramList, displayItemList
234 */
235 public String describeService(
236 String service, String lang, String subsetOption)
237 {
238 return describe(service, lang, subsetOption, serviceSubsetOptions);
239 }
240
241 /** For sending a describe message.
242 * If public, this method would give full access: a describe message that
243 * lets the user specify all the details of who the receiver is, and what
244 * details are requested.
245 * @param to - the Greenstone module (MessageRouter, Collection,
246 * ServiceCluster or (Collection-)Service to send this describe message to.
247 * (The module asked to describe itself.)
248 * @param lang - the language of the display content in the response.
249 * @param subsetOption - the set of elements of the describe response that
250 * are requested. These vary depending on the GS3 module asked to describe
251 * itself.
252 * @param validSubsetOptions - the list of subsetOptions that are allowed
253 * for the module this describe message is sent to. Parameter subsetOption
254 * has to be among the list of validSubsetOptions.
255 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 35-41</a>
256 */
257 protected String describe(String to, String lang,
258 String subsetOption, String validSubsetOptions)
259 {
260 // Create message element: <message></message>
261 Element message = this.doc.createElement(GSXML.MESSAGE_ELEM);
262 // <message><request lang="en" to="" type="describe" uid="" /></message>
263 Element request = GSXML.createBasicRequest(
264 this.doc, GSXML.REQUEST_TYPE_DESCRIBE, to, lang, "");
265
266 // Check if only a subset of this Module Interface's data is asked
267 // to be described
268 if(!subsetOption.equals("")) {
269 // Now deal with the value for subset param:
270 // only deal with valid params for subset of to-ModuleInterface
271 if(validSubsetOptions.indexOf(subsetOption) == -1)
272 return error("Invalid List to be described. Choose one of:\n"
273 + validSubsetOptions);
274
275 // else, append <paramList>
276 // <param name="subset" value="subsetOption" /></paramList>
277 Element paramList = this.doc.createElement(
278 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
279 // append <param name="subset" value=paramValue />
280 // createParam(Document, name, value);
281 // Name needs to be "subset", but this can be either GSXML.SUBSET_PARAM
282 // or GSXML.SYSTEM_SUBSET_ATT. It's the first one probably.
283 paramList.appendChild(GSXML.createParameter(
284 this.doc, GSXML.SUBSET_PARAM, subsetOption));
285 request.appendChild(paramList);
286 }
287 message.appendChild(request);
288
289 // Send it off to the Message Router and return the response
290 return this.processInternal(message);
291 }
292
293 /* (2) Process-type message, QUERY-TYPE SERVICES - p.45 */
294 /** For executing a (process-type message) query-type service.
295 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 45</a>
296 * @param collection is the name of the Collection whose query service this
297 * query-process request is sent to. If "", then the Message Router is assumed.
298 * @param service is the name of the Query Service (of that collection) to
299 * which this request is sent.
300 * @param lang is the language of the display content in the response
301 * @param nameToValsMap is a Map of the (fieldname, value) pairs for the
302 * parameters of the query. The field names should be those recognised by
303 * Greenstone 3. That is, the names must exist for the (Collection-)Service Query that this
304 * message is sent To (as given in 'to' argument).
305 * For names of Greenstone-accepted arguments,
306 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Actions_and_Arguments">Greenstone wiki - Actions and Arguments</a>
307 */
308 public String query(String collection, String service,
309 String lang, Map nameToValsMap)
310 {
311 // <paramList></paramList>
312 Element paramList = this.doc.createElement(
313 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
314 // <param>s: creating parameters of (name, value) pairs
315 Set entrySet = nameToValsMap.entrySet();
316 Iterator i = entrySet.iterator();
317 while(i.hasNext()) {
318 Entry entry = (Entry)i.next();
319 String name = (String)entry.getKey();
320 String value = (String)entry.getValue();
321 paramList.appendChild(GSXML.createParameter(
322 this.doc, name, value));
323 }
324
325 Element message = this.doc.createElement(GSXML.MESSAGE_ELEM);
326 Element request = GSXML.createBasicRequest(
327 this.doc, GSXML.REQUEST_TYPE_PROCESS,
328 collection+"/"+service, lang, "");
329
330 request.appendChild(paramList);
331 message.appendChild(request);
332
333 // Send it off to the Message Router and return the response
334 return this.processInternal(message);
335 }
336
337 /**
338 * This method is used to perform the most basic query of them:
339 * it assumes defaults for all other parameters and provides only
340 * the query string. It is built on top of a TextQuery.
341 * @param collection is the Greenstone collection to be searched
342 * @param lang is the preferred language of the display content in
343 * the response to be returned.
344 * @param query is the string to be sought in the Greenstone collection
345 * @return a Greenstone 3 XML response message for the query specifying
346 * the search results.
347 */
348 public String basicQuery(String collection, String lang, String query) {
349 // The basicQuery is built on top of the TextQuery service
350 final String queryService = "TextQuery";
351
352 // (1) describe request on the TextQuery
353 String queryDescription = describeCollectionService(
354 collection, queryService, "en", "paramList"); // just get paramList
355 //System.out.println(queryDescription);
356
357 Document doc = this.converter.getDOM(queryDescription);
358 NodeList nl = doc.getElementsByTagName(
359 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
360
361 Element paramList = null;
362 if(nl.getLength() <= 0) { // no paramList in textQuery description means
363 // no query field either: that means we can't continue
364 return this.error("BasicQuery is not available for this collection"
365 + " as it provides no TextQuery service.");
366 } //else
367
368 paramList = (Element)nl.item(0);
369 nl = paramList.getElementsByTagName(GSXML.PARAM_ELEM);
370 if(nl.getLength() <= 0) { // no params, means no query field, so return
371 return this.error("BasicQuery is not available for this collection.");
372 }
373
374 // (2) get the defaults for each parameter and use that to set
375 // the defaults
376 Map params = new HashMap(nl.getLength()); // field name to value map
377 for(int i = 0; i < nl.getLength(); i++) {
378 Element param = (Element)nl.item(i);
379 String paramName = param.getAttribute(GSXML.NAME_ATT);
380 String def = param.getAttribute(GSXML.DEFAULT_ATT);
381 if(def.equals("")) {
382 // if there's no default, the field must want the query String
383 params.put(paramName, query);
384 } else { // there is a default, use the default for this param
385 params.put(paramName, def);
386 }
387 }
388
389 // (3) Perform the query using defaults and return the response
390 return this.query(collection, queryService, lang, params);
391 }
392
393 /* (3) RETRIEVE PROCESS METHODS - Manual, pp.47-49 */
394 /** DocumentContentRetrieve request sent to a collection's
395 * DocumentContentRetrieve service (see manual, p.48)
396 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 48</a>
397 * @param collection is the name of the Collection whose
398 * DocumentContentRetrieve is requested
399 * @param lang is the language of the display content in the response
400 * @param docNodeIDs is the list of documentNodeIDs for which the
401 * content ought to be retrieved. */
402 public String retrieveDocumentContent(
403 String collection, String lang, String[] docNodeIDs)
404 {
405 // creating <documentNodeList></documentNodeList>
406 Element docNodeList = this.doc.createElement(
407 GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
408
409 // creating subelements: <documentNode nodeID="..." />
410 for(int i = 0; i < docNodeIDs.length; i++) {
411 Element docNode = this.doc.createElement(GSXML.DOC_NODE_ELEM);
412 docNode.setAttribute(GSXML.NODE_ID_ATT, docNodeIDs[i]);
413 docNodeList.appendChild(docNode);
414 }
415
416 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
417 Element request = GSXML.createBasicRequest(
418 doc, GSXML.REQUEST_TYPE_PROCESS,
419 collection+"/DocumentContentRetrieve", lang, "");
420
421 // create an empty <paramlist /> element (as example in manual)
422 Element paramlist = doc.createElement(
423 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
424
425 request.appendChild(paramlist);
426 request.appendChild(docNodeList);
427 message.appendChild(request);
428
429 // Send it off to the Message Router and return the response
430 return this.processInternal(message);
431 }
432
433 /** DocumentStructureRetrieve request sent to a collection's
434 * DocumentStructureRetrieve service (manual pp.48, 49) to retrieve
435 * the entire document structure.
436 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 48, 49</a>
437 * @param collection is the name of the Collection whose
438 * DocumentStructureRetrieve is requested
439 * @param lang is the language of the display content in the response
440 * @param docNodeIDs is the list of documentNodeIDs for which the
441 * entire structure ought to be retrieved. */
442 public String retrieveEntireDocumentStructure(String collection,
443 String lang, String[] docNodeIDs)
444 {
445 return retrieveDocumentStructure(collection, lang, docNodeIDs,
446 new String[] { "entire" }, null);
447 }
448
449 /** DocumentStructureRetrieve request sent to a collection's
450 * DocumentStructureRetrieve service (manual pp.48, 49) to retrieve
451 * the specified part of the document's structure.
452 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 48, 49</a>
453 * @param collection is the name of the Collection whose
454 * DocumentStructureRetrieve is requested
455 * @param lang is the language of the display content in the response
456 * @param docNodeIDs is the list of documentNodeIDs for which the
457 * structure ought to be retrieved.
458 * @param structure specifies what structure information needs to
459 * be retrieved. The values can be one or more of ancestors, parent,
460 * siblings, children, descendants (<b>note spelling</b>), entire.
461 * @param info - for specifying extra information to be retrieved.
462 * Possible values for info parameters are numSiblings, siblingPosition,
463 * numChildren */
464 public String retrieveDocumentStructure(String collection, String lang,
465 String[] docNodeIDs, String[] structure, String[] info)
466 {
467 // creating subelements: <documentNode nodeID="..." />
468 Element docNodeList = this.doc.createElement(
469 GSXML.DOC_NODE_ELEM+GSXML.LIST_MODIFIER);
470 for(int i = 0; i < docNodeIDs.length; i++) {
471 Element docNode = this.doc.createElement(GSXML.DOC_NODE_ELEM);
472 docNode.setAttribute(GSXML.NODE_ID_ATT, docNodeIDs[i]);
473 docNodeList.appendChild(docNode);
474 }
475
476 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
477 Element request = GSXML.createBasicRequest(
478 doc, GSXML.REQUEST_TYPE_PROCESS,
479 collection+"/DocumentStructureRetrieve", lang, "");
480
481 // Create the <paramlist></paramlist> element of param elements,
482 // if any; and only if values are legal (that is, if they occur in
483 // static Strings docStructureOptions and docStructureInfo):
484 // <param name="structure" value = "structure[i]">
485 // <param name="info" value = "info[i]">
486 Element paramList = doc.createElement(
487 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
488
489 if(structure != null) {
490 for(int i = 0; i < structure.length; i++) {
491 if(docStructureOptions.indexOf(structure[i]) != -1) {
492 paramList.appendChild(GSXML.createParameter(
493 this.doc, "structure", structure[i]));
494 }
495 }
496 }
497 if(info != null) {
498 for(int i = 0; i < info.length; i++) {
499 if(docStructureInfo.indexOf(info[i]) != -1) {
500 paramList.appendChild(GSXML.createParameter(
501 this.doc, "info", info[i]));
502 }
503 }
504 }
505
506 // paramList is allowed to be empty and may indeed be empty:
507 request.appendChild(paramList);
508 request.appendChild(docNodeList);
509 message.appendChild(request);
510
511 // Send it off to the Message Router and return the response
512 return this.processInternal(message);
513 }
514
515 /* Retrieve for Doc Metadata: explained in the manual on page 47 */
516 /** DocumentMetadataRetrieve request sent to a collection's
517 * DocumentMetadataRetrieve service to retrieve all of a document's metadata.
518 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 47</a>
519 * @param collection is the name of the Collection whose
520 * DocumentMetadataRetrieve is requested
521 * @param lang is the language of the display content in the response
522 * @param docNodeIDs is the list of documentNodeIDs for which the
523 * structure ought to be retrieved.
524 */
525 public String retrieveAllDocumentMetadata(String collection, String lang,
526 String[] docNodeIDs)
527 {
528 // See bottom of manual p.44 for the fact that "all" is used
529 // as the metaName value when retrieving all metadata for a doc
530 return retrieveDocumentMetadata(collection, lang, docNodeIDs,
531 new String[]{ "all" });
532 }
533
534 /** DocumentMetadataRetrieve service to retrieve some specific
535 * metadata values of a document. (Manual on page 47.)
536 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 47</a>
537 * @param collection is the name of the Collection whose
538 * DocumentContentRetrieve is requested
539 * @param lang is the language of the display content in the response
540 * @param docNodeIDs is the list of documentNodeIDs for which the
541 * structure ought to be retrieved.
542 * @param metaNames is a list of metadata names which are requested
543 * to be fetched for the specified documents */
544 public String retrieveDocumentMetadata(String collection, String lang,
545 String[] docNodeIDs, String[] metaNames)
546 {
547 return metadataRetrieve(collection+"/DocumentMetadataRetrieve",
548 lang, docNodeIDs, metaNames, GSXML.DOC_NODE_ELEM);
549 }
550
551 /** Retrieve all classification Metadata for browsing (sent to the
552 * ClassifierBrowseMetadataRetrieve service).
553 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 47, 48</a>
554 * @param collection is the name of the Collection whose
555 * ClassifierBrowseMetadataRetrieve service is called
556 * @param categoryName - name of the browsing category, usually
557 * ClassifierBrowse. (If left as "", then it defaults to ClassifierBrowse)
558 * @param lang is the language of the display content in the response
559 * @param nodeIDs is the list of document or classifier NodeIDs
560 * for which the metadata ought to be retrieved.*/
561 public String retrieveAllBrowseMetadata(String collection,
562 String categoryName, String lang, String[] nodeIDs)
563 {
564 if(categoryName.equals(""))
565 categoryName = "ClassifierBrowse";
566 // See bottom of manual p.47 for the fact that "all" is used as
567 // the metaName value when retrieving all metadata for a classifier
568 return metadataRetrieve(collection+"/"+categoryName+"MetadataRetrieve",
569 lang, nodeIDs, new String[]{ "all" }, GSXML.CLASS_NODE_ELEM);
570 }
571
572 /** ClassifierBrowseMetadataRetrieve service to retrieve some specific
573 * metadata values of a document.
574 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - pages 47, 48</a>
575 * @param collection is the name of the Collection whose
576 * ClassifierBrowseMetadataRetrieve service is called
577 * @param categoryName - name of the browsing category, usually
578 * ClassifierBrowse. (If left as "", then it defaults to ClassifierBrowse)
579 * @param lang is the language of the display content in the response
580 * @param nodeIDs is the list of document or classifier NodeIDs
581 * for which the metadata ought to be retrieved.
582 * @param metaNames is a list of metadata names which are requested
583 * to be fetched for the specified documents or classifiers */
584 public String retrieveBrowseMetadata(String collection, String categoryName,
585 String lang, String[] nodeIDs, String[] metaNames)
586 {
587 if(categoryName.equals(""))
588 categoryName = "ClassifierBrowse";
589 return metadataRetrieve(collection+"/"+categoryName+"MetadataRetrieve",
590 lang, nodeIDs, metaNames, GSXML.CLASS_NODE_ELEM);
591 }
592
593 /** Performs a metadata retrieve for documents and (browse) classification
594 * hierarchies. Sends a Document- or ClassifierBrowse- MetadataRetrieve message
595 * to the Document- or ClassifierBrowse- MetadataRetrieve service.
596 * @param to - the Document- or ClassifierBrowse- MetadataRetrieve service to
597 * send this metadata retrieve message to.
598 * @param lang - the language of the display content in the response
599 * @param nodeIDs - the list of (document or classifier) nodeIDs for which
600 * to retrieve the metadata for
601 * @param metaNames - a list specifiying the names of the metadata items
602 * to be retrieved for each nodeID. E.g. "Title", but a list is allowed.
603 * @param NODE_ELEM - either of GSXML's names for the &lt;documentNode&gt; or
604 * &lt;classifierNode&gt; elements.
605 */
606 protected String metadataRetrieve(String to, String lang,
607 String[] nodeIDs, String[] metaNames, final String NODE_ELEM)
608 {
609 // create the <paramlist></paramlist> element of param elements:
610 // <param name="metadata" value = "metaName[i]">
611 Element metadataParamList = this.doc.createElement(
612 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
613 for(int i = 0; i < metaNames.length; i++) {
614 metadataParamList.appendChild(GSXML.createParameter(
615 this.doc, GSXML.METADATA_ELEM, metaNames[i]));
616 }
617
618 // creating subelements: <documentNode nodeID="..." />
619 // or <classifierNode nodeID="..." />
620 Element nodeList = this.doc.createElement(
621 NODE_ELEM+GSXML.LIST_MODIFIER);
622 for(int i = 0; i < nodeIDs.length; i++) {
623 Element docNode = this.doc.createElement(NODE_ELEM);
624 docNode.setAttribute(GSXML.NODE_ID_ATT, nodeIDs[i]);
625 nodeList.appendChild(docNode);
626 }
627
628 Element message = doc.createElement(GSXML.MESSAGE_ELEM);
629 Element request = GSXML.createBasicRequest(doc,
630 GSXML.REQUEST_TYPE_PROCESS, to, lang, "");
631
632 request.appendChild(metadataParamList);
633 request.appendChild(nodeList);
634 message.appendChild(request);
635
636 // Send it off to the Message Router and return the response
637 return this.processInternal(message);
638 }
639
640 /* (4) Classifier BROWSE PROCESS METHODS - p.46 */
641 /** To send a browse request for all the descendants of a classifier node.
642 * Useful for getting the entire structure of a top-level &lt;classificationNode&gt;
643 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 46</a>
644 * @param collection is the name of the Collection whose browse Classifier
645 * Browse Service is called
646 * @param browseService is the name of the (Classifier) Browse Service (of
647 * the given collection) to which this request message is sent.
648 * @param lang is the language of the display content in the response
649 * @param classifierNodeIDs is an array of classifierNodeIDs for which the
650 * structures ought to be retrieved.
651 */
652 public String browseDescendants(String collection, String browseService,
653 String lang, String[] classifierNodeIDs)
654 {
655 // We are at the top level, we want all the descendants:
656 // <param name="structure" value = "descendants">
657 // <classifierNodeList><classifier nodeID="CLx" /></classifierNodeList>
658 return browse(collection, browseService, lang,
659 classifierNodeIDs,
660 new String[] {"descendants"}); // note the spelling
661 }
662
663 /** To send a browse request for specific parts of a classifier node
664 * (children, ancestors, descendants). Useful for getting specific parts
665 * of the structure of a top-level &lt;classificationNode&gt;.
666 * @see <a href="http://wiki.greenstone.org/wiki/index.php/Greenstone3">The Greenstone 3 Developer's Manual - page 46</a>
667 * @param collection is the name of the Collection whose browse Classifier
668 * Browse Service is called
669 * @param browseService is the name of the (Classifier) Browse Service (of
670 * the given collection) to which this request message is sent.
671 * @param lang is the language of the display content in the response
672 * @param classifierNodeIDs is the list of classifierNodeIDs for which the
673 * structure ought to be retrieved.
674 * @param structureParams the list of parameters indicating what structure
675 * information is requested. Accepted values are ancestors, parent, siblings,
676 * children, descendants.
677 */
678 public String browse(String collection, String browseService, String lang,
679 String[] classifierNodeIDs, String[] structureParams)
680 {
681 if(browseService.equals(""))
682 browseService = "ClassifierBrowse";
683
684 // Create message element: <message></message>
685 Element message = this.doc.createElement(GSXML.MESSAGE_ELEM);
686 // <message><request lang="en" to="" type="process" uid="" /></message>
687 Element request = GSXML.createBasicRequest(this.doc,
688 GSXML.REQUEST_TYPE_PROCESS, collection+"/"+browseService, lang, "");
689
690 // <param name="structure" value = "structureParams[i]">
691 Element paramList = this.doc.createElement(
692 GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
693 for(int i = 0; i < structureParams.length; i++) {
694 // check it is a valid structure parameter
695 if(browseStructureOptions.indexOf(structureParams[i]) != -1) {
696 paramList.appendChild(GSXML.createParameter(
697 this.doc, "structure", structureParams[i]));
698 }
699 }
700
701 // <classifierNodeList><classifier nodeID="CLx" />
702 // <classifier nodeID="CLy" /></classifierNodeList>
703 // where CLx and CLy are given in the parameter classifierNodeIDs
704 Element classifierNodeList = this.doc.createElement(
705 GSXML.CLASS_NODE_ELEM+GSXML.LIST_MODIFIER);
706 for(int i = 0; i < classifierNodeIDs.length; i++) {
707 Element classifier = this.doc.createElement(GSXML.CLASS_NODE_ELEM);
708 classifier.setAttribute(GSXML.NODE_ID_ATT, classifierNodeIDs[i]);
709 classifierNodeList.appendChild(classifier);
710 }
711
712 // now finish constructing the request message:
713 request.appendChild(paramList);
714 request.appendChild(classifierNodeList);
715 message.appendChild(request);
716
717 // Send it off to the Message Router and return the response
718 return this.processInternal(message);
719 }
720
721 /** Called by most other methods in order to send the constructed message
722 * to the Greenstone's MessageRouter, intercept the response and return it.
723 * @param message is the XML message Element to send to GS3's MessageRouter.
724 * @return the XML response in String format. */
725 protected String processInternal(Element message) {
726 // Let the messagerouter process the request message and get the response
727 LOG.debug(this.converter.getPrettyString(message));
728
729 // Let the messagerouter process the request message and get the response
730 Element response = converter.nodeToElement(mr.process(message));
731 // won't be null except when Node returned is not an element
732 // otherwise, MR always returns some response
733
734 // In order to avoid "Content is not allowed in prolog" exception on the
735 // web services' client end (problem encountered in GS mailing list), need
736 // to make sure no characters (incl spaces) preceed the XML sent back
737 // from here. It may also require the <?xml?> tag at the very start.
738 if(responseMsg.charAt(0) == ' ') {
739 responseMsg = responseMsg.trim();
740 }
741
742 return "<?xml version=\"1.0\" encoding=\"utf-8\"?>"+responseMsg;
743
744 }
745
746 /** Creates a String response message to represent an XML error response
747 * message using the error specified in the message parameter. A String is
748 * created because this method ensures that a response message is reliably
749 * constructed (no exceptions are thrown) that can be sent to clients.
750 * @param errorMessage - the errormessage to be conveyed
751 * @return an XML response message containing an GS3 error element. */
752 protected String error(String errorMessage) {
753 StringBuffer buf = new StringBuffer("<" + GSXML.MESSAGE_ELEM + ">");
754 buf.append("<" + GSXML.RESPONSE_ELEM + " "
755 + GSXML.FROM_ATT + "=\"" + "Greenstone 3 Web Services\"" + ">");
756 buf.append("<" + GSXML.ERROR_ELEM + " "
757 + GSXML.ERROR_TYPE_ATT + "=\""+ GSXML.ERROR_TYPE_OTHER + "\"" + ">");
758 buf.append(errorMessage+"\n");
759 buf.append("</" + GSXML.ERROR_ELEM + ">");
760 buf.append("</" + GSXML.RESPONSE_ELEM + ">");
761 buf.append("</" + GSXML.MESSAGE_ELEM + ">");
762 return buf.toString();
763 }
764
765 /*
766 Look in file QBRWebServicesHelp.properties
767 - Have a properties file that maps methodname to help string specific
768 to the method.
769 - Read it all in statically at the start of the class, into a Properties Map.
770 - When this method is called, display the usage: "help methodname"
771 and list all the available methods by going over the keys in the Map.
772 - When the helpWithMethod(String methodname) method is called, return the
773 value of the Map for the methodname key. This value would be the help
774 description for that method.
775 */
776 /** @return a help string for listing all the web service methods. */
777 public static String help() {
778 if(!helpErrormessage.equals("")) {
779 return helpErrormessage;
780 }
781
782 StringBuffer helpString = new StringBuffer(
783 "USAGE: helpWithMethod(String <method name>)\n");
784 helpString.append(
785 "\nNearly all the web service operations return a String\n");
786 helpString.append(
787 "representing a Greenstone 3 XML response message.\n");
788 helpString.append("\nA list of all the method names: \n");
789
790 Enumeration props = properties.keys();
791 while(props.hasMoreElements()){
792 String methodName = (String)props.nextElement();
793 helpString.append("\t");
794 helpString.append(methodName);
795 helpString.append("\n");
796 }
797
798 return helpString.toString();
799 }
800
801 /** @param methodname is the name of the method to be described.
802 * @return a help string for the given method, explaining what the method
803 * does, what parameters it expects and their types and what it returns.
804 */
805 public static String helpWithMethod(String methodname) {
806 if(!helpErrormessage.equals("")) {
807 return helpErrormessage;
808 }
809 // else we can get the method's description from the properties
810 // map loaded from the QBRWebServicesHelp.properties file:
811 String helpString = properties.getProperty(methodname,
812 "No description for " + methodname); // if the method does not exist
813
814 return helpString;
815 }
816
817
818 // NOTE: the main method of this web service class won't work while it's
819 // being exposed as a web service (while its copy of the .class file
820 // is in $GSDLHOME/web/WEB-INF/classes/<this-class'-package-structure>/.)
821 // where the main method (of the exposed class file) is commented out.
822 // Testing all the web service methods
823 public static void main(String[] args)
824 {
825 QBRSOAPServer ws = new QBRSOAPServer();
826
827 // (1) trying 4 describe methods
828 System.out.println(ws.describe("", ""));
829 System.out.println(ws.describeCollection("gs2mgppdemo", "", ""));
830 //?AnyExampleServiceClusters?//System.out.println(ws.describeServiceCluster("", "", ""));
831 System.out.println(ws.describeCollectionService("gs2mgppdemo", "AdvancedFieldQuery", "", ""));
832 System.out.println(ws.describeService("AddItem", "", ""));
833
834 // (2) try 2 query methods: query and basicQuery
835 HashMap map = new HashMap();
836 map.put("maxDocs", "100");
837 map.put("level", "Sec");
838 map.put("accent", "1");
839 map.put("matchMode", "some");
840 map.put("fqf", "ZZ,ZZ,ZZ,ZZ");
841 map.put("case", "1");
842 map.put("sortBy", "1");
843 map.put("fqv", "snail,water,,");
844
845 String response = ws.query("gs2mgppdemo", "FieldQuery", "", map);
846 System.out.println("Regular query: " + response);
847
848 // basicQuery
849 response = ws.basicQuery("gs2mgppdemo", "", "cat");
850 System.out.println("Basic query on 'cat' over collection gs2mgppdemo:"
851 + response);
852
853 // (3) try 2 browse
854 System.out.println("describe browse:\n"
855 + ws.describeCollectionService("gs2mgppdemo", "ClassifierBrowse", "", ""));
856
857 System.out.println("browse children of CL1-CL4:\n" +
858 ws.browse("gs2mgppdemo", "ClassifierBrowse", "",
859 new String[]{"CL1", "CL2", "CL3", "CL4"}, new String[]{"children"}));
860
861 System.out.println("browse descendants of CL2.3:\n" +
862 ws.browseDescendants("gs2mgppdemo", "ClassifierBrowse", "",
863 new String[]{"CL2.3"}));
864
865 // (4) try 2 DocStructure
866 System.out.println("retrieve ancestors and children structure of HASH016193b2847874f3c956d22e.4:\n" +
867 ws.retrieveDocumentStructure("gs2mgppdemo", "",
868 new String[]{"HASH016193b2847874f3c956d22e.4"},
869 new String[]{"ancestors", "children"}, new String[]{"numSiblings"}));
870
871
872 System.out.println("retrieve entire structure of HASH016193b2847874f3c956d22e.4.1:\n" +
873 ws.retrieveEntireDocumentStructure(
874 "gs2mgppdemo", "", new String[]{"HASH016193b2847874f3c956d22e.4.1"}));
875
876 // (5) try the 1 DocumentContent retrieve
877 System.out.println("retrieve content of HASH016193b2847874f3c956d22e.4:\n" +
878 ws.retrieveDocumentContent("gs2mgppdemo", "",
879 new String[]{"HASH016193b2847874f3c956d22e.4", "HASH016193b2847874f3c956d22e.4.1",
880 "HASH016193b2847874f3c956d22e.4.2"}));
881
882
883 // (6) try 2 DocumentMeta
884 System.out.println("retrieve title meta of HASH016193b2847874f3c956d22e.4 children:\n" +
885 ws.retrieveDocumentMetadata("gs2mgppdemo", "",
886 new String[]{"HASH016193b2847874f3c956d22e.4.1", "HASH016193b2847874f3c956d22e.4.2",
887 "HASH016193b2847874f3c956d22e.4.3"}, new String[]{"Title"}));
888
889 System.out.println("retrieve all meta of HASH016193b2847874f3c956d22e.4 children:\n" +
890 ws.retrieveAllDocumentMetadata("gs2mgppdemo", "",
891 new String[]{"HASH016193b2847874f3c956d22e.4",
892 "HASH016193b2847874f3c956d22e.4.1", "HASH016193b2847874f3c956d22e.4.2",
893 "HASH016193b2847874f3c956d22e.4.3"}));
894
895 // (7) try 2 BrowseMeta
896 System.out.println("retrieve all browse meta of CL1, CL2, CL2.1, CL3:\n" +
897 ws.retrieveAllBrowseMetadata("gs2mgppdemo", "", "",
898 new String[]{"CL1", "CL2",
899 "CL2.1", "CL3"}));
900
901 System.out.println("retrieve Title, hastxt browse meta of CL1, CL2, CL2.1, CL3:\n" +
902 ws.retrieveBrowseMetadata("gs2mgppdemo", "", "",
903 new String[]{"CL1", "CL2","CL2.1", "CL3"}, new String[]{"Title", "hastxt"}));
904
905 // (8) try help
906 System.out.println(ws.help());
907 System.out.println(ws.helpWithMethod("describe"));
908 System.out.println(ws.helpWithMethod("help"));
909 System.out.println(ws.helpWithMethod("helpWithMethod"));
910
911
912 System.out.println("Finished executing");
913
914 } // end main method
915
916} // end web service class
Note: See TracBrowser for help on using the repository browser.