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

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

CollectionClassLoader changed to CustomClassLoader

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