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

Last change on this file since 27087 was 27087, checked in by ak19, 11 years ago

Implemented RSS support for GS3. At present, can see this with a=rss in the url, when a collection is built. Soon will have a button (at least for the GS3 demo collection).

  • Property svn:keywords set to Author Date Id Revision
File size: 14.4 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 describe requests - the container doc */
80 protected Document 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 = globalFormat;
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.doc = this.converter.newDOM();
162 this.short_service_info = this.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 = this.converter.nodeToElement(message_node);
223
224 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
225 Element mainResult = this.doc.createElement(GSXML.MESSAGE_ELEM);
226 if (requests.getLength() == 0)
227 {
228 // no requests
229 return mainResult; // empty message for now
230 }
231
232 for (int i = 0; i < requests.getLength(); i++)
233 {
234 Element request = (Element) requests.item(i);
235
236 String type = request.getAttribute(GSXML.TYPE_ATT);
237 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE))
238 {
239 Element response = processDescribe(request);
240 if (response != null)
241 {
242 mainResult.appendChild(this.doc.importNode(response, true));
243 }
244
245 }
246 else if (type.equals(GSXML.REQUEST_TYPE_FORMAT))
247 {
248 Element response = processFormat(request);
249 mainResult.appendChild(this.doc.importNode(response, true));
250 }
251 else
252 {
253 // other type of request, must be processed by the subclass -
254 // send to the service method
255 StringBuffer error_string = new StringBuffer();
256 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
257 Element response = null;
258 try
259 {
260 Class c = this.getClass();
261 Class[] params = { Class.forName("org.w3c.dom.Element") };
262
263 String method_name = "process" + to;
264 Method m = null;
265 while (c != null)
266 {
267
268 try
269 {
270 m = c.getDeclaredMethod(method_name, params);
271 // if this has worked, break
272 break;
273 }
274 catch (NoSuchMethodException e)
275 {
276 c = c.getSuperclass();
277 }
278 catch (SecurityException e)
279 {
280 logger.error("security exception for finding method " + method_name);
281 error_string.append("ServiceRack.process: security exception for finding method " + method_name);
282 }
283 } // while
284 if (m != null)
285 {
286 Object[] args = { request };
287 try
288 {
289 response = (Element) m.invoke(this, args);
290
291 }
292 catch (Exception e)
293 {
294 logger.error("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString(), e);
295
296 error_string.append("Trying to call a processService type method (process" + to + ") on a subclass(" + this.getClass().getName() + "), but an exception happened:" + e.toString());
297 }
298 }
299 else
300 {
301 logger.error("method " + method_name + " not found for class " + this.getClass().getName());
302 error_string.append("ServiceRack.process: method " + method_name + " not found for class " + this.getClass().getName());
303 }
304
305 }
306 catch (ClassNotFoundException e)
307 {
308 logger.error("Element class not found");
309 error_string.append("Element class not found");
310 }
311 if (response != null)
312 {
313 mainResult.appendChild(this.doc.importNode(response, true));
314 }
315 else
316 {
317 // add in a dummy response
318 logger.error("adding in an error element\n");
319 response = this.doc.createElement(GSXML.RESPONSE_ELEM);
320 GSXML.addError(this.doc, response, error_string.toString());
321 mainResult.appendChild(response);
322
323 }
324
325 } // else process request
326 } // for each request
327
328 return mainResult;
329
330 }
331
332 /**
333 * process method for describe requests
334 */
335 protected Element processDescribe(Element request)
336 {
337
338 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
339 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
340
341 String lang = request.getAttribute(GSXML.LANG_ATT);
342 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
343 if (to.equals(""))
344 { // return the service list
345 response.appendChild(getServiceList(lang));
346 return response;
347 }
348 response.setAttribute(GSXML.FROM_ATT, to);
349 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
350 Element description = null;
351 if (param_list == null)
352 {
353 description = getServiceDescription(to, lang, null);
354 }
355 else
356 {
357 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
358 for (int i = 0; i < params.getLength(); i++)
359 {
360
361 Element param = (Element) params.item(i);
362 // Identify the structure information desired
363 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM))
364 {
365 String info = param.getAttribute(GSXML.VALUE_ATT);
366 if (description == null)
367 {
368 description = getServiceDescription(to, lang, info);
369 }
370 else
371 {
372 Element temp = getServiceDescription(to, lang, info);
373 GSXML.mergeElements(description, temp);
374 }
375 }
376 }
377 }
378 if (description != null)
379 { // may be null if non-existant service
380 response.appendChild(description);
381 }
382 return response;
383
384 }
385
386 /**
387 * process method for stylesheet requests
388 */
389 protected Element processFormat(Element request)
390 {
391 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
392 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_FORMAT);
393
394 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
395
396 if (to.equals(""))
397 { // serviceRack query - is this appropriate??
398 return response;
399 }
400
401 // describe a particular service
402 if (this.format_info_map.containsKey(to))
403 {
404 response.appendChild(response.getOwnerDocument().importNode(getServiceFormat(to), true));
405 if (_globalFormat != null)
406 {
407 response.appendChild(response.getOwnerDocument().importNode(GSXML.duplicateWithNewName(response.getOwnerDocument(), _globalFormat, GSXML.GLOBAL_FORMAT_ELEM, false), true));
408 }
409 response.setAttribute(GSXML.FROM_ATT, to);
410 return response;
411 }
412
413 // else no format info
414 logger.error("ServiceRack describe request: no format info for " + to + ".");
415 return response;
416 }
417
418 /** returns the service list for the subclass */
419 protected Element getServiceList(String lang)
420 {
421 // for now, it is static and has no language stuff
422 return (Element) this.short_service_info.cloneNode(true);
423 }
424
425 /** returns a specific service description */
426 abstract protected Element getServiceDescription(String service, String lang, String subset);
427
428 protected Element getServiceFormat(String service)
429 {
430 Element format = (Element) ((Element) this.format_info_map.get(service)).cloneNode(true);
431 return format;
432 }
433
434 /** overloaded version for no args case */
435 protected String getTextString(String key, String lang)
436 {
437 return getTextString(key, lang, null, null);
438 }
439
440 protected String getTextString(String key, String lang, String dictionary)
441 {
442 return getTextString(key, lang, dictionary, null);
443 }
444
445 protected String getTextString(String key, String lang, String[] args)
446 {
447 return getTextString(key, lang, null, args);
448 }
449
450 /**
451 * getTextString - retrieves a language specific text string for the given
452 * key and locale, from the specified resource_bundle (dictionary)
453 */
454 protected String getTextString(String key, String lang, String dictionary, String[] args)
455 {
456
457 // we want to use the collection class loader in case there are coll specific files
458 if (dictionary != null)
459 {
460 // just try the one specified dictionary
461 Dictionary dict = new Dictionary(dictionary, lang, this.class_loader);
462 String result = dict.get(key, args);
463 if (result == null)
464 { // not found
465 return "_" + key + "_";
466 }
467 return result;
468 }
469
470 // now we try class names for dictionary names
471 String class_name = this.getClass().getName();
472 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
473 Dictionary dict = new Dictionary(class_name, lang, this.class_loader);
474 String result = dict.get(key, args);
475 if (result != null)
476 {
477 return result;
478 }
479
480 // we have to try super classes
481 Class c = this.getClass().getSuperclass();
482 while (result == null && c != null)
483 {
484 class_name = c.getName();
485 class_name = class_name.substring(class_name.lastIndexOf('.') + 1);
486 if (class_name.equals("ServiceRack"))
487 {
488 // this is as far as we go
489 break;
490 }
491 dict = new Dictionary(class_name, lang, this.class_loader);
492 result = dict.get(key, args);
493 c = c.getSuperclass();
494 }
495 if (result == null)
496 {
497 return "_" + key + "_";
498 }
499 return result;
500
501 }
502
503 protected String getMetadataNameText(String key, String lang)
504 {
505
506 String properties_name = "metadata_names";
507 Dictionary dict = new Dictionary(properties_name, lang);
508
509 String result = dict.get(key);
510 if (result == null)
511 { // not found
512 return null;
513 }
514 return result;
515 }
516}
Note: See TracBrowser for help on using the repository browser.