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

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

Removing an unnecessary line of code

  • Property svn:keywords set to Author Date Id Revision
File size: 14.0 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.core.MessageRouter;
27import org.greenstone.gsdl3.core.ModuleInterface;
28import org.greenstone.gsdl3.util.CollectionClassLoader;
29import org.greenstone.gsdl3.util.Dictionary;
30import org.greenstone.gsdl3.util.GSPath;
31import org.greenstone.gsdl3.util.GSXML;
32import org.greenstone.gsdl3.util.XMLConverter;
33import org.w3c.dom.Document;
34import org.w3c.dom.Element;
35import org.w3c.dom.Node;
36import org.w3c.dom.NodeList;
37
38/**
39 * ServiceRack - abstract base class for services
40 *
41 * A ServiceRack provides one or more Services. This base class implements the
42 * process method. Each service is invoked by a method called process<service
43 * name> which takes one parameter - the xml request Element, and returns an XML
44 * response Element. for example, the TextQuery service would be invoked by
45 * processTextQuery(Element request)
46 *
47 * @author Katherine Don
48 */
49public abstract class ServiceRack implements ModuleInterface
50{
51
52 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.ServiceRack.class.getName());
53
54 /** the absolute address of the site home */
55 protected String site_home = null;
56 /** the http address of the site home */
57 protected String site_http_address = null;
58
59 protected String library_name = null;
60 /**
61 * the name of the cluster (or collection) that this service belongs to - if
62 * any
63 */
64 protected String cluster_name = null;
65
66 /** some services can talk back to the message router */
67 protected MessageRouter router = null;
68
69 /** a converter class to create Documents etc */
70 protected XMLConverter converter = null;
71
72 /** the original config info - if need to store it */
73 protected Element config_info = null;
74
75 /** XML element for describe requests - the container doc */
76 protected Document doc = null;
77
78 /**
79 * XML element for describe requests - list of supported services - this is
80 * static
81 */
82 protected Element short_service_info = null;
83
84 /**
85 * XML element for stylesheet requests - map of service name to format elem
86 */
87 protected HashMap<String, Node> format_info_map = null;
88
89 protected Element _globalFormat = null;
90
91 /**
92 * A class loader that knows about the collection resources directory can
93 * put properties files, dtds etc in here
94 */
95 CollectionClassLoader class_loader = null;
96
97 /** sets the cluster name */
98 public void setClusterName(String cluster_name)
99 {
100 this.cluster_name = cluster_name;
101 }
102
103 /** sets the collect name */
104 public void setCollectionName(String coll_name)
105 {
106 setClusterName(coll_name);
107 }
108
109 public void cleanUp()
110 {
111 }
112
113 public void setGlobalFormat(Element globalFormat)
114 {
115 _globalFormat = globalFormat;
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 Element message = this.converter.nodeToElement(message_node);
213
214 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
215 Element mainResult = this.doc.createElement(GSXML.MESSAGE_ELEM);
216 if (requests.getLength() == 0)
217 {
218 // no requests
219 return mainResult; // empty message for now
220 }
221
222 for (int i = 0; i < requests.getLength(); i++)
223 {
224 Element request = (Element) requests.item(i);
225
226 String type = request.getAttribute(GSXML.TYPE_ATT);
227 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE))
228 {
229 Element response = processDescribe(request);
230 if (response != null)
231 {
232 mainResult.appendChild(this.doc.importNode(response, true));
233 }
234
235 }
236 else if (type.equals(GSXML.REQUEST_TYPE_FORMAT))
237 {
238 Element response = processFormat(request);
239 mainResult.appendChild(this.doc.importNode(response, true));
240 }
241 else
242 {
243 // other type of request, must be processed by the subclass -
244 // send to the service method
245 StringBuffer error_string = new StringBuffer();
246 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
247 Element response = null;
248 try
249 {
250 Class c = this.getClass();
251 Class[] params = { Class.forName("org.w3c.dom.Element") };
252
253 String method_name = "process" + to;
254 Method m = null;
255 while (c != null)
256 {
257
258 try
259 {
260 m = c.getDeclaredMethod(method_name, params);
261 // if this has worked, break
262 break;
263 }
264 catch (NoSuchMethodException e)
265 {
266 c = c.getSuperclass();
267 }
268 catch (SecurityException e)
269 {
270 logger.error("security exception for finding method " + method_name);
271 error_string.append("ServiceRack.process: security exception for finding method " + method_name);
272 }
273 } // while
274 if (m != null)
275 {
276 Object[] args = { request };
277 try
278 {
279 response = (Element) m.invoke(this, args);
280
281 }
282 catch (Exception e)
283 {
284 logger.error("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString(), e);
285
286 error_string.append("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString());
287 }
288 }
289 else
290 {
291 logger.error("method " + method_name + " not found for class " + this.getClass().getName());
292 error_string.append("ServiceRack.process: method " + method_name + " not found for class " + this.getClass().getName());
293 }
294
295 }
296 catch (ClassNotFoundException e)
297 {
298 logger.error("Element class not found");
299 error_string.append("Element class not found");
300 }
301 if (response != null)
302 {
303 mainResult.appendChild(this.doc.importNode(response, true));
304 }
305 else
306 {
307 // add in a dummy response
308 logger.error("adding in an error element\n");
309 response = this.doc.createElement(GSXML.RESPONSE_ELEM);
310 GSXML.addError(this.doc, response, error_string.toString());
311 mainResult.appendChild(response);
312
313 }
314
315 } // else process request
316 } // for each request
317
318 return mainResult;
319
320 }
321
322 /**
323 * process method for describe requests
324 */
325 protected Element processDescribe(Element request)
326 {
327
328 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
329 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
330
331 String lang = request.getAttribute(GSXML.LANG_ATT);
332 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
333 if (to.equals(""))
334 { // return the service list
335 response.appendChild(getServiceList(lang));
336 return response;
337 }
338 response.setAttribute(GSXML.FROM_ATT, to);
339 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
340 Element description = null;
341 if (param_list == null)
342 {
343 description = getServiceDescription(to, lang, null);
344 }
345 else
346 {
347 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
348 for (int i = 0; i < params.getLength(); i++)
349 {
350
351 Element param = (Element) params.item(i);
352 // Identify the structure information desired
353 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM))
354 {
355 String info = param.getAttribute(GSXML.VALUE_ATT);
356 if (description == null)
357 {
358 description = getServiceDescription(to, lang, info);
359 }
360 else
361 {
362 Element temp = getServiceDescription(to, lang, info);
363 GSXML.mergeElements(description, temp);
364 }
365 }
366 }
367 }
368 if (description != null)
369 { // may be null if non-existant service
370 response.appendChild(description);
371 }
372 return response;
373
374 }
375
376 /**
377 * process method for stylesheet requests
378 */
379 protected Element processFormat(Element request)
380 {
381 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
382 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_FORMAT);
383
384 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
385
386 if (to.equals(""))
387 { // serviceRack query - is this appropriate??
388 return response;
389 }
390
391 // describe a particular service
392 if (this.format_info_map.containsKey(to))
393 {
394 response.appendChild(response.getOwnerDocument().importNode(getServiceFormat(to), true));
395 if (_globalFormat != null)
396 {
397 response.appendChild(response.getOwnerDocument().importNode(GSXML.duplicateWithNewName(response.getOwnerDocument(), _globalFormat, GSXML.GLOBAL_FORMAT_ELEM, false), true));
398 }
399 response.setAttribute(GSXML.FROM_ATT, to);
400 return response;
401 }
402
403 // else no format info
404 logger.error("ServiceRack describe request: no format info for " + to + ".");
405 return response;
406 }
407
408 /** returns the service list for the subclass */
409 protected Element getServiceList(String lang)
410 {
411 // for now, it is static and has no language stuff
412 return (Element) this.short_service_info.cloneNode(true);
413 }
414
415 /** returns a specific service description */
416 abstract protected Element getServiceDescription(String service, String lang, String subset);
417
418 protected Element getServiceFormat(String service)
419 {
420 Element format = (Element) ((Element) this.format_info_map.get(service)).cloneNode(true);
421 return format;
422 }
423
424 /** overloaded version for no args case */
425 protected String getTextString(String key, String lang)
426 {
427 return getTextString(key, lang, null, null);
428 }
429
430 protected String getTextString(String key, String lang, String dictionary)
431 {
432 return getTextString(key, lang, dictionary, null);
433 }
434
435 protected String getTextString(String key, String lang, String[] args)
436 {
437 return getTextString(key, lang, null, args);
438 }
439
440 /**
441 * getTextString - retrieves a language specific text string for the given
442 * key and locale, from the specified resource_bundle (dictionary)
443 */
444 protected String getTextString(String key, String lang, String dictionary, String[] args)
445 {
446
447 // we want to use the collection class loader in case there are coll specific files
448 if (dictionary != null)
449 {
450 // just try the one specified dictionary
451 Dictionary dict = new Dictionary(dictionary, lang, this.class_loader);
452 String result = dict.get(key, args);
453 if (result == null)
454 { // not found
455 return "_" + key + "_";
456 }
457 return result;
458 }
459
460 // now we try class names for dictionary names
461 String class_name = this.getClass().getName();
462 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
463 Dictionary dict = new Dictionary(class_name, lang, this.class_loader);
464 String result = dict.get(key, args);
465 if (result != null)
466 {
467 return result;
468 }
469
470 // we have to try super classes
471 Class c = this.getClass().getSuperclass();
472 while (result == null && c != null)
473 {
474 class_name = c.getName();
475 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
476 if (class_name.equals("ServiceRack"))
477 {
478 // this is as far as we go
479 break;
480 }
481 dict = new Dictionary(class_name, lang, this.class_loader);
482 result = dict.get(key, args);
483 c = c.getSuperclass();
484 }
485 if (result == null)
486 {
487 return "_" + key + "_";
488 }
489 return result;
490
491 }
492
493 protected String getMetadataNameText(String key, String lang)
494 {
495
496 String properties_name = "metadata_names";
497 Dictionary dict = new Dictionary(properties_name, lang);
498
499 String result = dict.get(key);
500 if (result == null)
501 { // not found
502 return null;
503 }
504 return result;
505 }
506}
Note: See TracBrowser for help on using the repository browser.