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

Last change on this file since 32453 was 32453, checked in by kjdon, 6 years ago

replacing hard coded param names with static string variables. set up save params for those params we need to save to the session.

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