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

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

Corrected utf-8 to UTF-8 when specifying the XML encoding

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.