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

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

Service racks now store the global format and return it whenever the format is requested

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