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

Last change on this file since 24393 was 24393, checked in by sjm84, 13 years ago

Adding in the server-side code for the Document Maker as well as several other enhancements

  • Property svn:keywords set to Author Date Id Revision
File size: 13.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 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 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();
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.