source: trunk/gsdl3/src/java/org/greenstone/gsdl3/collection/ServiceCluster.java@ 9433

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

added in code to cope when we have more than one service with the same name, eg multiple TextQuery services

  • Property svn:keywords set to Author Date Id Revision
File size: 21.9 KB
Line 
1/*
2 * ServiceCluster.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 */
19// leave the package name as is for now - should be changed to something better
20// cluster? groups?
21package org.greenstone.gsdl3.collection;
22
23
24import org.greenstone.gsdl3.util.*;
25import org.greenstone.gsdl3.core.*;
26import org.greenstone.gsdl3.service.*;
27
28// java XML classes we're using
29import org.w3c.dom.Document;
30import org.w3c.dom.Node;
31import org.w3c.dom.Element;
32import org.w3c.dom.NodeList;
33
34import java.io.File;
35import java.util.HashMap;
36
37/* ServiceCluster - a groups of services that are related in some way
38 * Implements ModuleInterface. Contains a list of services provided by the cluster, along with metadata about the cluster itself.
39 * a collection is a special type of cluster
40 * @author <a href="mailto:[email protected]">Katherine Don</a>
41 * @version $Revision: 9433 $
42 * @see ModuleInterface
43 */
44public class ServiceCluster
45 implements ModuleInterface {
46
47 protected static final String CONFIG_ENCODING = "utf-8";
48
49 protected static final String DEFAULT_LANG = "en"; // hack for now, should be read from the coll cfg file? or site cfg file for cluster
50
51 /** base directory for the site that this cluster belongs to*/
52 protected String site_home = null;
53 /** http address of the site that this cluster belongs to */
54 protected String site_http_address = null;
55 /** The name of the cluster - for a collection, this is the collection name*/
56 protected String cluster_name = null;
57
58 /** a reference to the message router */
59 protected MessageRouter router = null;
60 /** The map of services.
61 *
62 * Maps Services to ServiceRack objects
63 * @see ServiceRack
64 *
65 */
66 protected HashMap service_map=null;
67 /** maps pseudo service names to real service names - needed if we have two services with the same name for one collection */
68 protected HashMap service_name_map=null;
69
70 /** XML converter for String to DOM and vice versa */
71 protected XMLConverter converter=null;
72
73 /** container doc for description elements */
74 protected Document doc = null;
75 /** list of services */
76 protected Element service_list = null;
77 /** list of metadata - all metadata, regardless of language goes in here */
78 protected Element metadata_list = null;
79 /** language specific stuff */
80 //protected Element lang_specific_metadata_list = null;
81 protected Element display_item_list = null;
82 /** the element that will have any descriptions passed back in */
83 protected Element description = null;
84
85 public void setSiteHome(String home) {
86 this.site_home = home;
87 }
88
89 public void setSiteAddress(String address) {
90 this.site_http_address = address;
91 }
92
93 public void setClusterName(String name) {
94 this.cluster_name = name;
95 this.description.setAttribute(GSXML.NAME_ATT, name);
96 }
97
98 public void setMessageRouter(MessageRouter m) {
99 this.router = m;
100 }
101
102 public ServiceCluster() {
103 this.service_map = new HashMap();
104 this.service_name_map = new HashMap();
105 this.converter = new XMLConverter();
106 this.doc = this.converter.newDOM();
107 this.description = this.doc.createElement(GSXML.CLUSTER_ELEM);
108 this.display_item_list = this.doc.createElement(GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER);
109 this.metadata_list = this.doc.createElement(GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
110 }
111
112 /**
113 * Configures the cluster.
114 *
115 * gsdlHome and clusterName must be set before configure is called.
116 *
117 * reads the site configuration file, and configures itself
118 * this calls configure(Element) with the XML element node from the config
119 * file.
120 * configure(Element) should be used if the config file has already been
121 * parsed. This method will work with any subclass.
122 *
123 * @return true if configure successful, false otherwise.
124 */
125 public boolean configure() {
126
127 if (this.site_home == null || this.cluster_name== null) {
128 System.err.println("ServiceCluster: site_home and cluster_name must be set before configure called!");
129 return false;
130 }
131 System.out.println("configuring service cluster");
132 // read the site configuration file
133 File config_file = new File(GSFile.siteConfigFile(this.site_home));
134
135 if (!config_file.exists()) {
136 System.err.println("ServiceCluster: couldn't configure cluster: "+this.cluster_name +", "+config_file+" does not exist");
137 return false;
138 }
139
140 Document doc = this.converter.getDOM(config_file, CONFIG_ENCODING);
141
142 // get the appropriate service cluster element
143 Element cluster_list = (Element)GSXML.getChildByTagName(doc.getDocumentElement(), GSXML.CLUSTER_ELEM+GSXML.LIST_MODIFIER);
144 Element sc = GSXML.getNamedElement(cluster_list, GSXML.CLUSTER_ELEM,
145 GSXML.NAME_ATT, this.cluster_name);
146
147 return this.configure(sc);
148 }
149
150
151 public boolean configure(Element service_cluster_info) {
152
153 // get the metadata - for now just add it to the list
154 Element meta_list = (Element) GSXML.getChildByTagName(service_cluster_info, GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
155 if (meta_list !=null) {
156 if (!addMetadata(meta_list)) {
157
158 System.err.println("ServiceCluster: couldn't configure the metadata");
159 }
160 }
161
162 // get the display info
163 Element display_list = (Element) GSXML.getChildByTagName(service_cluster_info, GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER);
164 if (display_list !=null) {
165 if (!addDisplayItems(display_list)) {
166
167 System.err.println("ServiceCluster: couldn't configure the display items");
168 }
169 }
170
171
172 // do the service racks
173 Element service_rack_list = (Element)GSXML.getChildByTagName(service_cluster_info, GSXML.SERVICE_CLASS_ELEM+GSXML.LIST_MODIFIER);
174 if (service_rack_list == null) {
175 // is this an error? could you ever have a service cluster
176 // without service racks???
177 System.err.println("ServiceCluster: cluster has no service racks!!");
178 } else {
179
180 if (!configureServiceRack(service_rack_list, null)) {
181 System.err.println("ServiceCluster: couldn't configure the service racks!!");
182 return false;
183 }
184 }
185
186 return true;
187 }
188
189 /** adds metadata from a metadataList into the metadata_list xml
190 */
191 protected boolean addMetadata(Element metadata_list) {
192 if (metadata_list == null) return false;
193 NodeList metanodes = metadata_list.getElementsByTagName(GSXML.METADATA_ELEM);
194 if (metanodes.getLength()>0) {
195 for(int k=0; k<metanodes.getLength(); k++) {
196 this.metadata_list.appendChild(this.doc.importNode(metanodes.item(k), true));
197 }
198 }
199
200 return true;
201 }
202
203 protected boolean addDisplayItems(Element display_list) {
204
205 if (display_list==null) return false;
206 NodeList displaynodes = display_list.getElementsByTagName(GSXML.DISPLAY_TEXT_ELEM);
207 if (displaynodes.getLength()>0) {
208 for(int k=0; k<displaynodes.getLength(); k++) {
209 Element d = (Element) displaynodes.item(k);
210 String lang = d.getAttribute(GSXML.LANG_ATT);
211 if (lang==null||lang.equals("")) {
212 //set the lang to teh default
213 d.setAttribute(GSXML.LANG_ATT, DEFAULT_LANG);
214 }
215 String name = d.getAttribute(GSXML.NAME_ATT);
216 Element this_item = GSXML.getNamedElement(this.display_item_list, GSXML.DISPLAY_TEXT_ELEM, GSXML.NAME_ATT, name);
217 if (this_item==null) {
218 this_item = this.doc.createElement(GSXML.DISPLAY_TEXT_ELEM);
219 this_item.setAttribute(GSXML.NAME_ATT, name);
220 this.display_item_list.appendChild(this_item);
221 }
222
223 this_item.appendChild(this.doc.importNode(d, true));
224
225
226 }
227 }
228
229 return true;
230 }
231
232 /** creates and configures all the services - extra_info is some more xml
233that is passed to teh service - eg used for coll config files for Collection
234 */
235 protected boolean configureServiceRack(Element service_rack_list,
236 Element extra_info) {
237
238 // empty the service map in case this is a reconfigure
239 service_map.clear();
240 this.service_list = this.doc.createElement(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER);
241
242 // create all the services
243 NodeList nodes = service_rack_list.getElementsByTagName(GSXML.SERVICE_CLASS_ELEM);
244 if (nodes.getLength()==0) {
245 System.err.println("ServiceCluster configuration error: cluster "+this.cluster_name+" has no service modules!");
246 return false;
247 }
248
249 for(int i=0; i<nodes.getLength(); i++) {
250
251 // the xml request to send to the serviceRack to query what
252 // services it provides
253 Element message = this.doc.createElement(GSXML.MESSAGE_ELEM);
254 Element request = GSXML.createBasicRequest(this.doc, GSXML.REQUEST_TYPE_DESCRIBE, "", "", "");
255 message.appendChild(request);
256
257 Element n = (Element)nodes.item(i);
258 String servicetype = n.getAttribute(GSXML.NAME_ATT);
259
260 try {
261 ServiceRack s = (ServiceRack)Class.forName("org.greenstone.gsdl3.service."+servicetype).newInstance();
262
263 s.setSiteHome(this.site_home);
264 s.setSiteAddress(this.site_http_address);
265 s.setClusterName(this.cluster_name);
266 s.setMessageRouter(this.router);
267 // pass the xml node to the service for configuration
268 if (s.configure(n, extra_info)) {
269
270 // find out the supported service types for this service module
271 Node types = s.process(message);
272 NodeList typenodes = ((Element)types).getElementsByTagName(GSXML.SERVICE_ELEM);
273
274 for (int j=0; j<typenodes.getLength();j++) {
275 String service = ((Element) typenodes.item(j)).getAttribute(GSXML.NAME_ATT);
276 if (service_map.get(service)!=null) {
277 char extra = '0';
278 String new_service = service+extra;
279
280 while (service_map.get(new_service)!=null) {
281 extra++;
282 new_service = service+extra;
283 }
284 this.service_name_map.put(new_service, service);
285 service=new_service;
286 ((Element) typenodes.item(j)).setAttribute(GSXML.NAME_ATT, service);
287 }
288 this.service_map.put(service, s);
289 // also add info to the ServiceInfo XML element
290 this.service_list.appendChild(this.doc.importNode(typenodes.item(j), true));
291 }
292 }
293 } catch (Exception e) {
294 System.err.println("ServiceCluster Error: configure exception: couldn't create service module:org.greenstone.gsdl3.service."+servicetype+"\n"+e.getMessage() + e.getClass() );
295 e.printStackTrace();
296 //return false;
297 }
298
299 }
300 return true;
301
302
303 }
304
305
306 /**
307 * Process an XML document - uses Strings
308 * just calls process(Node).
309 *
310 * @param in the Document to process - a string
311 * @return the resultant document as a string - contains any error messages
312 * @see String
313 */
314 public String process(String in) {
315
316 Document doc = this.converter.getDOM(in);
317 Element e = doc.getDocumentElement();
318
319 Element res = process(e);
320 return this.converter.getString(res);
321
322 }
323
324 /** process XML as Node
325 *
326 */
327 public Element process(Element message) {
328
329 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
330 Document mess_doc = message.getOwnerDocument();
331 Element mainResult = this.doc.createElement(GSXML.MESSAGE_ELEM);
332 if (requests.getLength()==0) {
333 System.err.println("ServiceCluster.process: no requests for cluster:"+this.cluster_name);
334 // no requests
335 return mainResult; // for now
336 }
337 for (int i=0; i<requests.getLength(); i++) {
338 Element request = (Element)requests.item(i);
339 String to = request.getAttribute(GSXML.TO_ATT);
340
341 // the cluster name should be first, check, then remove
342 String clustername = GSPath.getFirstLink(to);
343 if (!clustername.equals(this.cluster_name)){
344 System.err.println("ServiceCluster Error: cluster name wrong! was "+clustername+" should have been "+this.cluster_name);
345 continue; // ignore this request
346 }
347 to = GSPath.removeFirstLink(to);
348 request.setAttribute(GSXML.TO_ATT, to);
349
350 if (to.equals("")) { // this command is for me
351 Element response = processMessage(request);
352 mainResult.appendChild(response);
353
354 } else { // the request is for one of my services
355 String service = GSPath.getFirstLink(to);
356
357 if (!this.service_map.containsKey(service)) {
358 System.err.println("ServiceCluster Error: non-existant service, "+service+", specified!");
359 continue;
360 }
361 String real_service = service;
362 if (this.service_name_map.containsKey(service)) {
363 real_service = (String)this.service_name_map.get(service);
364 // need to change the to att in the request - give the real service name
365 to = request.getAttribute(GSXML.TO_ATT);
366 String old_to = to;
367 to = GSPath.replaceFirstLink(to, real_service);
368 request.setAttribute(GSXML.TO_ATT, to);
369 }
370 // have to pass the request to the service
371 Element single_message = mess_doc.createElement(GSXML.MESSAGE_ELEM);
372 single_message.appendChild(request);
373 Element response_message = ((ModuleInterface)this.service_map.get(service)).process(single_message);
374 if (response_message != null) {
375 Element response = (Element) GSXML.getChildByTagName(response_message, GSXML.RESPONSE_ELEM);
376 String from = response.getAttribute(GSXML.FROM_ATT);
377 if (!real_service.equals(service)) {
378 // replace the real service name with the pseudo service name
379 from = GSPath.replaceFirstLink(from, service);
380 // also need to do it in the service itself
381 // shoudl this be done here??
382 Element service_elem = (Element) GSXML.getChildByTagName(response, GSXML.SERVICE_ELEM);
383 if (service_elem!= null) {
384 service_elem.setAttribute(GSXML.NAME_ATT, service);
385 }
386 }
387 from = GSPath.prependLink(from, this.cluster_name);
388 response.setAttribute(GSXML.FROM_ATT, from);
389 mainResult.appendChild(this.doc.importNode(response, true));
390 }
391
392 } // else
393
394
395 } // for each request
396 return mainResult;
397 }
398
399 /** handles requests made to the ServiceCluster itself
400 *
401 * @param req - the request Element- <request>
402 * @return the result Element - should be <response>
403 */
404 protected Element processMessage(Element request) {
405
406 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
407 response.setAttribute(GSXML.FROM_ATT, this.cluster_name);
408 String type = request.getAttribute(GSXML.TYPE_ATT);
409 String lang = request.getAttribute(GSXML.LANG_ATT);
410 response.setAttribute(GSXML.TYPE_ATT, type);
411
412 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE)) {
413 // create the collection element
414 Element description = (Element)this.description.cloneNode(false);
415 response.appendChild(description);
416 // check the param list
417 Element param_list = (Element) GSXML.getChildByTagName(request, GSXML.PARAM_ELEM+GSXML.LIST_MODIFIER);
418 if (param_list == null) {
419 addAllDisplayInfo(description, lang);
420 description.appendChild(this.service_list);
421 description.appendChild(this.metadata_list);
422 return response;
423 }
424
425 // go through the param list and see what components are wanted
426 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
427 for (int i=0; i<params.getLength(); i++) {
428
429 Element param = (Element)params.item(i);
430 // Identify the structure information desired
431 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM)) {
432 String info = param.getAttribute(GSXML.VALUE_ATT);
433 if (info.equals(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER)) {
434 description.appendChild(this.service_list);
435 } else if (info.equals(GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER)) {
436 description.appendChild(metadata_list);
437 } else if (info.equals(GSXML.DISPLAY_TEXT_ELEM+GSXML.LIST_MODIFIER)) {
438 addAllDisplayInfo(description, lang);
439
440 }
441 }
442 }
443 return response;
444 }
445
446 if (type.equals(GSXML.REQUEST_TYPE_SYSTEM)) {
447 response = processSystemRequest(request);
448 } else { // unknown type
449 System.err.println("ServiceCluster Error: cant handle request of type "+ type);
450
451 }
452 return response;
453 }
454
455 protected Element processSystemRequest(Element request) {
456
457 Element response = this.doc.createElement(GSXML.RESPONSE_ELEM);
458 response.setAttribute(GSXML.FROM_ATT, this.cluster_name);
459 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_SYSTEM);
460
461 // a list of system requests - should put any error messages
462 // or success messages into response
463 NodeList commands = request.getElementsByTagName(GSXML.SYSTEM_ELEM);
464 String message=null;
465 for (int i=0; i<commands.getLength(); i++) {
466 // all the commands should be Elements
467 Element elem = (Element)commands.item(i);
468 String action = elem.getAttribute(GSXML.TYPE_ATT);
469 if (action.equals(GSXML.SYSTEM_TYPE_CONFIGURE)) {
470 String subset = elem.getAttribute(GSXML.SYSTEM_SUBSET_ATT);
471 if (subset.equals("")) {
472 // need to reconfigure the service cluster
473
474 if (this.configure()) {
475 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, this.cluster_name + " reconfigured");
476 response.appendChild(s);
477
478 } else {
479 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, this.cluster_name + " could not be reconfigured");
480 response.appendChild(s);
481 }
482 } else if (this.configureSubset(subset)) {
483 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, this.cluster_name + " "+subset+" reconfigured");
484 response.appendChild(s);
485 } else {
486 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, this.cluster_name + " "+subset + " could not be reconfigured");
487 response.appendChild(s);
488 }
489 continue;
490 } // configure action
491
492 String module_name = elem.getAttribute(GSXML.SYSTEM_MODULE_NAME_ATT);
493 String module_type = elem.getAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT);
494 if (action.equals(GSXML.SYSTEM_TYPE_ACTIVATE)) {
495 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, "activate action not yet implemented - does it even make sense in this context??");
496 response.appendChild(s);
497 } else if (action.equals(GSXML.SYSTEM_TYPE_DEACTIVATE)) {
498 if (module_type.equals(GSXML.SERVICE_ELEM)) {
499 // deactivate the service
500 // remove from service_map
501 this.service_map.remove(module_name);
502 Element service_elem = GSXML.getNamedElement(this.service_list, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, module_name);
503 service_list.removeChild(service_elem);
504 message = module_type+": "+module_name+" deactivated";
505 } else {
506 message = "can't deactivate "+module_type+" type modules!";}
507 Element s = GSXML.createTextElement(this.doc, GSXML.STATUS_ELEM, message);
508 response.appendChild(s);
509 } else {
510 System.err.println("ServiceCluster: cant process system request, action "+action);
511 continue;
512 }
513 } // for each command
514 return response;
515 }
516
517 /**
518 * do a configure on only part of the collection
519 */
520 protected boolean configureSubset(String subset) {
521
522 File configFile = new File(GSFile.siteConfigFile(this.site_home));
523 if (!configFile.exists() ) {
524 System.err.println("ServiceCluster: site config file: "+configFile.getPath()+" not found!");
525 // wont be able to do any of the requests
526 return false;
527
528 }
529
530 Element site_config_elem = this.converter.getDOM(configFile).getDocumentElement();
531 Element cluster_config_elem = GSXML.getNamedElement((Element)GSXML.getChildByTagName(site_config_elem, GSXML.CLUSTER_ELEM+GSXML.LIST_MODIFIER), GSXML.CLUSTER_ELEM, GSXML.NAME_ATT, this.cluster_name);
532 if (cluster_config_elem == null) {
533 System.err.println("ServiceCluster: site config file: "+configFile.getPath()+" has no element for cluster "+this.cluster_name);
534 // wont be able to do any of teh requests
535 return false;
536
537 }
538 if (subset.equals(GSXML.SERVICE_ELEM+GSXML.LIST_MODIFIER)) {
539 Element service_rack_list = (Element)GSXML.getChildByTagName(cluster_config_elem, GSXML.SERVICE_CLASS_ELEM+GSXML.LIST_MODIFIER);
540
541 return configureServiceRack(service_rack_list, null);
542 } else if (subset.equals(GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER)) {
543 this.metadata_list = this.doc.createElement(GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
544 Element metadata_list = (Element)GSXML.getChildByTagName(cluster_config_elem, GSXML.METADATA_ELEM+GSXML.LIST_MODIFIER);
545 return addMetadata(metadata_list);
546 } else {
547 System.err.println("ServiceCluster: cant process system request, configure "+subset);
548 return false;
549 }
550
551 }
552
553
554 protected boolean addAllDisplayInfo(Element description, String lang) {
555
556 NodeList items = this.display_item_list.getChildNodes();
557 for (int i=0; i<items.getLength(); i++) { // for each key
558 Element m = (Element) items.item(i);
559 // find the child with the correct language
560 Element new_m = GSXML.getNamedElement(m, GSXML.DISPLAY_TEXT_ELEM, GSXML.LANG_ATT, lang);
561 if (new_m==null && lang != DEFAULT_LANG) {
562 // use the default lang
563 new_m = GSXML.getNamedElement(m, GSXML.DISPLAY_TEXT_ELEM, GSXML.LANG_ATT, DEFAULT_LANG);
564 }
565 if (new_m==null) {
566 // just get the first one
567 new_m = (Element)GSXML.getChildByTagName(m, GSXML.DISPLAY_TEXT_ELEM);
568 }
569 description.appendChild(new_m.cloneNode(true));
570 }
571 return true;
572
573 }
574
575
576 protected Element getDisplayTextElement(String key, String lang) {
577
578 Element this_item = GSXML.getNamedElement(this.display_item_list, GSXML.DISPLAY_TEXT_ELEM, GSXML.NAME_ATT, key);
579 if (this_item == null) {
580 return null;
581 }
582
583 Element this_lang = GSXML.getNamedElement(this_item, GSXML.DISPLAY_TEXT_ELEM, GSXML.LANG_ATT, lang);
584 if (this_lang == null && lang != DEFAULT_LANG) {
585 // try the default
586 this_lang = GSXML.getNamedElement(this_item, GSXML.DISPLAY_TEXT_ELEM, GSXML.LANG_ATT, DEFAULT_LANG);
587 }
588 if (this_lang == null) {
589 // just return the first one
590 return (Element)this_item.getFirstChild().cloneNode(true);
591 }
592 return (Element)this_lang.cloneNode(true);
593
594 }
595}
596
597
598
599
600
601
Note: See TracBrowser for help on using the repository browser.