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

Last change on this file since 5148 was 5114, checked in by kjdon, 21 years ago

now handles the case where there is no metadata for a cluster or collection

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