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

Last change on this file since 36592 was 36592, checked in by kjdon, 20 months ago

modified Dictionary, so that if a lang fragment starts with [PENDING], then don't use it. This is to allow the addition of old strings which will help the translators, but which are too out of date to display in the interface. OTher classes use of Dictionary modified so that they go through getTextString, or createDictionaryAndGetString, thereby benifitting from the pending stuff

  • Property svn:keywords set to Author Date Id Revision
File size: 17.1 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 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 }
182 /** the no-args constructor */
183 public ServiceRack()
184 {
185 this.converter = new XMLConverter();
186 this.desc_doc = XMLConverter.newDOM();
187 this.short_service_info = this.desc_doc.createElement(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
188 this.format_info_map = new HashMap<String, Node>();
189 this.save_params = new ArrayList<String>();
190 this.sensitive_params = new ArrayList<String>();
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
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));
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;
234 }
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 {
250 Element message = GSXML.nodeToElement(message_node);
251
252 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
253 Document result_doc = XMLConverter.newDOM();
254 Element mainResult = result_doc.createElement(GSXML.MESSAGE_ELEM);
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 {
268 Element response = processDescribe(request);
269 if (response != null)
270 {
271 mainResult.appendChild(result_doc.importNode(response, true));
272 }
273
274 }
275 else if (type.equals(GSXML.REQUEST_TYPE_FORMAT))
276 {
277 Element response = processFormat(request);
278 mainResult.appendChild(result_doc.importNode(response, true));
279 }
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);
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);
330
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 );
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 {
349 mainResult.appendChild(result_doc.importNode(response, true));
350 }
351 else
352 {
353 // add in a dummy response
354 logger.error("adding in an error element\n");
355 response = result_doc.createElement(GSXML.RESPONSE_ELEM);
356 GSXML.addError(response, error_string.toString());
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 {
373 Document result_doc = XMLConverter.newDOM();
374 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
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
381 response.appendChild(result_doc.importNode(getServiceList(result_doc, lang), true));
382 return response;
383 }
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 {
389 description = getServiceDescription(result_doc, to, lang, null);
390 }
391 else
392 {
393 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
394 for (int i = 0; i < params.getLength(); i++)
395 {
396
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 {
404 description = getServiceDescription(result_doc, to, lang, info);
405 }
406 else
407 {
408 Element temp = getServiceDescription(result_doc, to, lang, info);
409 GSXML.mergeElements(description, temp);
410 }
411 }
412 }
413 }
414 if (description != null)
415 { // may be null if non-existant service
416 response.appendChild(description);
417 }
418 return response;
419
420 }
421
422 /**
423 * process method for stylesheet requests
424 */
425 protected Element processFormat(Element request)
426 {
427 Document result_doc = XMLConverter.newDOM();
428 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
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;
436 }
437
438 // describe a particular service
439 if (this.format_info_map.containsKey(to))
440 {
441 response.appendChild(getServiceFormat(result_doc, to));
442 if (_globalFormat != null)
443 {
444 response.appendChild(result_doc.importNode(this._globalFormat, true));
445 }
446 response.setAttribute(GSXML.FROM_ATT, to);
447 return response;
448 }
449
450 // else no format info
451 logger.error("ServiceRack describe request: no format info for " + to + ".");
452 return response;
453 }
454
455 /** returns the service list for the subclass */
456 protected Element getServiceList(Document doc, String lang)
457 {
458 // for now, it is static and has no language stuff
459 return (Element)doc.importNode(this.short_service_info, true);
460 }
461
462 /** returns a specific service description */
463 abstract protected Element getServiceDescription(Document doc, String service, String lang, String subset);
464
465 protected Element getServiceFormat(Document doc, String service)
466 {
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;
472 }
473
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
485 public String getDisplayText(Element display, String name, String lang, String lang_default, String default_dictionary) {
486
487
488 String def = null;
489 String first = null;
490 String key = null;
491 String dictionary_name = default_dictionary;
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;
516 String d = e.getAttribute(GSXML.DICTIONARY_ATT);
517 if (!d.equals("")) {
518 dictionary_name = d;
519 }
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
539 if (s!= null) {
540 return s;
541 }
542 //if (!s.equals( "_"+key +"_")) {
543 // return s;
544 //}
545 }
546
547 if (def != null)
548 {
549 return def;
550 }
551 if (first != null)
552 {
553 return first;
554 }
555 return "";
556 }
557
558 /** overloaded version for no args case */
559 protected String getTextString(String key, String lang)
560 {
561 return getTextString(key, lang, null, null);
562 }
563
564 protected String getTextString(String key, String lang, String dictionary)
565 {
566 return getTextString(key, lang, dictionary, null);
567 }
568
569 protected String getTextString(String key, String lang, String[] args)
570 {
571 return getTextString(key, lang, null, args);
572 }
573
574 /**
575 * getTextString - retrieves a language specific text string for the given
576 * key and locale, from the specified resource_bundle (dictionary)
577 * dictionary may be null, in which case it will use class names
578 */
579 protected String getTextString(String key, String lang, String dictionary, String[] args)
580 {
581
582 return Dictionary.getTextString(key, lang, args, dictionary, this.getClass(), "ServiceRack", this.class_loader);
583 }
584
585 protected String getMetadataNameText(String key, String lang)
586 {
587 return Dictionary.getTextString(key, lang, null, "metadata_names", null, null, this.class_loader);
588 }
589}
Note: See TracBrowser for help on using the repository browser.