source: main/trunk/greenstone3/src/java/org/greenstone/gsdl3/core/MessageRouter.java@ 33180

Last change on this file since 33180 was 33180, checked in by kjdon, 5 years ago

adding library_name in front of sites for colleciton assoc file links - we can't get library name otherwise in URLFilter as this Filter operates outside of the servlets. So lets pass in via our URLs. used for security checking collection documents

  • Property svn:keywords set to Author Date Id Revision
File size: 44.0 KB
Line 
1/*
2 * MessageRouter.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.core;
20
21import java.io.File;
22import java.net.Authenticator;
23import java.net.PasswordAuthentication;
24import java.util.ArrayList;
25import java.util.HashMap;
26import java.util.Map;
27import java.util.Iterator;
28
29import org.apache.commons.lang3.StringUtils;
30import org.apache.log4j.Logger;
31import org.greenstone.gsdl3.collection.Collection;
32import org.greenstone.gsdl3.collection.ServiceCluster;
33import org.greenstone.gsdl3.comms.Communicator;
34import org.greenstone.gsdl3.comms.SOAPCommunicator;
35import org.greenstone.gsdl3.service.ServiceRack;
36import org.greenstone.gsdl3.util.CustomClassLoader;
37import org.greenstone.gsdl3.util.DisplayItemUtil;
38import org.greenstone.gsdl3.util.GSFile;
39import org.greenstone.gsdl3.util.GSParams;
40import org.greenstone.gsdl3.util.GSPath;
41import org.greenstone.gsdl3.util.GSXML;
42import org.greenstone.gsdl3.util.UserContext;
43import org.greenstone.gsdl3.util.XMLConverter;
44import org.greenstone.util.GlobalProperties;
45import org.w3c.dom.Document;
46import org.w3c.dom.Element;
47import org.w3c.dom.Node;
48import org.w3c.dom.NodeList;
49
50/**
51 * The hub of a Greenstone system.
52 *
53 * Accepts XML requests (via process method of ModuleInterface) and routes them
54 * to the appropriate collection or service or external entity.
55 *
56 * contains a map of module objects - may be services, collections, comms
57 * objects talking to other MessageRouters etc.
58 *
59 *
60 * Since some service classes are moved into a separate directory in order
61 * for them to be checked out from a different repository, we modify the
62 * configureServices method to search some of the classes in other place if
63 * they are not found in the service directory.
64 */
65public class MessageRouter implements ModuleInterface
66{
67
68 static Logger logger = Logger.getLogger(org.greenstone.gsdl3.core.MessageRouter.class.getName());
69
70 /** the (directory) name of the site */
71 protected String site_name = null;
72 /** site home - the home directory for the site */
73 protected String site_home = null;
74 /** the http address for this site */
75 protected String site_http_address = null;
76
77 protected String library_name = null;
78
79 /** map of names to Module objects */
80 protected HashMap<String, ModuleInterface> module_map = null;
81
82 /** the full description of this site */
83
84 // should these things be separated into local and remote??
85
86 /** the original xml config element */
87 public Element config_info = null;
88
89 /** list of collections that can be reached */
90 protected Element collection_list = null;
91 /** list of collections that are loaded but are private */
92 protected Element private_collection_list = null;
93 /** list of service clusters that can be reached */
94 protected Element cluster_list = null;
95 /** list of single services that can be reached */
96 protected Element service_list = null;
97 /** list of external sites that can be reached */
98 protected Element site_list = null;
99 /** list of metadata for the site */
100 protected Element metadata_list = null;
101 /** list of display items for all languages */
102 protected Element display_item_list = null;
103
104 /** the list of params */
105 protected GSParams params = null;
106
107 /** a customised class loader so we can find resources in site resources folder*/
108 protected CustomClassLoader class_loader = null;
109 protected static final String DEFAULT_LANG = "en"; // hack for now, should be read from the coll cfg file? or site cfg file for cluster
110
111 //***************************************************************
112 // public methods
113 //***************************************************************
114
115 /** constructor */
116 public MessageRouter()
117 {
118 }
119
120 public void cleanUp()
121 {
122 cleanUpModuleMapEntire();
123 }
124
125 /** site_name must be set before configure is called */
126 public void setSiteName(String site_name)
127 {
128 this.site_name = site_name;
129 }
130
131 public String getSiteName()
132 {
133 return this.site_name;
134 }
135
136 /** library_name must be set before configure is called */
137 public void setLibraryName(String library_name)
138 {
139 this.library_name = library_name;
140 }
141
142 public String getLibraryName()
143 {
144 return this.library_name;
145 }
146
147 public void setParams(GSParams params)
148 {
149 this.params = params;
150 }
151
152 /**
153 * configures the system
154 *
155 * looks in site_home/collect for collections, reads config file
156 * site_home/siteConfig.xml
157 *
158 */
159 public boolean configure()
160 {
161
162 logger.info("configuring the Message Router");
163
164 if (this.site_name == null)
165 {
166 logger.error(" You must set site_name before calling configure");
167 return false;
168 }
169 this.site_home = GSFile.siteHome(GlobalProperties.getGSDL3Home(), this.site_name);
170 // set up the class loader
171 this.class_loader = new CustomClassLoader(this.getClass().getClassLoader(), GSFile.siteResourceDir(this.site_home));
172
173 String web_address = GlobalProperties.getGSDL3WebAddress();
174 if (web_address.equals("")) { // *****
175 this.site_http_address = this.library_name+"/sites/"+this.site_name;
176 } else {
177 this.site_http_address = web_address + "/"+this.library_name+"/sites/" + this.site_name;
178 }
179 logger.error("KATH - site http address = "+this.site_http_address);
180 // are we behind a firewall?? - is there a better place to set up the proxy?
181 String host = GlobalProperties.getProperty("proxy.host");
182 String port = GlobalProperties.getProperty("proxy.port");
183 final String user = GlobalProperties.getProperty("proxy.user");
184 final String passwd = GlobalProperties.getProperty("proxy.password");
185
186 if (host != null && !host.equals("") && port != null && !port.equals(""))
187 {
188 System.setProperty("http.proxyType", "4");
189 System.setProperty("http.proxyHost", host);
190 System.setProperty("http.proxyPort", port);
191 System.setProperty("http.proxySet", "true");
192 // have we got a user/password?
193 if (user != null && !user.equals("") && passwd != null && !passwd.equals(""))
194 {
195 try
196 {
197 // set up the authenticator
198 Authenticator.setDefault(new Authenticator()
199 {
200 protected PasswordAuthentication getPasswordAuthentication()
201 {
202 return new PasswordAuthentication(user, new String(passwd).toCharArray());
203 }
204 });
205
206 }
207 catch (Exception e)
208 {
209 logger.error("MessageRouter Error: couldn't set up an authenticator the proxy");
210
211 }
212 }
213 }
214
215 this.module_map = new HashMap<String, ModuleInterface>();
216
217 // This stuff may be done at a reconfigure also
218 return configureLocalSite();
219
220 }
221
222 /**
223 * Process an XML request - as a String
224 *
225 * @param xml_in
226 * the request to process
227 * @return the response - contains any error messages
228 * @see String
229 */
230 public String process(String xml_in)
231 {
232
233 Document doc = XMLConverter.getDOM(xml_in);
234
235 Node result = process(doc);
236 return XMLConverter.getString(result);
237 }
238
239 /**
240 * Process an XML request - as a DOM Element
241 *
242 * @param xml_in
243 * the message to process - should be <message>
244 * @return the response - contains any error messages
245 * @see Element
246 */
247 public Node process(Node message_node)
248 {
249
250 Document doc = XMLConverter.newDOM();
251 Element message = GSXML.nodeToElement(message_node);
252
253 // check that its a correct message tag
254 if (!message.getTagName().equals(GSXML.MESSAGE_ELEM))
255 {
256 logger.error(" Invalid message. GSDL message should start with <" + GSXML.MESSAGE_ELEM + ">, instead it starts with:" + message.getTagName() + ".");
257 return null;
258 }
259
260 NodeList requests = message.getElementsByTagName(GSXML.REQUEST_ELEM);
261
262 Element mainResult = doc.createElement(GSXML.MESSAGE_ELEM);
263
264 // empty request
265 if (requests.getLength() == 0)
266 {
267 logger.error("empty request");
268 return mainResult;
269 }
270
271 // for now, just process each request one by one, and append the results to mainResult
272 // Note: if you add an element to another node in the same document, it
273 // gets removed from where it was. This changes the node list - you cant iterate over the node list in a normal manner if you are moving elements out of it
274 int num_requests = requests.getLength();
275 for (int i = 0; i < num_requests; i++)
276 {
277 Node result = null;
278 Element req = (Element) requests.item(i);
279 if (req == null)
280 {
281 logger.warn("request " + i + " is null");
282 continue;
283 }
284 String path = req.getAttribute(GSXML.TO_ATT); // returns "" if no att of this name
285 if (path.equals(""))
286 {
287 // its a message for the message router
288 String type_att = req.getAttribute(GSXML.TYPE_ATT);
289 if (type_att.equals(GSXML.REQUEST_TYPE_MESSAGING))
290 {
291 // its a messaging request - modifies the requests/responses
292 result = modifyMessages(req, message, mainResult);
293 }
294 else
295 {
296 // standard request
297 result = processMessage(req);
298 }
299
300 if (result != null)
301 {
302 mainResult.appendChild(doc.importNode(result, true));
303 }
304 }
305 else
306 {
307 // The message needs to go to another module. The same message can
308 // be passed to multiple modules - they will be in a comma
309 // separated list in the 'to' attribute
310 String[] modules = StringUtils.split(path, ",");
311
312 for (String this_mod : modules)
313 {
314 // why can't we do this outside the loop??
315 Element mess = doc.createElement(GSXML.MESSAGE_ELEM);
316 Element copied_request = (Element) doc.importNode(req, true);
317 mess.appendChild(copied_request);
318
319 // find the module to pass it on to
320 // need to put the request into a message element
321 String obj = GSPath.getFirstLink(this_mod);
322
323 if (this.module_map.containsKey(obj))
324 {
325 copied_request.setAttribute(GSXML.TO_ATT, this_mod);
326 result = this.module_map.get(obj).process(mess);
327
328 if (result != null)
329 {
330 // append the contents of the message to the mainResult - there will only be one response at this stage
331 Node res = GSXML.getChildByTagName(result, GSXML.RESPONSE_ELEM);
332 if (res != null)
333 {
334 mainResult.appendChild(doc.importNode(res, true));
335 }
336 }
337 else
338 {
339 // add in a place holder response
340 Element response = doc.createElement(GSXML.RESPONSE_ELEM);
341 response.setAttribute(GSXML.FROM_ATT, this_mod);
342 mainResult.appendChild(response);
343 logger.error("MessageRouter Error: request had null result!");
344 }
345 }
346 else
347 {
348 logger.error("MessageRouter Error: request has illegal module name in:\n" + XMLConverter.getString(req));
349 }
350 }
351 }
352
353 } // for each request
354
355 //logger.debug("MR returned response");
356 //logger.debug(XMLConverter.getString(mainResult));
357
358 return mainResult;
359 }
360
361 public Element getCollectionList()
362 {
363 return collection_list;
364 }
365
366 public Element getPrivateCollectionList()
367 {
368 return private_collection_list;
369 }
370
371 public HashMap<String, ModuleInterface> getModuleMap()
372 {
373 return module_map;
374 }
375
376 // ********************************************************************
377 // auxiliary configure and cleanup methods
378 // *******************************************************************
379
380 /**
381 * Calls clean up on all modules referenced in the module_map and removes
382 * them .
383 */
384 protected void cleanUpModuleMapEntire()
385 {
386 if (this.module_map != null)
387 {
388 Iterator<ModuleInterface> i = this.module_map.values().iterator();
389 while (i.hasNext())
390 {
391 ModuleInterface i_next = i.next();
392
393 i_next.cleanUp();
394 i.remove();
395 }
396 }
397 }
398
399 /**
400 * Goes through the children of list, and for each local/site-specific name
401 * attribute, calls cleanUp on the module and removes it from the module_map
402 * and removes it from the list
403 */
404 protected void cleanUpModuleMapSubset(Element list, String remote_site)
405 {
406
407 NodeList elements = list.getChildNodes(); // we are assuming no extraneous nodes
408 for (int i = elements.getLength() - 1; i >= 0; i--)
409 {
410 Element item = (Element) elements.item(i);
411 String name = item.getAttribute(GSXML.NAME_ATT);
412 String potential_site_name = GSPath.getFirstLink(name);
413 if (remote_site != null)
414 {
415 if (remote_site.equals(potential_site_name))
416 {
417 list.removeChild(item);
418 }
419 }
420 else
421 {
422 if (name.equals(potential_site_name))
423 {// there was no site
424 list.removeChild(item);
425 ModuleInterface m = this.module_map.remove(name);
426 m.cleanUp(); // clean up any open files/connections etc
427 m = null;
428 }
429 }
430 }
431 }
432
433 /**
434 * removes all site modules from module_map, and any stored info about this
435 * sites collections and services
436 */
437 protected void cleanUpAllExternalSiteInfo()
438 {
439
440 NodeList site_nodes = this.site_list.getChildNodes();
441 for (int i = site_nodes.getLength() - 1; i >= 0; i--)
442 {
443 Element item = (Element) site_nodes.item(i);
444 String name = item.getAttribute(GSXML.NAME_ATT);
445 // will remove the node from site_list
446 deactivateModule(GSXML.SITE_ELEM, name);
447 }
448
449 }
450
451 /**
452 * read thru own site config file - create services and connect to sites
453 */
454 protected boolean configureLocalSite()
455 {
456
457 // this may be a reconfigure, so clean up the old moduleMap
458 cleanUpModuleMapEntire();
459
460 File configFile = new File(GSFile.siteConfigFile(this.site_home));
461
462 if (!configFile.exists())
463 {
464 logger.error(" site config file: " + configFile.getPath() + " not found!");
465 return false;
466 }
467
468 Document config_doc = XMLConverter.getDOM(configFile);
469 if (config_doc == null)
470 {
471 logger.error(" couldn't parse site config file: " + configFile.getPath());
472 return false;
473 }
474
475 this.config_info = config_doc.getDocumentElement();
476
477 // load up the services: serviceRackList
478 Document doc = XMLConverter.newDOM();
479 this.service_list = doc.createElement(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER);
480 Element service_rack_list_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.SERVICE_CLASS_ELEM + GSXML.LIST_MODIFIER);
481 configureServices(service_rack_list_elem);
482
483 // load up the service clusters
484 this.cluster_list = doc.createElement(GSXML.CLUSTER_ELEM + GSXML.LIST_MODIFIER);
485 Element cluster_list_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.CLUSTER_ELEM + GSXML.LIST_MODIFIER);
486 configureClusters(cluster_list_elem);
487
488 // load up the collections
489 this.collection_list = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
490 this.private_collection_list = doc.createElement(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER);
491 configureCollections();
492
493 // load up the external sites - this also adds their services/clusters/collections to the other lists - so must be done last
494 this.site_list = doc.createElement(GSXML.SITE_ELEM + GSXML.LIST_MODIFIER);
495 Element site_list_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.SITE_ELEM + GSXML.LIST_MODIFIER);
496 configureExternalSites(site_list_elem);
497
498 // load up the site metadata
499 this.metadata_list = doc.createElement(GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
500 Element metadata_list_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER);
501 loadMetadata(metadata_list_elem);
502
503 // load up the displayItems
504 this.display_item_list = doc.createElement(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER);
505 Element display_item_list_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER);
506 DisplayItemUtil.storeDisplayItems(this.display_item_list, display_item_list_elem);
507 Element format_elem = (Element) GSXML.getChildByTagName(config_info, GSXML.FORMAT_ELEM);
508 configureFormat(format_elem);
509 return true;
510
511 }
512
513 protected boolean configureServices(Element service_rack_list)
514 {
515
516 // load up the individual services
517 logger.info("loading service modules...");
518
519 if (service_rack_list == null)
520 {
521 logger.info("... none to be loaded");
522 return true;
523 }
524
525 NodeList service_racks = service_rack_list.getElementsByTagName(GSXML.SERVICE_CLASS_ELEM);
526 if (service_racks.getLength() == 0)
527 {
528 logger.info("... none to be loaded");
529 return true;
530 }
531 Document doc = XMLConverter.newDOM();
532 Element service_message = doc.createElement(GSXML.MESSAGE_ELEM);
533 Element service_request = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", new UserContext());
534 service_message.appendChild(service_request);
535
536 for (int i = 0; i < service_racks.getLength(); i++)
537 {
538 Element n = (Element) service_racks.item(i);
539 String service_name = n.getAttribute(GSXML.NAME_ATT);
540 logger.info("..." + service_name);
541
542 Class service_class = null;
543 try
544 {
545 service_class = Class.forName("org.greenstone.gsdl3.service." + service_name);
546 }
547 catch (ClassNotFoundException e)
548 {
549 try
550 {
551 //try the service_name alone in case the package name is already specified
552 service_class = Class.forName(service_name);
553 }
554 catch (ClassNotFoundException ae)
555 {
556 logger.error(ae.getMessage());
557 }
558 }
559 try
560 {
561 ServiceRack s = (ServiceRack) service_class.newInstance();
562 s.setSiteHome(this.site_home);
563 s.setSiteAddress(this.site_http_address);
564 s.setLibraryName(this.library_name);
565 s.setMessageRouter(this);
566 // pass the XML node to the service for service configuration
567 if (!s.configure(n, null))
568 {
569 logger.error("couldn't configure ServiceRack " + service_name);
570 continue;
571 }
572
573 s.addServiceParameters(this.params); // copyign recpt and actions.
574 // find out the supported services for this service module
575 Element service_response = (Element) s.process(service_message);
576 NodeList services = service_response.getElementsByTagName(GSXML.SERVICE_ELEM);
577 if (services.getLength() == 0)
578 {
579 logger.error("MessageRouter configure error: serviceRack " + service_name + " has no services!");
580 }
581 else
582 {
583 Document service_list_doc = this.service_list.getOwnerDocument();
584 for (int j = 0; j < services.getLength(); j++)
585 {
586 String service = ((Element) services.item(j)).getAttribute(GSXML.NAME_ATT);
587
588 this.module_map.put(service, s);
589
590 // add short info to service_list_ XML
591 this.service_list.appendChild(service_list_doc.importNode(services.item(j), true));
592 }
593 }
594 }
595 catch (Exception e)
596 {
597 logger.error("MessageRouter configure exception: in ServiceRack class specification: " + e.getMessage());
598 e.printStackTrace();
599 }
600 } // for each service module
601 return true;
602 }
603
604 protected boolean configureClusters(Element config_cluster_list)
605 {
606
607 // load up the service clusters
608 logger.info("loading service clusters ...");
609 if (config_cluster_list == null)
610 {
611 logger.info("... none to be loaded");
612 return true;
613 }
614 NodeList service_clusters = config_cluster_list.getElementsByTagName(GSXML.CLUSTER_ELEM);
615 if (service_clusters.getLength() == 0)
616 {
617 logger.info("... none to be loaded");
618 return true;
619 }
620
621 Document doc = this.cluster_list.getOwnerDocument();
622 for (int i = 0; i < service_clusters.getLength(); i++)
623 {
624 Element cluster = (Element) service_clusters.item(i);
625 String name = cluster.getAttribute(GSXML.NAME_ATT);
626 logger.info("..." + name);
627 ServiceCluster sc = new ServiceCluster();
628 sc.setSiteHome(this.site_home);
629 sc.setSiteAddress(this.site_http_address);
630 sc.setClusterName(name);
631 sc.setParams(this.params); // pass in params class so SC can add any service ones to it
632 sc.setMessageRouter(this);
633 if (!sc.configure(cluster))
634 {
635 logger.error("couldn't configure ServiceCluster " + name);
636 continue;
637 }
638
639 this.module_map.put(name, sc); // this replaces the old one if there was one already present
640 //add short info to cluster list
641 Element e = doc.createElement(GSXML.CLUSTER_ELEM);
642 e.setAttribute(GSXML.NAME_ATT, name);
643 this.cluster_list.appendChild(e);
644
645 }
646 return true;
647 }
648
649 // for now, only handle paramDefault elems
650 protected boolean configureFormat(Element format_elem) {
651 if (format_elem == null) {
652 return false;
653 }
654 NodeList param_defaults = format_elem.getElementsByTagName(GSXML.PARAM_DEFAULT_ELEM);
655 for (int i=0; i<param_defaults.getLength(); i++) {
656
657 Element p = (Element)param_defaults.item(i);
658 String name = p.getAttribute(GSXML.NAME_ATT);
659 String value = p.getAttribute(GSXML.VALUE_ATT);
660 this.params.setParamDefault(name, value);
661 }
662 return true;
663 }
664
665 /**
666 * looks through the collect directory and activates any collections it
667 * finds. If this is a reconfigure, clean up must be done first before
668 * calling this
669 */
670 protected boolean configureCollections()
671 {
672
673 // read thru the collect directory and activate all the valid collections
674 File collectDir = new File(GSFile.collectDir(this.site_home));
675 if (collectDir.exists())
676 {
677 logger.info("Reading thru directory " + collectDir.getPath() + " to find collections for activation.");
678 File[] contents = collectDir.listFiles();
679 for (int i = 0; i < contents.length; i++)
680 {
681 if (contents[i].isDirectory())
682 {
683
684 String colName = contents[i].getName();
685 if (!colName.startsWith("CVS") && !colName.startsWith(".svn"))
686 {
687 activateCollectionByName(colName);
688 }
689 }
690 }
691 } // collectDir
692 return true;
693 }
694
695 // testing whether a collection (or more generally, a module) is active
696 protected boolean pingModule(String name)
697 {
698 // module (including collection) would have been added to module_map
699 // if activated, and removed from map if deactivated.
700 // The this.collection_list Element would contain the collection too,
701 // but the following check seems to be more generally useful
702 return this.module_map.containsKey(name);
703 }
704
705 /**
706 * creates and configures a new collection if this is done for a
707 * reconfigure, the collection should be deactivated first.
708 *
709 * @param col_name
710 * the name of the collection
711 * @return true if collection created ok
712 */
713 protected boolean activateCollectionByName(String col_name)
714 {
715
716 logger.info("Activating collection: " + col_name + ".");
717 Document doc = this.collection_list.getOwnerDocument();
718 // Look for the etc/collectionInit.xml file, and see what sort of Collection to load
719 Collection c = null;
720 File init_file = new File(GSFile.collectionInitFile(this.site_home, col_name));
721
722 if (init_file.exists())
723 {
724 Document init_doc = XMLConverter.getDOM(init_file);
725 if (init_doc != null)
726 {
727 Element init_elem = init_doc.getDocumentElement();
728 if (init_elem != null)
729 {
730 String coll_class_name = init_elem.getAttribute("class");
731 if (!coll_class_name.equals(""))
732 {
733 try
734 {
735 c = (Collection) Class.forName("org.greenstone.gsdl3.collection." + coll_class_name).newInstance();
736 }
737 catch (Exception e)
738 {
739 logger.info(" couldn't create a new collection, type " + coll_class_name + ", defaulting to class Collection");
740 }
741 }
742 }
743 }
744 }
745 if (c == null)
746 { // we haven't found another classname to use
747 c = new Collection();
748 }
749
750 c.setCollectionName(col_name);
751 c.setSiteHome(this.site_home);
752 c.setSiteAddress(this.site_http_address);
753 c.setParams(this.params); // pass in params class so coll can add any service ones to it
754 c.setMessageRouter(this);
755 if (c.configure())
756 {
757 logger.info("have just configured collection " + col_name);
758 // add to list of collections
759 this.module_map.put(col_name, c);
760 Element e = doc.createElement(GSXML.COLLECTION_ELEM);
761 e.setAttribute(GSXML.NAME_ATT, col_name);
762
763 if (c.isPublic())
764 {
765 // only public collections will appear on the home page
766 // add short description_ to collection_list_
767 this.collection_list.appendChild(e);
768 }
769 else
770 {
771 this.private_collection_list.appendChild(e);
772 }
773 return true;
774 }
775 else
776 {
777 logger.error("Couldn't configure collection: " + col_name + ".");
778 return false;
779 }
780 }
781
782 /**
783 * Goes through the siteList and activates each site found. If this is done
784 * for a reconfigure, a clean up must be done first ****HOW???
785 */
786 protected boolean configureExternalSites(Element config_site_list)
787 {
788
789 // load up the sites
790 logger.info("loading external sites...");
791 if (config_site_list == null)
792 {
793 logger.info("...none found");
794 return true;
795 }
796
797 NodeList sites = config_site_list.getElementsByTagName(GSXML.SITE_ELEM);
798 if (sites.getLength() == 0)
799 {
800 logger.info("...none found");
801 return true;
802 }
803
804 // this is a name to identify the current site in the Communicator
805 String local_site_name = config_site_list.getAttribute(GSXML.LOCAL_SITE_NAME_ATT);
806 if (local_site_name.equals(""))
807 {
808 local_site_name = site_name;
809 }
810
811 for (int i = 0; i < sites.getLength(); i++)
812 {
813 Element s = (Element) sites.item(i);
814 activateSite(s, local_site_name);
815 }
816 return true;
817 }
818
819 protected boolean activateSiteByName(String site_name)
820 {
821 logger.info("Activating site: " + site_name + ".");
822
823 File configFile = new File(GSFile.siteConfigFile(this.site_home));
824
825 if (!configFile.exists())
826 {
827 logger.error(" site config file: " + configFile.getPath() + " not found!");
828 return false;
829 }
830 Document config_doc = XMLConverter.getDOM(configFile);
831 if (config_doc == null)
832 {
833 logger.error(" couldn't parse site config file: " + configFile.getPath());
834 return false;
835 }
836 Element config_elem = config_doc.getDocumentElement();
837
838 Element config_site_list = (Element) GSXML.getChildByTagName(config_elem, GSXML.SITE_ELEM + GSXML.LIST_MODIFIER);
839 if (config_site_list == null)
840 {
841 logger.error("activateSite, no sites found");
842 return false;
843 }
844 // this is a name to identify the current site in the Communicator
845 String local_site_name = config_site_list.getAttribute("localSiteName");
846 if (local_site_name.equals(""))
847 {
848 local_site_name = site_name;
849 }
850
851 Element this_site_elem = GSXML.getNamedElement(config_site_list, GSXML.SITE_ELEM, GSXML.NAME_ATT, site_name);
852 if (this_site_elem == null)
853 {
854 logger.error("activateSite, site " + site_name + " not found");
855 return false;
856 }
857
858 return activateSite(this_site_elem, local_site_name);
859 }
860
861 protected boolean activateSite(Element site_elem, String local_site_name)
862 {
863
864 Communicator comm = null;
865 String type = site_elem.getAttribute(GSXML.TYPE_ATT);
866 String name = site_elem.getAttribute(GSXML.NAME_ATT);
867 if (type.equals(GSXML.COMM_TYPE_SOAP_JAVA))
868 {
869 logger.info("activating SOAP site " + name);
870 comm = new SOAPCommunicator();
871 if (comm.configure(site_elem))
872 {
873 comm.setLocalSiteName(local_site_name);
874
875 // add to map of modules
876 this.module_map.put(name, comm);
877 this.site_list.appendChild(this.site_list.getOwnerDocument().importNode(site_elem, true));
878 // need to get collection list and service
879 // list from here- if the site isn't up yet, the site will
880 // have to be added later
881 if (!getRemoteSiteInfo(comm, name))
882 {
883 logger.error(" couldn't get info from site");
884 }
885 }
886 else
887 {
888 logger.error(" couldn't configure site");
889 return false;
890 }
891
892 }
893 else
894 {
895 logger.error(" cant talk to server of type:" + type + ", so not making a connection to " + name);
896 return false;
897 }
898 return true;
899 }
900
901 /** Goes through the metadataList and loads each metadatum found */
902 protected boolean loadMetadata(Element config_metadata_list)
903 {
904
905 // load up the sites
906 logger.info("loading site metadata...");
907 if (config_metadata_list == null)
908 {
909 logger.info("...none found");
910 return true;
911 }
912
913 NodeList metadata = config_metadata_list.getElementsByTagName(GSXML.METADATA_ELEM);
914 if (metadata.getLength() == 0)
915 {
916 logger.info("...none found");
917 return true;
918 }
919
920 Document doc = this.metadata_list.getOwnerDocument();
921 for (int i = 0; i < metadata.getLength(); i++)
922 {
923 Element s = (Element) metadata.item(i);
924 this.metadata_list.appendChild(doc.importNode(s, true));
925 }
926 return true;
927 }
928
929 /**
930 * get site info from external site
931 *
932 * @param comm
933 * - the communicator object for the external site
934 * @param site_name
935 * - the name of the external site
936 * @return true if successful
937 */
938 protected boolean getRemoteSiteInfo(Communicator comm, String site_name)
939 {
940
941 logger.info(" getting info from site:" + site_name);
942 Document doc = XMLConverter.newDOM();
943 Element info_request = doc.createElement(GSXML.MESSAGE_ELEM);
944 Element req = GSXML.createBasicRequest(doc, GSXML.REQUEST_TYPE_DESCRIBE, "", new UserContext());
945 info_request.appendChild(req);
946
947 // process the message
948 Node info_response_node = comm.process(info_request);
949 Element info_response = GSXML.nodeToElement(info_response_node);
950
951 if (info_response == null)
952 {
953 return false;
954 }
955 // collection info
956 NodeList colls = info_response.getElementsByTagName(GSXML.COLLECTION_ELEM);
957 if (colls.getLength() > 0)
958 {
959 for (int i = 0; i < colls.getLength(); i++)
960 {
961 Element e = (Element) colls.item(i);
962 String col_name = e.getAttribute(GSXML.NAME_ATT);
963 // add the info to own coll list - may want to keep
964 // this separate in future - so can distinguish own and
965 // other collections ??
966 e.setAttribute(GSXML.NAME_ATT, GSPath.prependLink(col_name, site_name));
967 this.collection_list.appendChild(doc.importNode(e, true));
968 }
969 }
970
971 // service info
972 NodeList services = info_response.getElementsByTagName(GSXML.SERVICE_ELEM);
973 if (services.getLength() > 0)
974 {
975 for (int i = 0; i < services.getLength(); i++)
976 {
977 Element e = (Element) services.item(i);
978 String serv_name = e.getAttribute(GSXML.NAME_ATT);
979 e.setAttribute(GSXML.NAME_ATT, GSPath.prependLink(serv_name, site_name));
980 this.service_list.appendChild(doc.importNode(e, true));
981 }
982 }
983
984 // serviceCluster info
985 NodeList clusters = info_response.getElementsByTagName(GSXML.CLUSTER_ELEM);
986 if (clusters.getLength() > 0)
987 {
988 for (int i = 0; i < clusters.getLength(); i++)
989 {
990 Element e = (Element) clusters.item(i);
991 String clus_name = e.getAttribute(GSXML.NAME_ATT);
992 e.setAttribute(GSXML.NAME_ATT, GSPath.prependLink(clus_name, site_name));
993 this.cluster_list.appendChild(doc.importNode(e, true));
994 }
995 }
996 return true;
997 }
998
999 protected boolean activateServiceClusterByName(String cluster_name)
1000 {
1001 return false;
1002
1003 }
1004
1005 protected boolean activateServiceRackByName(String module_name)
1006 {
1007 return false;
1008 }
1009
1010 protected boolean deactivateModule(String type, String name)
1011 {
1012
1013 logger.info("deactivating " + type + " module: " + name);
1014 if (this.module_map.containsKey(name))
1015 {
1016
1017 logger.info("found the module");
1018 ModuleInterface m = this.module_map.remove(name);
1019 // also remove the xml bit from description list
1020 if (type.equals(GSXML.COLLECTION_ELEM))
1021 {
1022 if (((Collection) m).isPublic())
1023 {
1024 Element this_col = GSXML.getNamedElement(this.collection_list, GSXML.COLLECTION_ELEM, GSXML.NAME_ATT, name);
1025 if (this_col != null)
1026 {
1027 this.collection_list.removeChild(this_col);
1028 }
1029 }
1030 else
1031 {
1032 // a private collection
1033 Element this_col = GSXML.getNamedElement(this.private_collection_list, GSXML.COLLECTION_ELEM, GSXML.NAME_ATT, name);
1034 if (this_col != null)
1035 {
1036 this.private_collection_list.removeChild(this_col);
1037 }
1038 }
1039 }
1040 else if (type.equals(GSXML.SERVICE_ELEM))
1041 {
1042 Element this_service = GSXML.getNamedElement(this.service_list, GSXML.SERVICE_ELEM, GSXML.NAME_ATT, name);
1043 if (this_service != null)
1044 {
1045 this.service_list.removeChild(this_service);
1046 }
1047 }
1048 else if (type.equals(GSXML.CLUSTER_ELEM))
1049 {
1050 Element this_cluster = GSXML.getNamedElement(this.cluster_list, GSXML.CLUSTER_ELEM, GSXML.NAME_ATT, name);
1051 if (this_cluster != null)
1052 {
1053 this.cluster_list.removeChild(this_cluster);
1054 }
1055 }
1056 else if (type.equals(GSXML.SITE_ELEM))
1057 {
1058 Element this_site = GSXML.getNamedElement(this.site_list, GSXML.SITE_ELEM, GSXML.NAME_ATT, name);
1059 if (this_site != null)
1060 {
1061 this.site_list.removeChild(this_site);
1062
1063 // also remove this sites colls, services, clusters etc
1064 cleanUpModuleMapSubset(this.collection_list, name);
1065 cleanUpModuleMapSubset(this.cluster_list, name);
1066 cleanUpModuleMapSubset(this.service_list, name);
1067
1068 // can remote collections be in the private coll list ??
1069 }
1070 }
1071 else
1072 {
1073 logger.error("invalid module type: " + type + ", can't remove info about this module");
1074 }
1075
1076 m.cleanUp(); // clean up any open files/connections etc - can cause trouble on windows
1077 m = null;
1078 return true;
1079 }
1080 // else not deactivated
1081 logger.error(name + " module not found");
1082 return false;
1083
1084 }
1085
1086 //*****************************************************************
1087 // auxiliary process methods
1088 //*****************************************************************
1089
1090 /**
1091 * handles requests made to the MessageRouter itself
1092 *
1093 * @param req
1094 * - the request Element- <request>
1095 * @return the result Element - should be <response>
1096 */
1097 protected Element processMessage(Element req)
1098 {
1099
1100 // message for self, should be type=describe/configure at this stage
1101 Document doc = XMLConverter.newDOM();
1102 String type = req.getAttribute(GSXML.TYPE_ATT);
1103 String lang = req.getAttribute(GSXML.LANG_ATT);
1104 Element response = doc.createElement(GSXML.RESPONSE_ELEM);
1105 response.setAttribute(GSXML.FROM_ATT, "");
1106 if (type.equals(GSXML.REQUEST_TYPE_DESCRIBE))
1107 {
1108 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_DESCRIBE);
1109 // check the param list
1110 Element param_list = (Element) GSXML.getChildByTagName(req, GSXML.PARAM_ELEM + GSXML.LIST_MODIFIER);
1111 if (param_list == null)
1112 {
1113 response.appendChild(doc.importNode(this.collection_list, true));
1114 response.appendChild(doc.importNode(this.cluster_list, true));
1115 response.appendChild(doc.importNode(this.site_list, true));
1116 response.appendChild(doc.importNode(this.service_list, true));
1117 response.appendChild(doc.importNode(this.metadata_list, true));
1118 Element di_list = doc.createElement(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER);
1119 response.appendChild(di_list);
1120 DisplayItemUtil.addLanguageSpecificDisplayItems(di_list, this.display_item_list, lang, DEFAULT_LANG, this.class_loader);
1121 return response;
1122 }
1123
1124 NodeList params = param_list.getElementsByTagName(GSXML.PARAM_ELEM);
1125
1126 // go through the param list and see what components are wanted
1127 for (int i = 0; i < params.getLength(); i++)
1128 {
1129
1130 Element param = (Element) params.item(i);
1131 // Identify the structure information desired
1132 if (param.getAttribute(GSXML.NAME_ATT).equals(GSXML.SUBSET_PARAM))
1133 {
1134 String info = param.getAttribute(GSXML.VALUE_ATT);
1135 if (info.equals(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER))
1136 {
1137 response.appendChild(doc.importNode(this.collection_list, true));
1138 }
1139 else if (info.equals(GSXML.CLUSTER_ELEM + GSXML.LIST_MODIFIER))
1140 {
1141 response.appendChild(doc.importNode(this.cluster_list, true));
1142 }
1143 else if (info.equals(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER))
1144 {
1145 response.appendChild(doc.importNode(this.service_list, true));
1146 }
1147 else if (info.equals(GSXML.SITE_ELEM + GSXML.LIST_MODIFIER))
1148 {
1149 response.appendChild(doc.importNode(this.site_list, true));
1150 }
1151 else if (info.equals(GSXML.METADATA_ELEM + GSXML.LIST_MODIFIER))
1152 {
1153 response.appendChild(doc.importNode(this.metadata_list, true));
1154 }
1155 else if (info.equals(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER))
1156 {
1157 Element di_list = doc.createElement(GSXML.DISPLAY_TEXT_ELEM + GSXML.LIST_MODIFIER);
1158 response.appendChild(di_list);
1159 DisplayItemUtil.addLanguageSpecificDisplayItems(di_list, this.display_item_list, lang, DEFAULT_LANG, this.class_loader);
1160
1161 }
1162 }
1163 }
1164 return response;
1165
1166 }
1167
1168 if (type.equals(GSXML.REQUEST_TYPE_SYSTEM))
1169 {
1170
1171 // a list of system requests - should put any error messages
1172 // or success messages into response
1173 NodeList commands = req.getElementsByTagName(GSXML.SYSTEM_ELEM);
1174 Element site_config_elem = null;
1175 boolean success = false;
1176
1177 for (int i = 0; i < commands.getLength(); i++)
1178 {
1179 // all the commands should be Elements
1180 Element elem = (Element) commands.item(i);
1181 String action = elem.getAttribute(GSXML.TYPE_ATT);
1182
1183 if (action.equals(GSXML.SYSTEM_TYPE_PING))
1184 { // ?a=s&sa=ping or ?a=s&sa=ping(&st=collection)&sn=colname
1185
1186 String message = ""; // will be creating the same messages as in GS2's recept/pingaction.cpp
1187 String module_name = elem.getAttribute(GSXML.SYSTEM_MODULE_NAME_ATT);
1188 String module_type = elem.getAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT);
1189
1190 if (module_name.equals(""))
1191 { // server-level ping
1192 message = "Ping succeeded.";
1193 }
1194 else
1195 { // ping at collection level
1196 if (pingModule(module_name))
1197 {
1198 message = "Ping for " + module_name + " succeeded.";
1199 }
1200 else
1201 {
1202 message = "Ping for " + module_name + " did not succeed.";
1203 }
1204 }
1205 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, message);
1206 response.appendChild(s);
1207 }
1208 //else if (action.equals(GSXML.SYSTEM_TYPE_ISPERSISTENT)) {
1209 // Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, "Persistent: true.");
1210 // response.appendChild(s);
1211 //}
1212 else if (action.equals(GSXML.SYSTEM_TYPE_CONFIGURE))
1213 {
1214 String subset = elem.getAttribute(GSXML.SYSTEM_SUBSET_ATT);
1215 if (subset.equals(""))
1216 {
1217 // need to reconfigure the MR
1218 this.configureLocalSite();
1219 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, "MessageRouter reconfigured successfully");
1220 response.appendChild(s);
1221
1222 }
1223 else
1224 {
1225 // else it a specific request
1226 if (subset.equals(GSXML.COLLECTION_ELEM + GSXML.LIST_MODIFIER))
1227 {
1228 // get rid of all the old collection stuff (not counting remote ones) before activating all the new ones
1229 cleanUpModuleMapSubset(this.collection_list, null);
1230 cleanUpModuleMapSubset(this.private_collection_list, null);
1231 success = configureCollections();
1232 }
1233 else
1234 {
1235
1236 // need the site config file
1237 if (site_config_elem == null)
1238 {
1239
1240 File configFile = new File(GSFile.siteConfigFile(this.site_home));
1241 if (!configFile.exists())
1242 {
1243 logger.error(" site config file: " + configFile.getPath() + " not found!");
1244 continue;
1245 }
1246 Document site_config_doc = XMLConverter.getDOM(configFile);
1247 if (site_config_doc == null)
1248 {
1249 logger.error(" couldn't parse site config file: " + configFile.getPath());
1250 continue;
1251 }
1252 site_config_elem = site_config_doc.getDocumentElement();
1253 }
1254 if (subset.equals(GSXML.SERVICE_ELEM + GSXML.LIST_MODIFIER))
1255 {
1256 Element service_rack_list = (Element) GSXML.getChildByTagName(site_config_elem, GSXML.SERVICE_CLASS_ELEM + GSXML.LIST_MODIFIER);
1257 cleanUpModuleMapSubset(this.service_list, null);
1258 success = configureServices(service_rack_list);
1259 }
1260 else if (subset.equals(GSXML.CLUSTER_ELEM + GSXML.LIST_MODIFIER))
1261 {
1262 Element cluster_list = (Element) GSXML.getChildByTagName(site_config_elem, GSXML.CLUSTER_ELEM + GSXML.LIST_MODIFIER);
1263 cleanUpModuleMapSubset(this.cluster_list, null);
1264 success = configureClusters(cluster_list);
1265 }
1266 else if (subset.equals(GSXML.SITE_ELEM + GSXML.LIST_MODIFIER))
1267 {
1268 Element site_list = (Element) GSXML.getChildByTagName(site_config_elem, GSXML.SITE_ELEM + GSXML.LIST_MODIFIER);
1269 cleanUpAllExternalSiteInfo();
1270 success = configureExternalSites(site_list);
1271 }
1272 }
1273 String message = null;
1274 if (success)
1275 {
1276 message = subset + "reconfigured successfully";
1277 }
1278 else
1279 {
1280 message = "Error in reconfiguring " + subset;
1281 }
1282 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, message);
1283 response.appendChild(s);
1284 }
1285
1286 }
1287 else
1288 {
1289 String module_name = elem.getAttribute(GSXML.SYSTEM_MODULE_NAME_ATT);
1290 String module_type = elem.getAttribute(GSXML.SYSTEM_MODULE_TYPE_ATT);
1291
1292 if (action.equals(GSXML.SYSTEM_TYPE_DEACTIVATE))
1293 {
1294 success = deactivateModule(module_type, module_name);
1295 if (success)
1296 {
1297 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, module_type + ": " + module_name + " deactivated", GSXML.SYSTEM_TYPE_DEACTIVATE, GSXML.SUCCESS);
1298 response.appendChild(s);
1299 }
1300 else
1301 {
1302 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, module_type + ": " + module_name + " could not be deactivated", GSXML.SYSTEM_TYPE_DEACTIVATE, GSXML.ERROR);
1303 response.appendChild(s);
1304 }
1305
1306 }
1307 else if (action.equals(GSXML.SYSTEM_TYPE_ACTIVATE))
1308 {
1309 // we need to deactivate the module first, in case this is a
1310 // reconfigure
1311 deactivateModule(module_type, module_name);
1312 if (module_type.equals(GSXML.COLLECTION_ELEM))
1313 {
1314 success = activateCollectionByName(module_name);
1315 }
1316 else if (module_type.equals(GSXML.SITE_ELEM))
1317 {
1318 success = activateSiteByName(module_name);
1319 }
1320 else if (module_type.equals(GSXML.CLUSTER_ELEM))
1321 {
1322 success = activateServiceClusterByName(module_name);
1323 }
1324 if (success)
1325 {
1326 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, module_type + ": " + module_name + " activated", GSXML.SYSTEM_TYPE_ACTIVATE, GSXML.SUCCESS);
1327 response.appendChild(s);
1328 }
1329 else
1330 {
1331 Element s = GSXML.createTextElement(doc, GSXML.STATUS_ELEM, module_type + ": " + module_name + " could not be activated", GSXML.SYSTEM_TYPE_ACTIVATE, GSXML.ERROR);
1332 response.appendChild(s);
1333 }
1334 }
1335 } // else not a configure action
1336 } // for all commands
1337 return response;
1338
1339 } // system type request
1340
1341 // if get here something has gone wrong
1342 String eol = System.getProperty("line.separator");
1343
1344 String mess = "Can't process request:" + eol + " " + XMLConverter.getString(req);
1345 logger.error(mess);
1346 return null;
1347
1348 }
1349
1350 //* Used to copy nodes from one message to another. E.g. copy a response node to the next request. Not sure if this is actually used anywhere yet... */
1351
1352 protected Element modifyMessages(Element request, Element message, Element result)
1353 {
1354 Document doc = XMLConverter.newDOM();
1355 Element response = doc.createElement(GSXML.RESPONSE_ELEM);
1356 response.setAttribute(GSXML.FROM_ATT, "");
1357 response.setAttribute(GSXML.TYPE_ATT, GSXML.REQUEST_TYPE_MESSAGING);
1358
1359 NodeList commands = request.getElementsByTagName("command");
1360 if (commands == null)
1361 {
1362 logger.error("no commands, " + XMLConverter.getPrettyString(request));
1363 return response;
1364 }
1365 for (int i = 0; i < commands.getLength(); i++)
1366 {
1367 Element action = (Element) commands.item(i);
1368 String type = action.getAttribute(GSXML.TYPE_ATT);
1369 if (type.equals("copyNode"))
1370 {
1371 // copies the from node as a child of to node
1372 String from_path = action.getAttribute("from");
1373 String to_path = action.getAttribute("to");
1374 Element from_node = null;
1375 String from_node_root = GSPath.getFirstLink(from_path);
1376 if (from_node_root.startsWith(GSXML.REQUEST_ELEM))
1377 {
1378 from_node = message;
1379 }
1380 else if (from_node_root.startsWith(GSXML.RESPONSE_ELEM))
1381 {
1382 from_node = result;
1383 }
1384 if (from_node == null)
1385 {
1386 continue;
1387 }
1388 Element to_node = null;
1389 String to_node_root = GSPath.getFirstLink(to_path);
1390 if (to_node_root.startsWith(GSXML.REQUEST_ELEM))
1391 {
1392 to_node = message;
1393 }
1394 else if (to_node_root.startsWith(GSXML.RESPONSE_ELEM))
1395 {
1396 to_node = result;
1397 }
1398 if (to_node == null)
1399 {
1400 continue;
1401 }
1402 // now we know what node to copy where
1403 Node orig_node = GSXML.getNodeByPathIndexed(from_node, from_path);
1404 if (orig_node == null)
1405 {
1406 continue;
1407 }
1408 Node new_parent = GSXML.getNodeByPathIndexed(to_node, to_path);
1409 if (new_parent == null)
1410 {
1411 continue;
1412
1413 }
1414 new_parent.appendChild(to_node.getOwnerDocument().importNode(orig_node, true));
1415 }
1416
1417 else if (type.equals("copyChildren"))
1418 {
1419
1420 }
1421 } // for each command
1422 return response;
1423 }
1424
1425 // ****************************************************
1426 // other methods
1427 // ****************************************************
1428
1429}
Note: See TracBrowser for help on using the repository browser.