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

Last change on this file since 16780 was 16688, checked in by davidb, 16 years ago

Changed 'Element process(Element)' in ModuleInterface to 'Node process(Node)'. After some deliberation is was decided this is a more useful (generic) layer of the DOM to pass information around in. Helps with the DocType problem when producing XSL Transformed pages, for example. When this was an Element, it would loose track of its DocType. Supporting method provided in XMLConverter 'Element nodeToElement(Node)' which checks a nodes docType and casts to Element if appropriate, or if a Document, typecasts to that and then extracts the top-level Element. With this fundamental change in ModuleInterface, around 20 files needed to be updated (Actions, Services, etc) that build on top of 'process()' to reflect this change, and use nodeToElement where necessary.

  • 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.
49 * This base class implements the process method.
50 *Each service is invoked
51 * by a method called process<service name> which takes one parameter - the xml request Element, and returns an XML response Element.
52 * for example, the TextQuery service would be invoked by
53 * processTextQuery(Element request)
54 *
55 * @author <a href="mailto:[email protected]">Katherine Don</a>
56 * @version $Revision: 16688 $
57 */
58public abstract class ServiceRack
59 implements ModuleInterface
60{
61
62 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.service.ServiceRack.class.getName());
63
64 /** the absolute address of the site home */
65 protected String site_home =null;
66 /** the http address of the site home */
67 protected String site_http_address =null;
68
69 protected String library_name = null;
70 /** the name of the cluster (or collection) that this service
71 belongs to - if any */
72 protected String cluster_name = null;
73
74 /** some services can talk back to the message router */
75 protected MessageRouter router = null;
76
77 /** a converter class to create Documents etc */
78 protected XMLConverter converter = null;
79
80 /** the original config info - if need to store it */
81 protected Element config_info = null;
82
83 /** XML element for describe requests - the container doc */
84 protected Document doc = null;
85
86 /** XML element for describe requests - list of supported services
87 - this is static */
88 protected Element short_service_info = null;
89
90 /** XML element for stylesheet requests - map of service name to format
91 elem */
92 protected HashMap format_info_map = null;
93
94 /** A class loader that knows about the collection resources directory
95 * can put properties files, dtds etc in here */
96 CollectionClassLoader class_loader = null;
97
98 /** sets the cluster name */
99 public void setClusterName(String cluster_name) {
100 this.cluster_name = cluster_name;
101 }
102 /** sets the collect name */
103 public void setCollectionName(String coll_name) {
104 setClusterName(coll_name);
105 }
106
107 public void cleanUp() {}
108
109 /** sets the site home */
110 public void setSiteHome(String site_home) {
111 this.site_home = site_home;
112 }
113 /** sets the site http address */
114 public void setSiteAddress(String site_address) {
115 this.site_http_address = site_address;
116 }
117
118 public void setLibraryName(String library_name) {
119 this.library_name = library_name;
120 }
121 public String getLibraryName() {
122 return this.library_name;
123 }
124 /** sets the message router */
125 public void setMessageRouter(MessageRouter m) {
126 this.router = m;
127 setLibraryName(m.getLibraryName());
128 }
129
130 /** the no-args constructor */
131 public ServiceRack() {
132 this.converter = new XMLConverter();
133 this.doc = this.converter.newDOM();
134 this.short_service_info = this.doc.createElement(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER);
135 this.format_info_map = new HashMap();
136 }
137
138
139 /** configure the service module
140 *
141 * @param info the XML node <serviceRack name="XXX"/> with name equal
142 * to the class name (of the subclass)
143 *
144 * must configure short_service_info_ and service_info_map_
145 * @return true if configured ok
146 * must be implemented in subclasses
147 */
148 public boolean configure(Element info) {
149 return configure(info, null);
150 }
151
152 public boolean configure(Element info, Element extra_info) {
153 // set up the class loader
154 this.class_loader = new CollectionClassLoader(this.getClass().getClassLoader(), this.site_home, this.cluster_name);
155 return true;
156 }
157
158 /**
159 * Process an XML document - convenience method that uses Strings rather than Elements. just calls process(Element).
160 *
161 * @param xml_in the Document to process - a string
162 * @return the resultant document as a string - contains any error messages
163 * @see String
164 */
165 public String process(String xml_in) {
166
167 Document doc = this.converter.getDOM(xml_in);
168 if (doc == null) {
169 logger.error("Couldn't parse request");
170 logger.error(xml_in);
171 return null;
172 }
173 Node res = process(doc);
174 return this.converter.getString(res);
175
176 }
177
178 /** process an XML request in DOM form
179 *
180 * @param message the Node node containing the request
181 * should be <message>
182 * @return an Node with the result XML
183 * @see Node/Element
184 */
185 public Node process(Node message_node) {
186
187 Element message = this.converter.nodeToElement(message_node);
188
189 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
190 Document mess_doc = message.getOwnerDocument();
191 Element mainResult = this.doc.createElement(GSXML.MESSAGE_ELEM);
192 if (requests.getLength()==0) {
193 // no requests
194 return mainResult; // empty message for now
195 }
196
197 for (int i=0; i<requests.getLength(); i++) {
198 Element request = (Element)requests.item(i);
199
200 String type = request.getAttribute(GSXML.TYPE_ATT);
201 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE)) {
202 Element response = processDescribe(request);
203 if (response !=null) {
204 mainResult.appendChild(this.doc.importNode(response, true));
205 }
206
207 } else if (type.equals(GSXML.REQUEST_TYPE_FORMAT)) {
208 Element response = processFormat(request);
209 mainResult.appendChild(this.doc.importNode(response, true));
210
211
212 } else {
213 // other type of request, must be processed by the subclass -
214 // send to the service method
215 StringBuffer error_string = new StringBuffer();
216 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
217 Element response = null;
218 try {
219 Class c = this.getClass();
220 Class []params = {Class.forName("org.w3c.dom.Element")};
221
222 String method_name = "process"+to;
223 Method m = null;
224 while (c != null) {
225
226 try {
227 m = c.getDeclaredMethod(method_name, params);
228 // if this has worked, break
229 break;
230 } catch (NoSuchMethodException e) {
231 c = c.getSuperclass();
232 } catch (SecurityException e) {
233 logger.error("security exception for finding method "+method_name);
234 error_string.append("ServiceRack.process: security exception for finding method "+method_name);
235 }
236 } // while
237 if (m != null) {
238 Object []args = {request};
239 try {
240 response = (Element)m.invoke(this, args);
241
242 } catch (Exception e) {
243 logger.error("Trying to call a processService type method (process"+to+") on a subclass("+this.getClass().getName()+"), but an exception happened:"+e.toString());
244 error_string.append("Trying to call a processService type method (process"+to+") on a subclass("+this.getClass().getName()+"), but an exception happened:"+e.toString());
245 }
246 } else {
247 logger.error("method "+method_name+" not found for class "+this.getClass().getName());
248 error_string.append("ServiceRack.process: method "+method_name+" not found for class "+this.getClass().getName());
249 }
250
251 } catch (ClassNotFoundException e) {
252 logger.error("Element class not found");
253 error_string.append("Element class not found");
254 }
255 if (response !=null) {
256 mainResult.appendChild(this.doc.importNode(response, true));
257 } else {
258 // add in a dummy response
259 logger.error("adding in an error element\n");
260 response = this.doc.createElement(GSXML.RESPONSE_ELEM);
261 GSXML.addError(this.doc, response, error_string.toString());
262 mainResult.appendChild(response);
263
264 }
265
266 } // else process request
267 } // for each request
268
269 return mainResult;
270
271 }
272
273
274
275 /** process method for describe requests
276 */
277 protected Element processDescribe(Element request) {
278
279 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
280 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
281
282 String lang = request.getAttribute(GSXML.LANG_ATT);
283 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
284 if (to.equals("")) { // return the service list
285 response.appendChild(getServiceList(lang));
286 return response;
287 }
288 response.setAttribute(GSXML.FROM_ATT, to);
289 Element param_list = (Element)GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
290 Element description = null;
291 if (param_list == null) {
292 description = getServiceDescription(to, lang, null);
293 } else {
294 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
295 for (int i=0; i<params.getLength(); i++) {
296
297 Element param = (Element)params.item(i);
298 // Identify the structure information desired
299 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM )) {
300 String info = param.getAttribute(GSXML.VALUE_ATT);
301 if (description == null) {
302 description = getServiceDescription(to, lang, info);
303 } else {
304 Element temp = getServiceDescription(to, lang, info);
305 GSXML.mergeElements(description, temp);
306 }
307 }
308 }
309 }
310 if (description != null) { // may be null if non-existant service
311 response.appendChild(description);
312 }
313 return response;
314
315 }
316
317 /** process method for stylesheet requests
318 */
319 protected Element processFormat(Element request) {
320 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
321 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_FORMAT);
322
323 String to = GSPath.getFirstLink(request.getAttribute(GSXML.TO_ATT));
324
325 if (to.equals("")) { // serviceRack query - is this appropriate??
326 return response;
327 }
328
329
330 // describe a particular service
331 if (this.format_info_map.containsKey(to)) {
332 response.appendChild(getServiceFormat(to));
333 response.setAttribute(GSXML.FROM_ATT, to);
334 return response;
335 }
336 // else no format info
337 logger.error("ServiceRack describe request: no format info for "+to+".");
338 return response;
339 }
340
341 /** returns the service list for the subclass */
342 protected Element getServiceList(String lang) {
343 // for now, it is static and has no language stuff
344 return (Element) this.short_service_info.cloneNode(true);
345 }
346
347 /** returns a specific service description */
348 abstract protected Element getServiceDescription(String service, String lang, String subset);
349
350 protected Element getServiceFormat(String service) {
351 Element format = (Element)((Element)this.format_info_map.get(service)).cloneNode(true);
352 return format;
353 }
354
355 /** overloaded version for no args case */
356 protected String getTextString(String key, String lang) {
357 return getTextString(key, lang, null, null);
358 }
359
360 protected String getTextString(String key, String lang, String dictionary) {
361 return getTextString(key, lang, dictionary, null);
362 }
363 protected String getTextString(String key, String lang, String [] args) {
364 return getTextString(key, lang, null, args);
365 }
366
367 /** getTextString - retrieves a language specific text string for the given
368key and locale, from the specified resource_bundle (dictionary)
369 */
370 protected String getTextString(String key, String lang, String dictionary, String[] args) {
371
372 // we want to use the collection class loader in case there are coll specific files
373 if (dictionary != null) {
374 // just try the one specified dictionary
375 Dictionary dict = new Dictionary(dictionary, lang, this.class_loader);
376 String result = dict.get(key, args);
377 if (result == null) { // not found
378 return "_"+key+"_";
379 }
380 return result;
381 }
382
383 // now we try class names for dictionary names
384 String class_name = this.getClass().getName();
385 class_name = class_name.substring(class_name.lastIndexOf('.')+1);
386 Dictionary dict = new Dictionary(class_name, lang, this.class_loader);
387 String result = dict.get(key, args);
388 if (result != null) {
389 return result;
390 }
391
392 // we have to try super classes
393 Class c = this.getClass().getSuperclass();
394 while (result == null && c != null) {
395 class_name = c.getName();
396 class_name = class_name.substring(class_name.lastIndexOf('.')+1);
397 if (class_name.equals("ServiceRack")) {
398 // this is as far as we go
399 break;
400 }
401 dict = new Dictionary(class_name, lang, this.class_loader);
402 result = dict.get(key, args);
403 c = c.getSuperclass();
404 }
405 if (result == null) {
406 return "_"+key+"_";
407 }
408 return result;
409
410 }
411
412 protected String getMetadataNameText(String key, String lang) {
413
414 String properties_name = "metadata_names";
415 Dictionary dict = new Dictionary(properties_name, lang);
416
417 String result = dict.get(key);
418 if (result == null) { // not found
419 return null;
420 }
421 return result;
422 }
423}
424
Note: See TracBrowser for help on using the repository browser.