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

Last change on this file since 11266 was 11266, checked in by kjdon, 18 years ago

router is now a MessageRouter, not a ModuleInterface - we want access to the config_info

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