source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/service/ServiceRack.java@ 30629

Last change on this file since 30629 was 30629, checked in by kjdon, 8 years ago

moved getDisplayText into ServiceRack from GSXML so that if there is a key specified we can use getTextString to look it up

  • Property svn:keywords set to Author Date Id Revision
File size: 17.6 KB
Line 
1/*
2 * ServiceRack.java
3 * Copyright (C) 2002 New Zealand Digital Library, http://www.nzdl.org
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19package org.greenstone.gsdl3.service;
20
21// greenstone classes
22import java.lang.reflect.Method;
23import java.util.HashMap;
24
25import org.apache.log4j.Logger;
26import org.greenstone.gsdl3.collection.ServiceCluster;
27import org.greenstone.gsdl3.core.MessageRouter;
28import org.greenstone.gsdl3.core.ModuleInterface;
29import org.greenstone.gsdl3.util.CollectionClassLoader;
30import org.greenstone.gsdl3.util.Dictionary;
31import org.greenstone.gsdl3.util.GSPath;
32import org.greenstone.gsdl3.util.GSXML;
33import org.greenstone.gsdl3.util.XMLConverter;
34import org.w3c.dom.Document;
35import org.w3c.dom.Element;
36import org.w3c.dom.Node;
37import org.w3c.dom.NodeList;
38
39import java.io.StringWriter;
40import java.io.PrintWriter;
41
42/**
43 * ServiceRack - abstract base class for services
44 *
45 * A ServiceRack provides one or more Services. This base class implements the
46 * process method. Each service is invoked by a method called process<service
47 * name> which takes one parameter - the xml request Element, and returns an XML
48 * response Element. for example, the TextQuery service would be invoked by
49 * processTextQuery(Element request)
50 *
51 * @author Katherine Don
52 */
53public abstract class ServiceRack implements ModuleInterface
54{
55
56 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.ServiceRack.class.getName());
57
58 /** the absolute address of the site home */
59 protected String site_home = null;
60 /** the http address of the site home */
61 protected String site_http_address = null;
62
63 protected String library_name = null;
64 /**
65 * the name of the cluster (or collection) that this service belongs to - if
66 * any
67 */
68 protected String cluster_name = null;
69
70 /** some services can talk back to the message router */
71 protected MessageRouter router = null;
72
73 /** Access to the collection/serviceCluster object for service subclasses */
74 protected ServiceCluster serviceCluster = null;
75
76 /** a converter class to create Documents etc */
77 protected XMLConverter converter = null;
78
79 /** the original config info - if need to store it */
80 protected Element config_info = null;
81
82 /** XML element for stored description XML */
83 protected Document desc_doc = null;
84
85 /**
86 * XML element for describe requests - list of supported services - this is
87 * static
88 */
89 protected Element short_service_info = null;
90
91 /**
92 * XML element for stylesheet requests - map of service name to format elem
93 */
94 protected HashMap<String, Node> format_info_map = null;
95
96 protected Element _globalFormat = null;
97
98 /**
99 * A class loader that knows about the collection resources directory can
100 * put properties files, dtds etc in here
101 */
102 CollectionClassLoader class_loader = null;
103
104 /** sets the cluster name */
105 public void setClusterName(String cluster_name)
106 {
107 this.cluster_name = cluster_name;
108 }
109
110 /** sets the serviceCluster/Collection object */
111 public void setServiceCluster(ServiceCluster serviceCluster)
112 {
113 this.serviceCluster = serviceCluster;
114 }
115
116 /** sets the collect name */
117 public void setCollectionName(String coll_name)
118 {
119 setClusterName(coll_name);
120 }
121
122 public void cleanUp()
123 {
124 }
125
126 public void setGlobalFormat(Element globalFormat)
127 {
128 _globalFormat = GSXML.duplicateWithNewName(this.desc_doc, globalFormat, GSXML.GLOBAL_FORMAT_ELEM, false);
129 }
130
131 /** sets the site home */
132 public void setSiteHome(String site_home)
133 {
134 this.site_home = site_home;
135 }
136
137 /** sets the site http address */
138 public void setSiteAddress(String site_address)
139 {
140 this.site_http_address = site_address;
141 }
142
143 public void setLibraryName(String library_name)
144 {
145 this.library_name = library_name;
146 }
147
148 public String getLibraryName()
149 {
150 return this.library_name;
151 }
152
153 /** sets the message router */
154 public void setMessageRouter(MessageRouter m)
155 {
156 this.router = m;
157 setLibraryName(m.getLibraryName());
158 }
159
160 /** the no-args constructor */
161 public ServiceRack()
162 {
163 this.converter = new XMLConverter();
164 this.desc_doc = XMLConverter.newDOM();
165 this.short_service_info = this.desc_doc.createElement(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
166 this.format_info_map = new HashMap<String, Node>();
167 }
168
169 /**
170 * configure the service module
171 *
172 * @param info
173 * the XML node <serviceRack name="XXX"/> with name equal to the
174 * class name (of the subclass)
175 *
176 * must configure short_service_info_ and service_info_map_
177 * @return true if configured ok must be implemented in subclasses
178 */
179 public boolean configure(Element info)
180 {
181 return configure(info, null);
182 }
183
184 public boolean configure(Element info, Element extra_info)
185 {
186 // set up the class loader
187 this.class_loader = new CollectionClassLoader(this.getClass().getClassLoader(), this.site_home, this.cluster_name);
188 return true;
189 }
190
191 /**
192 * Process an XML document - convenience method that uses Strings rather
193 * than Elements. just calls process(Element).
194 *
195 * @param xml_in
196 * the Document to process - a string
197 * @return the resultant document as a string - contains any error messages
198 * @see String
199 */
200 public String process(String xml_in)
201 {
202
203 Document doc = this.converter.getDOM(xml_in);
204 if (doc == null)
205 {
206 logger.error("Couldn't parse request");
207 logger.error(xml_in);
208 return null;
209 }
210 Node res = process(doc);
211 return this.converter.getString(res);
212
213 }
214
215 /**
216 * process an XML request in DOM form
217 *
218 * @param message
219 * the Node node containing the request should be <message>
220 * @return an Node with the result XML
221 * @see Node/Element
222 */
223 public Node process(Node message_node)
224 {
225 Element message = GSXML.nodeToElement(message_node);
226
227 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
228 Document result_doc = XMLConverter.newDOM();
229 Element mainResult = result_doc.createElement(GSXML.MESSAGE_ELEM);
230 if (requests.getLength() == 0)
231 {
232 // no requests
233 return mainResult; // empty message for now
234 }
235
236 for (int i = 0; i < requests.getLength(); i++)
237 {
238 Element request = (Element) requests.item(i);
239
240 String type = request.getAttribute(GSXML.TYPE_ATT);
241 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE))
242 {
243 Element response = processDescribe(request);
244 if (response != null)
245 {
246 mainResult.appendChild(result_doc.importNode(response, true));
247 }
248
249 }
250 else if (type.equals(GSXML.REQUEST_TYPE_FORMAT))
251 {
252 Element response = processFormat(request);
253 mainResult.appendChild(result_doc.importNode(response, true));
254 }
255 else
256 {
257 // other type of request, must be processed by the subclass -
258 // send to the service method
259 StringBuffer error_string = new StringBuffer();
260 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
261 Element response = null;
262 try
263 {
264 Class c = this.getClass();
265 Class[] params = { Class.forName("org.w3c.dom.Element") };
266
267 String method_name = "process" + to;
268 Method m = null;
269 while (c != null)
270 {
271
272 try
273 {
274 m = c.getDeclaredMethod(method_name, params);
275 // if this has worked, break
276 break;
277 }
278 catch (NoSuchMethodException e)
279 {
280 c = c.getSuperclass();
281 }
282 catch (SecurityException e)
283 {
284 logger.error("security exception for finding method " + method_name);
285 error_string.append("ServiceRack.process: security exception for finding method " + method_name);
286 }
287 } // while
288 if (m != null)
289 {
290 Object[] args = { request };
291 try
292 {
293 response = (Element) m.invoke(this, args);
294
295 }
296 catch (Exception e)
297 {
298 logger.error("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString(), e);
299 // for debugging, it's useful to see what's really causing this supposed "invocationTargetException"
300 // When the error message is displayed in the "Net" tab in the browser's web inspector, scroll down to "Caused by"
301 StringWriter errors = new StringWriter();
302 e.printStackTrace(new PrintWriter(errors));
303 String errStr = errors.toString();
304 errStr += XMLConverter.getString(request);
305
306
307 error_string.append("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString() + "\n" + errStr );
308 }
309 }
310 else
311 {
312 logger.error("method " + method_name + " not found for class " + this.getClass().getName());
313 error_string.append("ServiceRack.process: method " + method_name + " not found for class " + this.getClass().getName());
314 }
315
316 }
317 catch (ClassNotFoundException e)
318 {
319 logger.error("Element class not found");
320 error_string.append("Element class not found");
321 }
322 if (response != null)
323 {
324 mainResult.appendChild(result_doc.importNode(response, true));
325 }
326 else
327 {
328 // add in a dummy response
329 logger.error("adding in an error element\n");
330 response = result_doc.createElement(GSXML.RESPONSE_ELEM);
331 GSXML.addError(response, error_string.toString());
332 mainResult.appendChild(response);
333
334 }
335
336 } // else process request
337 } // for each request
338
339 return mainResult;
340
341 }
342
343 /**
344 * process method for describe requests
345 */
346 protected Element processDescribe(Element request)
347 {
348 Document result_doc = XMLConverter.newDOM();
349 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
350 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
351
352 String lang = request.getAttribute(GSXML.LANG_ATT);
353 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
354 if (to.equals(""))
355 { // return the service list
356 response.appendChild(result_doc.importNode(getServiceList(result_doc, lang), true));
357 return response;
358 }
359 response.setAttribute(GSXML.FROM_ATT, to);
360 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
361 Element description = null;
362 if (param_list == null)
363 {
364 description = getServiceDescription(result_doc, to, lang, null);
365 }
366 else
367 {
368 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
369 for (int i = 0; i < params.getLength(); i++)
370 {
371
372 Element param = (Element) params.item(i);
373 // Identify the structure information desired
374 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM))
375 {
376 String info = param.getAttribute(GSXML.VALUE_ATT);
377 if (description == null)
378 {
379 description = getServiceDescription(result_doc, to, lang, info);
380 }
381 else
382 {
383 Element temp = getServiceDescription(result_doc, to, lang, info);
384 GSXML.mergeElements(description, temp);
385 }
386 }
387 }
388 }
389 if (description != null)
390 { // may be null if non-existant service
391 response.appendChild(description);
392 }
393 return response;
394
395 }
396
397 /**
398 * process method for stylesheet requests
399 */
400 protected Element processFormat(Element request)
401 {
402 Document result_doc = XMLConverter.newDOM();
403 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
404 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_FORMAT);
405
406 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
407
408 if (to.equals(""))
409 { // serviceRack query - is this appropriate??
410 return response;
411 }
412
413 // describe a particular service
414 if (this.format_info_map.containsKey(to))
415 {
416 response.appendChild(getServiceFormat(result_doc, to));
417 if (_globalFormat != null)
418 {
419 response.appendChild(result_doc.importNode(this._globalFormat, true));
420 }
421 response.setAttribute(GSXML.FROM_ATT, to);
422 return response;
423 }
424
425 // else no format info
426 logger.error("ServiceRack describe request: no format info for " + to + ".");
427 return response;
428 }
429
430 /** returns the service list for the subclass */
431 protected Element getServiceList(Document doc, String lang)
432 {
433 // for now, it is static and has no language stuff
434 return (Element)doc.importNode(this.short_service_info, true);
435 }
436
437 /** returns a specific service description */
438 abstract protected Element getServiceDescription(Document doc, String service, String lang, String subset);
439
440 protected Element getServiceFormat(Document doc, String service)
441 {
442 Element format = (Element) this.format_info_map.get(service);
443 if (format != null) {
444 return (Element)doc.importNode(format, true);
445 }
446 return null;
447 }
448
449
450 /**
451 * Returns the appropriate language element from a display elem, display is
452 * the containing element, name is the name of the element to look for, lang
453 * is the preferred language, lang_default is the fall back lang, if neither
454 * lang is found will return the first one it finds
455 */
456 public String getDisplayText(Element display, String name, String lang, String lang_default) {
457 return getDisplayText(display, name, lang, lang_default, null);
458 }
459
460 public String getDisplayText(Element display, String name, String lang, String lang_default, String dictionary_name) {
461
462
463 String def = null;
464 String first = null;
465 String key = null;
466 NodeList elems = display.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
467 if (elems.getLength() == 0)
468 return "";
469 for (int i = 0; i < elems.getLength(); i++)
470 {
471 Element e = (Element) elems.item(i);
472 String n = e.getAttribute(GSXML.NAME_ATT);
473 if (name.equals(n))
474 {
475 String l = e.getAttribute(GSXML.LANG_ATT);
476 String k = e.getAttribute(GSXML.KEY_ATT);
477 // if we have a specific lang value, return that
478 if (!l.equals("") && lang.equals(l))
479 {
480 return GSXML.getNodeText(e);
481 }
482 else if (!l.equals("") && lang_default.equals(l))
483 {
484 def = GSXML.getNodeText(e);
485 }
486 else if (!k.equals("")) {
487 if ( key == null) {
488 // a key specified. only allowed one spec with key
489 key = k;
490 }
491 }
492 // we have no key and we don't match the languages
493 else if (first == null)
494 {
495 // but we are the first one, so we remember the value
496 first = GSXML.getNodeText(e);
497 }
498 }
499 else
500 {
501 continue;
502 }
503 }
504
505
506 if (key != null) {
507 String s = getTextString(key, lang, dictionary_name);
508 // only use this one if a value was actually found
509 if (!s.equals( "_"+key +"_")) {
510 return s;
511 }
512 }
513
514 if (def != null)
515 {
516 return def;
517 }
518 if (first != null)
519 {
520 return first;
521 }
522 return "";
523 }
524
525 /** overloaded version for no args case */
526 protected String getTextString(String key, String lang)
527 {
528 return getTextString(key, lang, null, null);
529 }
530
531 protected String getTextString(String key, String lang, String dictionary)
532 {
533 return getTextString(key, lang, dictionary, null);
534 }
535
536 protected String getTextString(String key, String lang, String[] args)
537 {
538 return getTextString(key, lang, null, args);
539 }
540
541 /**
542 * getTextString - retrieves a language specific text string for the given
543 * key and locale, from the specified resource_bundle (dictionary)
544 */
545 protected String getTextString(String key, String lang, String dictionary, String[] args)
546 {
547
548 // we want to use the collection class loader in case there are coll specific files
549 if (dictionary != null)
550 {
551 // just try the one specified dictionary
552 Dictionary dict = new Dictionary(dictionary, lang, this.class_loader);
553 String result = dict.get(key, args);
554 if (result == null)
555 { // not found
556 //return "_" + key + "_";
557 return null;
558 }
559 return result;
560 }
561
562 // now we try class names for dictionary names
563 String original_class_name = this.getClass().getName();
564 original_class_name = original_class_name.substring(original_class_name.lastIndexOf('.') + 1);
565 Dictionary dict = new Dictionary(original_class_name, lang, this.class_loader);
566 String result = dict.get(key, args);
567 if (result != null)
568 {
569 return result;
570 }
571
572 // we have to try super classes
573 String class_name;
574 Class c = this.getClass().getSuperclass();
575 while (result == null && c != null)
576 {
577 class_name = c.getName();
578 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
579 if (class_name.equals("ServiceRack"))
580 {
581 // this is as far as we go
582 break;
583 }
584 dict = new Dictionary(class_name, lang, this.class_loader);
585 result = dict.get(key, args);
586 c = c.getSuperclass();
587 }
588 if (result == null)
589 {
590 // try the ServiceRack properties
591 // at the moment we look for original_class_name.key, then just key
592 // if there is lots of inheritance, may want to try down the list of superclasses too...?
593 class_name = "ServiceRack";
594 dict = new Dictionary(class_name, lang, this.class_loader);
595 String full_key = original_class_name+"."+key;
596 result = dict.get(full_key, args);
597 if(result == null) {
598 result = dict.get(key, args);
599 }
600 if(result == null) {
601 return "_" + key + "_";
602 }
603 }
604 return result;
605
606 }
607
608 protected String getMetadataNameText(String key, String lang)
609 {
610
611 String properties_name = "metadata_names";
612 Dictionary dict = new Dictionary(properties_name, lang);
613
614 String result = dict.get(key);
615 if (result == null)
616 { // not found
617 return null;
618 }
619 return result;
620 }
621}
Note: See TracBrowser for help on using the repository browser.