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

Last change on this file since 25635 was 25635, checked in by sjm84, 12 years ago

Fixing Greenstone 3's use (or lack thereof) of generics, this was done automatically so we may want to change it over time. This change will also auto-format any files that have not already been formatted.

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