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

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

removed a debug log message

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