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

Last change on this file since 10093 was 10093, checked in by kjdon, 19 years ago

The ServiceRack class's configure method is no longer abstract so all the
subclasses should call super.configure.

  • 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: 10093 $
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 ModuleInterface 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(ModuleInterface 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.