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

Last change on this file since 29318 was 28966, checked in by kjdon, 10 years ago

Lots of changes. Mainly to do with removing this.doc from everywhere. Document is not thread safe. Now we tend to create a new Document everytime we are starting a new page/message etc. in service this.desc_doc is available as teh document to create service info stuff. But it should only be used for this and not for other messages. newDOM is now static for XMLConverter. method param changes for some GSXML methods.

  • Property svn:keywords set to Author Date Id Revision
File size: 14.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
39/**
40 * ServiceRack - abstract base class for services
41 *
42 * A ServiceRack provides one or more Services. This base class implements the
43 * process method. Each service is invoked by a method called process<service
44 * name> which takes one parameter - the xml request Element, and returns an XML
45 * response Element. for example, the TextQuery service would be invoked by
46 * processTextQuery(Element request)
47 *
48 * @author Katherine Don
49 */
50public abstract class ServiceRack implements ModuleInterface
51{
52
53 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.ServiceRack.class.getName());
54
55 /** the absolute address of the site home */
56 protected String site_home = null;
57 /** the http address of the site home */
58 protected String site_http_address = null;
59
60 protected String library_name = null;
61 /**
62 * the name of the cluster (or collection) that this service belongs to - if
63 * any
64 */
65 protected String cluster_name = null;
66
67 /** some services can talk back to the message router */
68 protected MessageRouter router = null;
69
70 /** Access to the collection/serviceCluster object for service subclasses */
71 protected ServiceCluster serviceCluster = null;
72
73 /** a converter class to create Documents etc */
74 protected XMLConverter converter = null;
75
76 /** the original config info - if need to store it */
77 protected Element config_info = null;
78
79 /** XML element for stored description XML */
80 protected Document desc_doc = null;
81
82 /**
83 * XML element for describe requests - list of supported services - this is
84 * static
85 */
86 protected Element short_service_info = null;
87
88 /**
89 * XML element for stylesheet requests - map of service name to format elem
90 */
91 protected HashMap<String, Node> format_info_map = null;
92
93 protected Element _globalFormat = null;
94
95 /**
96 * A class loader that knows about the collection resources directory can
97 * put properties files, dtds etc in here
98 */
99 CollectionClassLoader class_loader = null;
100
101 /** sets the cluster name */
102 public void setClusterName(String cluster_name)
103 {
104 this.cluster_name = cluster_name;
105 }
106
107 /** sets the serviceCluster/Collection object */
108 public void setServiceCluster(ServiceCluster serviceCluster)
109 {
110 this.serviceCluster = serviceCluster;
111 }
112
113 /** sets the collect name */
114 public void setCollectionName(String coll_name)
115 {
116 setClusterName(coll_name);
117 }
118
119 public void cleanUp()
120 {
121 }
122
123 public void setGlobalFormat(Element globalFormat)
124 {
125 _globalFormat = GSXML.duplicateWithNewName(this.desc_doc, globalFormat, GSXML.GLOBAL_FORMAT_ELEM, false);
126 }
127
128 /** sets the site home */
129 public void setSiteHome(String site_home)
130 {
131 this.site_home = site_home;
132 }
133
134 /** sets the site http address */
135 public void setSiteAddress(String site_address)
136 {
137 this.site_http_address = site_address;
138 }
139
140 public void setLibraryName(String library_name)
141 {
142 this.library_name = library_name;
143 }
144
145 public String getLibraryName()
146 {
147 return this.library_name;
148 }
149
150 /** sets the message router */
151 public void setMessageRouter(MessageRouter m)
152 {
153 this.router = m;
154 setLibraryName(m.getLibraryName());
155 }
156
157 /** the no-args constructor */
158 public ServiceRack()
159 {
160 this.converter = new XMLConverter();
161 this.desc_doc = XMLConverter.newDOM();
162 this.short_service_info = this.desc_doc.createElement(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
163 this.format_info_map = new HashMap<String, Node>();
164 }
165
166 /**
167 * configure the service module
168 *
169 * @param info
170 * the XML node <serviceRack name="XXX"/> with name equal to the
171 * class name (of the subclass)
172 *
173 * must configure short_service_info_ and service_info_map_
174 * @return true if configured ok must be implemented in subclasses
175 */
176 public boolean configure(Element info)
177 {
178 return configure(info, null);
179 }
180
181 public boolean configure(Element info, Element extra_info)
182 {
183 // set up the class loader
184 this.class_loader = new CollectionClassLoader(this.getClass().getClassLoader(), this.site_home, this.cluster_name);
185 return true;
186 }
187
188 /**
189 * Process an XML document - convenience method that uses Strings rather
190 * than Elements. just calls process(Element).
191 *
192 * @param xml_in
193 * the Document to process - a string
194 * @return the resultant document as a string - contains any error messages
195 * @see String
196 */
197 public String process(String xml_in)
198 {
199
200 Document doc = this.converter.getDOM(xml_in);
201 if (doc == null)
202 {
203 logger.error("Couldn't parse request");
204 logger.error(xml_in);
205 return null;
206 }
207 Node res = process(doc);
208 return this.converter.getString(res);
209
210 }
211
212 /**
213 * process an XML request in DOM form
214 *
215 * @param message
216 * the Node node containing the request should be <message>
217 * @return an Node with the result XML
218 * @see Node/Element
219 */
220 public Node process(Node message_node)
221 {
222 Element message = GSXML.nodeToElement(message_node);
223
224 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
225 Document result_doc = XMLConverter.newDOM();
226 Element mainResult = result_doc.createElement(GSXML.MESSAGE_ELEM);
227 if (requests.getLength() == 0)
228 {
229 // no requests
230 return mainResult; // empty message for now
231 }
232
233 for (int i = 0; i < requests.getLength(); i++)
234 {
235 Element request = (Element) requests.item(i);
236
237 String type = request.getAttribute(GSXML.TYPE_ATT);
238 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE))
239 {
240 Element response = processDescribe(request);
241 if (response != null)
242 {
243 mainResult.appendChild(result_doc.importNode(response, true));
244 }
245
246 }
247 else if (type.equals(GSXML.REQUEST_TYPE_FORMAT))
248 {
249 Element response = processFormat(request);
250 mainResult.appendChild(result_doc.importNode(response, true));
251 }
252 else
253 {
254 // other type of request, must be processed by the subclass -
255 // send to the service method
256 StringBuffer error_string = new StringBuffer();
257 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
258 Element response = null;
259 try
260 {
261 Class c = this.getClass();
262 Class[] params = { Class.forName("org.w3c.dom.Element") };
263
264 String method_name = "process" + to;
265 Method m = null;
266 while (c != null)
267 {
268
269 try
270 {
271 m = c.getDeclaredMethod(method_name, params);
272 // if this has worked, break
273 break;
274 }
275 catch (NoSuchMethodException e)
276 {
277 c = c.getSuperclass();
278 }
279 catch (SecurityException e)
280 {
281 logger.error("security exception for finding method " + method_name);
282 error_string.append("ServiceRack.process: security exception for finding method " + method_name);
283 }
284 } // while
285 if (m != null)
286 {
287 Object[] args = { request };
288 try
289 {
290 response = (Element) m.invoke(this, args);
291
292 }
293 catch (Exception e)
294 {
295 logger.error("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString(), e);
296
297 error_string.append("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString());
298 }
299 }
300 else
301 {
302 logger.error("method " + method_name + " not found for class " + this.getClass().getName());
303 error_string.append("ServiceRack.process: method " + method_name + " not found for class " + this.getClass().getName());
304 }
305
306 }
307 catch (ClassNotFoundException e)
308 {
309 logger.error("Element class not found");
310 error_string.append("Element class not found");
311 }
312 if (response != null)
313 {
314 mainResult.appendChild(result_doc.importNode(response, true));
315 }
316 else
317 {
318 // add in a dummy response
319 logger.error("adding in an error element\n");
320 response = result_doc.createElement(GSXML.RESPONSE_ELEM);
321 GSXML.addError(response, error_string.toString());
322 mainResult.appendChild(response);
323
324 }
325
326 } // else process request
327 } // for each request
328
329 return mainResult;
330
331 }
332
333 /**
334 * process method for describe requests
335 */
336 protected Element processDescribe(Element request)
337 {
338 Document result_doc = XMLConverter.newDOM();
339 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
340 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
341
342 String lang = request.getAttribute(GSXML.LANG_ATT);
343 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
344 if (to.equals(""))
345 { // return the service list
346 response.appendChild(result_doc.importNode(getServiceList(result_doc, lang), true));
347 return response;
348 }
349 response.setAttribute(GSXML.FROM_ATT, to);
350 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
351 Element description = null;
352 if (param_list == null)
353 {
354 description = getServiceDescription(result_doc, to, lang, null);
355 }
356 else
357 {
358 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
359 for (int i = 0; i < params.getLength(); i++)
360 {
361
362 Element param = (Element) params.item(i);
363 // Identify the structure information desired
364 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM))
365 {
366 String info = param.getAttribute(GSXML.VALUE_ATT);
367 if (description == null)
368 {
369 description = getServiceDescription(result_doc, to, lang, info);
370 }
371 else
372 {
373 Element temp = getServiceDescription(result_doc, to, lang, info);
374 GSXML.mergeElements(description, temp);
375 }
376 }
377 }
378 }
379 if (description != null)
380 { // may be null if non-existant service
381 response.appendChild(description);
382 }
383 return response;
384
385 }
386
387 /**
388 * process method for stylesheet requests
389 */
390 protected Element processFormat(Element request)
391 {
392 Document result_doc = XMLConverter.newDOM();
393 Element response = result_doc.createElement(GSXML.RESPONSE_ELEM);
394 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_FORMAT);
395
396 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
397
398 if (to.equals(""))
399 { // serviceRack query - is this appropriate??
400 return response;
401 }
402
403 // describe a particular service
404 if (this.format_info_map.containsKey(to))
405 {
406 response.appendChild(getServiceFormat(result_doc, to));
407 if (_globalFormat != null)
408 {
409 response.appendChild(result_doc.importNode(this._globalFormat, true));
410 }
411 response.setAttribute(GSXML.FROM_ATT, to);
412 return response;
413 }
414
415 // else no format info
416 logger.error("ServiceRack describe request: no format info for " + to + ".");
417 return response;
418 }
419
420 /** returns the service list for the subclass */
421 protected Element getServiceList(Document doc, String lang)
422 {
423 // for now, it is static and has no language stuff
424 return (Element)doc.importNode(this.short_service_info, true);
425 }
426
427 /** returns a specific service description */
428 abstract protected Element getServiceDescription(Document doc, String service, String lang, String subset);
429
430 protected Element getServiceFormat(Document doc, String service)
431 {
432 Element format = (Element) this.format_info_map.get(service);
433 if (format != null) {
434 return (Element)doc.importNode(format, true);
435 }
436 return null;
437 }
438
439 /** overloaded version for no args case */
440 protected String getTextString(String key, String lang)
441 {
442 return getTextString(key, lang, null, null);
443 }
444
445 protected String getTextString(String key, String lang, String dictionary)
446 {
447 return getTextString(key, lang, dictionary, null);
448 }
449
450 protected String getTextString(String key, String lang, String[] args)
451 {
452 return getTextString(key, lang, null, args);
453 }
454
455 /**
456 * getTextString - retrieves a language specific text string for the given
457 * key and locale, from the specified resource_bundle (dictionary)
458 */
459 protected String getTextString(String key, String lang, String dictionary, String[] args)
460 {
461
462 // we want to use the collection class loader in case there are coll specific files
463 if (dictionary != null)
464 {
465 // just try the one specified dictionary
466 Dictionary dict = new Dictionary(dictionary, lang, this.class_loader);
467 String result = dict.get(key, args);
468 if (result == null)
469 { // not found
470 return "_" + key + "_";
471 }
472 return result;
473 }
474
475 // now we try class names for dictionary names
476 String class_name = this.getClass().getName();
477 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
478 Dictionary dict = new Dictionary(class_name, lang, this.class_loader);
479 String result = dict.get(key, args);
480 if (result != null)
481 {
482 return result;
483 }
484
485 // we have to try super classes
486 Class c = this.getClass().getSuperclass();
487 while (result == null && c != null)
488 {
489 class_name = c.getName();
490 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
491 if (class_name.equals("ServiceRack"))
492 {
493 // this is as far as we go
494 break;
495 }
496 dict = new Dictionary(class_name, lang, this.class_loader);
497 result = dict.get(key, args);
498 c = c.getSuperclass();
499 }
500 if (result == null)
501 {
502 return "_" + key + "_";
503 }
504 return result;
505
506 }
507
508 protected String getMetadataNameText(String key, String lang)
509 {
510
511 String properties_name = "metadata_names";
512 Dictionary dict = new Dictionary(properties_name, lang);
513
514 String result = dict.get(key);
515 if (result == null)
516 { // not found
517 return null;
518 }
519 return result;
520 }
521}
Note: See TracBrowser for help on using the repository browser.