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

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