1 | // jademx - JADE management using JMX |
2 | // Copyright 2005-2006 Caboodle Networks, Inc. |
3 | // |
4 | // This library is free software; you can redistribute it and/or |
5 | // modify it under the terms of the GNU Lesser General Public |
6 | // License as published by the Free Software Foundation; either |
7 | // version 2.1 of the License, or (at your option) any later version. |
8 | // |
9 | // This library is distributed in the hope that it will be useful, |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | // Lesser General Public License for more details. |
13 | // |
14 | // You should have received a copy of the GNU Lesser General Public |
15 | // License along with this library; if not, write to the Free Software |
16 | // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA |
17 | |
18 | package jade.jademx.mbean; |
19 | |
20 | import jade.BootProfileImpl; |
21 | import jade.core.Profile; |
22 | import jade.core.Runtime; |
23 | import jade.jademx.config.jademx.onto.ConfigAgentSpecifier; |
24 | import jade.jademx.config.jademx.onto.ConfigArgument; |
25 | import jade.jademx.config.jademx.onto.ConfigPlatform; |
26 | import jade.jademx.config.jademx.onto.ConfigRuntime; |
27 | import jade.jademx.config.jademx.onto.JademxConfig; |
28 | import jade.util.Logger; |
29 | import jade.util.leap.List; |
30 | import jade.wrapper.PlatformController; |
31 | |
32 | import java.lang.reflect.Constructor; |
33 | import java.net.MalformedURLException; |
34 | import java.net.URL; |
35 | import java.util.HashMap; |
36 | import java.util.Iterator; |
37 | import java.util.MissingResourceException; |
38 | import java.util.ResourceBundle; |
39 | |
40 | import javax.management.ObjectName; |
41 | import javax.xml.XMLConstants; |
42 | import javax.xml.parsers.DocumentBuilder; |
43 | import javax.xml.parsers.DocumentBuilderFactory; |
44 | import javax.xml.parsers.ParserConfigurationException; |
45 | import javax.xml.validation.Schema; |
46 | import javax.xml.validation.SchemaFactory; |
47 | |
48 | import org.w3c.dom.Document; |
49 | import org.w3c.dom.Element; |
50 | import org.w3c.dom.Node; |
51 | import org.w3c.dom.NodeList; |
52 | import org.xml.sax.SAXException; |
53 | |
54 | /** |
55 | * MBean implementation wrapper for JADE Runtime. |
56 | * TODO: some more work so can get line numbers even though using DOM XML. |
57 | * @author David Bernstein, <a href="http://www.caboodlenetworks.com" |
58 | * >Caboodle Networks, Inc.</a> |
59 | */ |
60 | public class JadeRuntime |
61 | extends JadeBase |
62 | implements JadeRuntimeMBean, JadePlatformMonitor { |
63 | |
64 | ///** the JADE runtime */ |
65 | //private Runtime runtime; |
66 | /** JADE copyright notice */ |
67 | private static String copyrightNotice = Runtime.getCopyrightNotice(); |
68 | /** JADE version info */ |
69 | private static String versionInfo = Runtime.instance().getVersionInfo(); |
70 | /** JADE version */ |
71 | private static String version; |
72 | /** JADE version timestamp */ |
73 | private static String versionTimestamp; |
74 | /** jademx version */ |
75 | private static String jademxVersion = null; |
76 | /** jademx version timestamp */ |
77 | private static String jademxTimestamp = null; |
78 | /** jademx resource bundle name */ |
79 | private static final String JADEMX_BUNDLE_NAME = |
80 | "jade.jademx.config.jademx"; |
81 | /** name of jademx major version property */ |
82 | private static final String JADEMX_MAJOR_VERSION_PROP = |
83 | "jademx.major.version"; |
84 | /** name of jademx minor version property */ |
85 | private static final String JADEMX_MINOR_VERSION_PROP = |
86 | "jademx.minor.version"; |
87 | /** name of jademx patch version property */ |
88 | private static final String JADEMX_PATCH_VERSION_PROP = |
89 | "jademx.patch.version"; |
90 | /** name of jademx cvs date property */ |
91 | private static final String JADEMX_CVS_DATE_PROP = |
92 | "jademx.cvs.date"; |
93 | /** my logger */ |
94 | private final Logger logger = |
95 | Logger.getMyLogger(JadeRuntime.class.getName()); |
96 | |
97 | /** thread group to put created threads in */ |
98 | private ThreadGroup threadGroup = null; |
99 | /** active state of platform currently being started */ |
100 | private volatile boolean platformActive = false; |
101 | /** milliseconds to wait for thread initialization (+ns) */ |
102 | private long INIT_SLEEP_MS = 50; |
103 | /** nanoseconds to wait for thread initialization (+ms) */ |
104 | private int INIT_SLEEP_NS = 0; |
105 | ///** prefix for platform name */ |
106 | //private final static String PLATFORM_MBEAN_NAME_PREFIX = "platform"; |
107 | |
108 | /** signature for operations that take one string argument */ |
109 | public final static String SIGN_STR[] = |
110 | new String[] { String.class.getName() }; |
111 | |
112 | /** has shutdown been called at least once */ |
113 | private Boolean shutdownInvoked = Boolean.FALSE; |
114 | |
115 | |
116 | /** |
117 | * runtime implementation wrapper for JADE runtime. |
118 | * @param jadeFactory factory this MBean related to |
119 | * @param runtime JADE runtime this MBean proxies |
120 | */ |
121 | public JadeRuntime( JadeFactory jadeFactory, Runtime runtime ) { |
122 | super( jadeFactory, TYPE, jadeFactory.getRuntimeName() ); |
123 | //this.runtime = runtime; |
124 | |
125 | int dashIndex = versionInfo.indexOf('-'); |
126 | version = versionInfo.substring( 0, dashIndex-1 ); |
127 | version = version.trim(); |
128 | version = version.replaceAll("JADE",""); |
129 | //System.out.println("version:"+version); |
130 | versionTimestamp = |
131 | cvsDateToTimestamp( versionInfo.substring( dashIndex+1 ) ); |
132 | //System.out.println("versionTimestamp:"+versionTimestamp); |
133 | |
134 | ResourceBundle b = ResourceBundle.getBundle(JADEMX_BUNDLE_NAME); |
135 | try { |
136 | String major = b.getString( JADEMX_MAJOR_VERSION_PROP ); |
137 | String minor = b.getString( JADEMX_MINOR_VERSION_PROP ); |
138 | String patch = b.getString( JADEMX_PATCH_VERSION_PROP ); |
139 | jademxVersion = major + "." + minor + "." + patch; |
140 | } |
141 | catch ( MissingResourceException mre ) { |
142 | jademxVersion = "?"; |
143 | } |
144 | String cvsDate = null; |
145 | try { |
146 | cvsDate = b.getString( JADEMX_CVS_DATE_PROP ); |
147 | } |
148 | catch ( MissingResourceException mre ) { |
149 | cvsDate = "?"; |
150 | } |
151 | int colonPos = cvsDate.indexOf(':'); |
152 | int dollarPos = cvsDate.lastIndexOf('$'); |
153 | String date = cvsDate.substring(colonPos + 1, dollarPos); |
154 | jademxTimestamp = cvsDateToTimestamp( date ); |
155 | |
156 | logger.log( Logger.FINE, "created jademx JadeRuntime" ); |
157 | logger.info( "jademx "+jademxVersion+ |
158 | ", LGPL license, http://jademx.sourceforge.net/"); |
159 | } |
160 | |
161 | /** |
162 | * convert cvs-style date/time string to iso 8601 timestamp |
163 | * @param cvsDate date to convert |
164 | * @return equivalent iso8601 timestamp |
165 | */ |
166 | private String cvsDateToTimestamp( String cvsDate ) { |
167 | String timestamp = cvsDate.trim(); |
168 | timestamp = timestamp.replace('/','-'); |
169 | timestamp = timestamp.replace(' ','T'); |
170 | return timestamp; |
171 | } |
172 | |
173 | /* (non-Javadoc) |
174 | * @see jade.management.mbean.JadeRuntimeMBean#getCopyrightNotice() |
175 | */ |
176 | public String getCopyrightNotice() { |
177 | return copyrightNotice; |
178 | } |
179 | |
180 | /* (non-Javadoc) |
181 | * @see jade.management.mbean.JadeRuntimeMBean#getCopyrightNotice() |
182 | */ |
183 | public String getVersionInfo() { |
184 | return versionInfo; |
185 | } |
186 | /* (non-Javadoc) |
187 | * @see jade.jademx.mbean.JadeRuntimeMBean#getVersion() |
188 | */ |
189 | public String getVersion() { |
190 | return version; |
191 | } |
192 | /* (non-Javadoc) |
193 | * @see jade.jademx.mbean.JadeRuntimeMBean#getVersionTimestamp() |
194 | */ |
195 | public String getVersionTimestamp() { |
196 | return versionTimestamp; |
197 | } |
198 | /* (non-Javadoc) |
199 | * @see jade.jademx.mbean.JadeRuntimeMBean#getVersion() |
200 | */ |
201 | public String getJademxVersion() { |
202 | return jademxVersion; |
203 | } |
204 | /* (non-Javadoc) |
205 | * @see jade.jademx.mbean.JadeRuntimeMBean#getJademxTimestamp() |
206 | */ |
207 | public String getJademxTimestamp() { |
208 | return jademxTimestamp; |
209 | } |
210 | |
211 | |
212 | /** |
213 | * set of platforms managed by this runtime. |
214 | * map is: JADE platform name => JadePlatform |
215 | */ |
216 | private HashMap platformMap = new HashMap(); |
217 | /** |
218 | * add a platform to set managed by runtime |
219 | * @param jadePlatform platform to add |
220 | */ |
221 | void addPlatform( JadePlatform jadePlatform ) { |
222 | synchronized ( platformMap ) { |
223 | platformMap.put( jadePlatform.getPlatformController().getName(), |
224 | jadePlatform ); |
225 | } |
226 | } |
227 | /** |
228 | * remove a platform from set managed by runtime |
229 | * @param platformName JADE name of platform to remove |
230 | */ |
231 | void removePlatform( String platformName ) { |
232 | synchronized ( platformMap ) { |
233 | platformMap.remove( platformName ); |
234 | } |
235 | } |
236 | |
237 | // ATTRIBUTES DESCRIBING CHILD MBEANS |
238 | |
239 | |
240 | /* (non-Javadoc) |
241 | * @see jade.jademx.mbean.JadeRuntimeMBean#getPlatforms() |
242 | */ |
243 | public JadePlatform[] getPlatforms() { |
244 | JadePlatform platforms[]; |
245 | synchronized ( platformMap ) { |
246 | int agentCount = platformMap.size(); |
247 | platforms = new JadePlatform[agentCount]; |
248 | Iterator agentIter = platformMap.values().iterator(); |
249 | int agentIndex = 0; |
250 | while ( agentIter.hasNext() ) { |
251 | platforms[agentIndex++] = (JadePlatform)agentIter.next(); |
252 | } |
253 | } |
254 | return platforms; |
255 | } |
256 | |
257 | |
258 | /* (non-Javadoc) |
259 | * @see jade.jademx.mbean.JadeRuntimeMBean#getPlatformNames() |
260 | */ |
261 | public String[] getPlatformNames() { |
262 | String platforms[]; |
263 | synchronized ( platformMap ) { |
264 | int agentCount = platformMap.size(); |
265 | platforms = new String[agentCount]; |
266 | Iterator agentIter = platformMap.values().iterator(); |
267 | int agentIndex = 0; |
268 | while ( agentIter.hasNext() ) { |
269 | platforms[agentIndex++] = agentIter.next().toString(); |
270 | } |
271 | } |
272 | return platforms; |
273 | } |
274 | |
275 | |
276 | /* (non-Javadoc) |
277 | * @see jade.jademx.mbean.JadeRuntimeMBean#getPlatformObjectNames() |
278 | */ |
279 | public ObjectName[] getPlatformObjectNames() { |
280 | ObjectName platforms[]; |
281 | synchronized ( platformMap ) { |
282 | int agentCount = platformMap.size(); |
283 | platforms = new ObjectName[agentCount]; |
284 | Iterator agentIter = platformMap.values().iterator(); |
285 | int agentIndex = 0; |
286 | while ( agentIter.hasNext() ) { |
287 | platforms[agentIndex++] = |
288 | ((JadePlatform)agentIter.next()).getObjectName(); |
289 | } |
290 | } |
291 | return platforms; |
292 | } |
293 | |
294 | |
295 | |
296 | /* (non-Javadoc) |
297 | * @see jade.jademx.mbean.JadeRuntimeMBean#shutdown() |
298 | */ |
299 | public void shutdown() throws JademxException { |
300 | logger.log( Logger.FINE, "entering JadeRuntime.shutdown()"); |
301 | |
302 | // make sure don't try to do this more than once! |
303 | synchronized ( shutdownInvoked ) { |
304 | if ( shutdownInvoked.booleanValue() ) { |
305 | throw new JademxException( |
306 | "JadeRuntime.shutdown() invoked multiple times"); |
307 | } |
308 | shutdownInvoked = Boolean.TRUE; |
309 | } |
310 | |
311 | // in this method, try to be as thorough as possible in cleaning up, |
312 | // so even if an exception is thrown, catch it and keep on going. |
313 | // the first exception caught is re-thrown, subsequent exceptions are |
314 | // logged |
315 | |
316 | JademxException je = null; // 1st exception received |
317 | JadePlatform jadePlatforms[] = null; |
318 | synchronized ( platformMap ) { |
319 | // copy platformMap out so don't have concurrent modification |
320 | // when they go away |
321 | jadePlatforms = new JadePlatform[platformMap.size()]; |
322 | logger.log( Logger.FINE, "JadeRuntime.shutdown():found "+jadePlatforms.length+" platforms..."); |
323 | Iterator platformIter = platformMap.values().iterator(); |
324 | int i = 0; |
325 | while ( platformIter.hasNext() ) { |
326 | jadePlatforms[i++]= (JadePlatform)platformIter.next(); |
327 | } |
328 | } |
329 | logger.log( Logger.FINE, "JadeRuntime.shutdown():about to loop thru "+jadePlatforms.length+" platforms..."); |
330 | for ( int i = 0; i < jadePlatforms.length; i++ ) { |
331 | JadePlatform platform = (JadePlatform)jadePlatforms[i]; |
332 | logger.log( Logger.FINE, "about to try to kill platform:"+platform); |
333 | try { |
334 | platform.kill(); |
335 | } |
336 | catch (JademxException e) { |
337 | if ( null == je ) { |
338 | je = e; // only keep 1st |
339 | } |
340 | else { |
341 | logger.log( Logger.SEVERE, |
342 | "non-first platform kill exception not thrown but logged", |
343 | e ); |
344 | } |
345 | } |
346 | // platform.kill tries to remove this, but in case it didn't |
347 | // get that far, this is an ok 2nd attempt to get rid of it |
348 | removePlatform( platform.getPlatformName() ); |
349 | } |
350 | logger.log( Logger.FINE, "about to unregister "+this); |
351 | try { |
352 | unregister(); |
353 | } |
354 | catch ( Exception e ) { |
355 | logger.log( Logger.SEVERE, "JadeRuntime.shutdown(): "+ |
356 | "exception unregistering:"+e); |
357 | e.printStackTrace( System.out ); |
358 | if ( null == je ) { |
359 | je = new JademxException("exception unregistering "+this+":"+e, |
360 | e ); |
361 | } |
362 | else { |
363 | logger.log( Logger.SEVERE, |
364 | "non-first platform unregister exception not thrown but logged", |
365 | e ); |
366 | } |
367 | } |
368 | if ( null != je ) { |
369 | throw je; |
370 | } |
371 | } |
372 | |
373 | |
374 | /** |
375 | * read in jademx xml configuration document |
376 | * @param xml string description of how to get jademx XML configuration |
377 | * @param isResource true=>it's a resource name, false=>URL string |
378 | * @return jademx xml configuration document |
379 | * @throws JademxException problem reading document |
380 | */ |
381 | private Document readDocument(String xml, boolean isResource) |
382 | throws JademxException { |
383 | ClassLoader myClassLoader = this.getClass().getClassLoader(); |
384 | |
385 | // get config schema |
386 | SchemaFactory schemaFactory = |
387 | SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); |
388 | URL configSchemaURL = |
389 | myClassLoader.getResource( CONFIG_XML_SCHEMA_RESOURCE_NAME ); |
390 | Schema schema = null; |
391 | logger.log( Logger.CONFIG, |
392 | "jademx config schema url: " + configSchemaURL ); |
393 | |
394 | try { |
395 | schema = schemaFactory.newSchema( configSchemaURL ); |
396 | } |
397 | catch ( SAXException saxe ) { |
398 | // it's an unrecoverable internal or configuration error if |
399 | // can't parse the jademx configuration schema |
400 | throw new JademxException( |
401 | "exception creating jademx configuration schema from URL "+ |
402 | configSchemaURL,saxe); |
403 | } |
404 | |
405 | // get document builder |
406 | DocumentBuilderFactory documentBuilderFactory = |
407 | DocumentBuilderFactory.newInstance(); |
408 | |
409 | logger.log( Logger.FINER, |
410 | "documentBuilderFactory:"+documentBuilderFactory); |
411 | |
412 | documentBuilderFactory.setCoalescing( true ); |
413 | documentBuilderFactory.setExpandEntityReferences( true ); |
414 | documentBuilderFactory.setIgnoringComments( true ); |
415 | documentBuilderFactory.setIgnoringElementContentWhitespace( false); |
416 | documentBuilderFactory.setNamespaceAware( true ); |
417 | try { |
418 | documentBuilderFactory.setXIncludeAware( true ); |
419 | } |
420 | catch ( NoSuchMethodError nsme ) { |
421 | logger.log( Logger.CONFIG, |
422 | "(NoSuchMethodError)"+ |
423 | "XInclude not supported by DocumentBuilderFactory "+ |
424 | documentBuilderFactory ); |
425 | } |
426 | catch ( UnsupportedOperationException uoe ) { |
427 | logger.log( Logger.CONFIG, |
428 | "(UnsupportedOperationException)"+ |
429 | "XInclude not supported by DocumentBuilderFactory "+ |
430 | documentBuilderFactory ); |
431 | } |
432 | try { |
433 | documentBuilderFactory.setSchema( schema ); |
434 | } |
435 | catch ( NoSuchMethodError nsme ) { |
436 | logger.log( Logger.CONFIG, |
437 | "(NoSuchMethodError)"+ |
438 | "schema setting not supported by DocumentBuilderFactory "+ |
439 | documentBuilderFactory ); |
440 | } |
441 | catch ( UnsupportedOperationException uoe ) { |
442 | logger.log( Logger.CONFIG, |
443 | "(UnsupportedOperationException)"+ |
444 | "schema setting not supported by DocumentBuilderFactory "+ |
445 | documentBuilderFactory ); |
446 | } |
447 | DocumentBuilder documentBuilder = null; |
448 | try { |
449 | documentBuilder = documentBuilderFactory.newDocumentBuilder(); |
450 | } |
451 | catch ( ParserConfigurationException pcee ) { |
452 | // it's an unrecoverable internal or configuration error if |
453 | // can't create a document builder to parse configuration |
454 | throw new JademxException( |
455 | "exception creating jademx configuration document builder ", |
456 | pcee); |
457 | } |
458 | |
459 | // normalize spec to a URL |
460 | URL configURL; |
461 | try { |
462 | configURL = ( isResource |
463 | ? myClassLoader.getResource( xml ) |
464 | : new URL( xml ) ); |
465 | } |
466 | catch ( MalformedURLException murle ) { |
467 | throw new JademxException( "error creating URL for \""+xml+"\"", |
468 | murle); |
469 | } |
470 | logger.log( Logger.CONFIG, "jademx config url: " + configURL ); |
471 | if ( null == configURL ) { |
472 | throw new JademxException( "bad jademx config url", |
473 | new MalformedURLException("unable to get jademx config URL "+ |
474 | (isResource?"for resource ":"")+xml)); |
475 | } |
476 | // read config in |
477 | String configUrlString = configURL.toExternalForm(); |
478 | Document document; |
479 | try { |
480 | document = documentBuilder.parse( configUrlString ); |
481 | } |
482 | catch ( Exception e ) { |
483 | throw new JademxException("error parsing configuration \""+ |
484 | configUrlString+"\"", e); |
485 | } |
486 | return document; |
487 | } |
488 | |
489 | |
490 | /** |
491 | * make a boot profile representing platform options |
492 | * @param platform platform configuration object |
493 | * @param platformIndex place in platform list |
494 | */ |
495 | private Profile platformProfile( ConfigPlatform platform, int platformIndex ) { |
496 | |
497 | List optionList = platform.getOptions(); |
498 | int optionCount = 0; |
499 | if ( null != optionList ) { |
500 | optionCount = optionList.size(); |
501 | } |
502 | String[] options = new String[optionCount]; |
503 | if ( optionCount > 0 ) { |
504 | Iterator optionI = optionList.iterator(); |
505 | int i = 0; |
506 | while ( optionI.hasNext() ) { |
507 | options[i++] = (String)optionI.next(); |
508 | } |
509 | } |
510 | |
511 | // put options in jade object |
512 | BootProfileImpl profile = new BootProfileImpl( options ); |
513 | |
514 | return profile; |
515 | } |
516 | |
517 | /** |
518 | * convert XML platform element to jademx platform configuration |
519 | * @param platformElement platform element to convert |
520 | * @return platform configuration equivalent of platform element |
521 | */ |
522 | private ConfigPlatform platformElementToConfig( Element platformElement ) |
523 | throws JademxException { |
524 | ConfigPlatform platform = new ConfigPlatform(); |
525 | |
526 | // options |
527 | |
528 | // get platform options |
529 | NodeList optionsList = |
530 | platformElement.getElementsByTagName( OPTIONS_TAG ); |
531 | // when we can use assertions... |
532 | // assert 1 == optionsList.getLength() : optionsList.getLength(); |
533 | Element optionsElement = (Element)optionsList.item( 0 ); |
534 | NodeList optionList = null; |
535 | if ( null != optionsElement ) { |
536 | optionList = optionsElement.getElementsByTagName( OPTION_TAG ); |
537 | } |
538 | int optionCount = 0; |
539 | if ( null != optionList ) { |
540 | optionCount = optionList.getLength(); |
541 | } |
542 | for ( int i = 0; i < optionCount; i++ ) { |
543 | Element optionElement= (Element)optionList.item(i); |
544 | String option = getTextContent( optionElement ); |
545 | platform.addOption( option ); |
546 | } |
547 | |
548 | // agent specifiers |
549 | |
550 | // get agent specifiers element |
551 | NodeList agentSpecifiersList = |
552 | platformElement.getElementsByTagName( AGENT_SPECIFIERS_TAG ); |
553 | // when we can use assertions... |
554 | // assert 1 == optionsList.getLength() : optionsList.getLength(); |
555 | Element agentSpecifiersElement = null; |
556 | if ( null != agentSpecifiersList ) { |
557 | agentSpecifiersElement = (Element)agentSpecifiersList.item( 0 ); |
558 | } |
559 | NodeList agentSpecifierList = null; |
560 | if ( null != agentSpecifiersElement ) { |
561 | agentSpecifierList = |
562 | agentSpecifiersElement.getElementsByTagName( |
563 | AGENT_SPECIFIER_TAG ); |
564 | } |
565 | int agentCount = 0; |
566 | if ( null != agentSpecifierList ) { |
567 | agentCount = agentSpecifierList.getLength(); |
568 | } |
569 | // for each <agent-specifier> |
570 | for ( int i = 0; i < agentCount; i++ ) { |
571 | // fire up the agent |
572 | Element agentSpecifierElement = (Element)agentSpecifierList.item(i); |
573 | ConfigAgentSpecifier agentSpecifier = |
574 | agentSpecifierElementToConfig( agentSpecifierElement ); |
575 | platform.addAgentSpecifier( agentSpecifier ); |
576 | } |
577 | |
578 | |
579 | return platform; |
580 | } |
581 | |
582 | |
583 | /** |
584 | * provide DOM Level 3 functionality to get text even if DOM Level 2. |
585 | * @param node the node whose text content to get |
586 | * @return the collected text content for the element |
587 | */ |
588 | private String getTextContent( Node node ) { |
589 | String textContent = null; |
590 | if ( null != node ) { |
591 | StringBuffer sb = new StringBuffer(); |
592 | |
593 | // DOM Level 3 |
594 | //sb.append( element.getTextContent() ); |
595 | |
596 | // DOM Level 2 |
597 | node.normalize(); |
598 | |
599 | short nodeType = node.getNodeType(); |
600 | switch ( nodeType ) { |
601 | case Node.CDATA_SECTION_NODE: |
602 | case Node.TEXT_NODE: |
603 | logger.log( Logger.FINEST, |
604 | "CDATA or TEXT:"+node.getNodeValue()); |
605 | sb.append( node.getNodeValue() ); |
606 | break; |
607 | case Node.ENTITY_REFERENCE_NODE: |
608 | logger.log( Logger.WARNING, |
609 | "entity references not supported: " + |
610 | node.getNodeName()+" not expanded"); |
611 | break; |
612 | default: |
613 | logger.log( Logger.FINEST, "node type:"+nodeType); |
614 | |
615 | NodeList nodeList = node.getChildNodes(); |
616 | int nodeCount = nodeList.getLength(); |
617 | |
618 | for ( int i = 0; i < nodeCount; i++ ) { |
619 | Node n = nodeList.item( i ); |
620 | sb.append( getTextContent( n ) ); |
621 | } |
622 | } |
623 | |
624 | textContent = sb.toString(); |
625 | } |
626 | logger.log( Logger.FINER, "returning \""+textContent+"\""); |
627 | return textContent; |
628 | } |
629 | |
630 | /** |
631 | * a version of getTextContent that trims the returned string safely |
632 | * @param node the node whose text content to get |
633 | * @return the (trimmed if not null) text content for the element |
634 | */ |
635 | private String getTextContentTrim( Node node ) { |
636 | String textContent = getTextContent( node ); |
637 | if ( null != textContent) { |
638 | textContent = textContent.trim(); |
639 | } |
640 | return textContent; |
641 | } |
642 | |
643 | /* (non-Javadoc) |
644 | * @see jade.jademx.mbean.JadePlatformMonitor#setPlatformActive(boolean) |
645 | */ |
646 | public void setPlatformActive( boolean platformActive ) { |
647 | this.platformActive = platformActive; |
648 | } |
649 | |
650 | |
651 | /** |
652 | * make thread for this platform, start it, make its MBean and return it. |
653 | * @param profile profile to use to start platform |
654 | * @param platformIndex platform number |
655 | * @return platform MBean implementation |
656 | */ |
657 | private JadePlatform makeAndStartPlatform( |
658 | Profile profile, int platformIndex ) { |
659 | |
660 | //System.err.println("JadeRuntime.makeAndStartPlatform(): my thread:"+ |
661 | // Thread.currentThread()+",id:"+Thread.currentThread().getId()); |
662 | |
663 | // make Runnable for the thread |
664 | |
665 | JadeRunnable jadeRunnable = |
666 | new JadeRunnable( profile, this ); |
667 | String threadName = "JADE platform "+platformIndex; |
668 | Thread platformThread = |
669 | new Thread( threadGroup, jadeRunnable, threadName ); |
670 | |
671 | |
672 | //TODO: do we need to set context class loader..........? |
673 | //platformThread.setContextClassLoader( |
674 | // Thread.currentThread().getContextClassLoader()); |
675 | |
676 | // start the thread |
677 | |
678 | platformActive = false; |
679 | platformThread.start(); |
680 | |
681 | logger.log( Logger.FINER, "waiting for platform "+platformIndex+ |
682 | " thread to start..."); |
683 | while ( !platformActive ) { |
684 | try { |
685 | Thread.sleep( INIT_SLEEP_MS, INIT_SLEEP_NS ); |
686 | } |
687 | catch ( InterruptedException ie ) { |
688 | String msg = "interrupted while waiting for platform "+ |
689 | platformIndex+" to start"; |
690 | logger.log( Logger.SEVERE, msg, ie ); |
691 | // this is unrecoverable |
692 | throw new RuntimeException( msg, ie ); |
693 | } |
694 | } |
695 | logger.log( Logger.FINER, "platform "+platformIndex+" started"); |
696 | |
697 | // make JadePlatformMBean and return its canonical string name |
698 | |
699 | PlatformController platformController = |
700 | jadeRunnable.getPlatformController(); |
701 | |
702 | String platformName = "platform" + platformIndex; |
703 | JadePlatform jadePlatform = null; |
704 | try { |
705 | jadePlatform = |
706 | new JadePlatform( this, |
707 | platformName, platformController, platformThread ); |
708 | jadePlatform.setParentObjectName( getObjectName() ); |
709 | } |
710 | catch (Exception e) { |
711 | // unrecoverable |
712 | throw new RuntimeException("exception creating platform mbean", e); |
713 | } |
714 | if ( null == jadePlatform ) { |
715 | throw new RuntimeException("null JadePlatform"); |
716 | } |
717 | //System.err.println("JadeRuntime.makeAndStartPlatform(): "+ |
718 | //"platformThread:"+platformThread+",id:"+platformThread.getId()); |
719 | return jadePlatform; |
720 | } |
721 | |
722 | /** |
723 | * verify that an expected child tag found. |
724 | * If the XML library being used can't read a schema, then this can |
725 | * be helpful in debugging. |
726 | * TODO: get line number into error message with fancier DOM programming. |
727 | * @param text the text derived for the node |
728 | * @param parent name of the parent node |
729 | * @param child name of this node |
730 | * @throws JademxException on problem |
731 | */ |
732 | private void validateExpectedChildTag( |
733 | String text, String parent, String child ) |
734 | throws JademxException { |
735 | |
736 | if ( null == text ) { |
737 | throw new JademxException("<"+child+"> not found inside <"+ |
738 | parent +"> in jademx configuration"); |
739 | } |
740 | if ( text.length() == 0 ) { |
741 | throw new JademxException("empty <"+child+"> inside <"+ |
742 | parent+"> in jademx configuration"); |
743 | } |
744 | } |
745 | |
746 | /** array of one String class, for Class.getConstructor() */ |
747 | private static final Class oneStringArg[] = new Class[] {String.class}; |
748 | |
749 | /** |
750 | * start up specified agent |
751 | * @param jadePlatform platform mbean implementation |
752 | * @param agentSpecifier agent specifier |
753 | * @param platformIndex platform number |
754 | * @param agentIndex agent number with respect to platform |
755 | * @throws JademxException problem starting agent |
756 | */ |
757 | private void instantiateAgent( JadePlatform jadePlatform, |
758 | ConfigAgentSpecifier agentSpecifier, int platformIndex, int agentIndex ) |
759 | throws JademxException { |
760 | |
761 | logger.log( Logger.CONFIG, |
762 | "platform " + platformIndex +", agent " + agentIndex ); |
763 | |
764 | // get agent name |
765 | String agentName = agentSpecifier.getName(); |
766 | logger.log( Logger.CONFIG, |
767 | "agent name" + ":\"" + agentName +"\""); |
768 | |
769 | // get agent class name |
770 | String className = agentSpecifier.getClassname(); |
771 | logger.log( Logger.CONFIG, |
772 | "class name" + ":\"" + className +"\""); |
773 | |
774 | // get agent arguments |
775 | List argumentList = agentSpecifier.getArguments(); |
776 | int argumentCount = 0; |
777 | if ( null != argumentList ) { |
778 | argumentCount = argumentList.size(); |
779 | } |
780 | Object[] arguments = new Object[argumentCount]; |
781 | if ( argumentCount > 0 ) { |
782 | Iterator argumentI = argumentList.iterator(); |
783 | int argNum = 0; |
784 | while ( argumentI.hasNext() ) { |
785 | ConfigArgument argument = (ConfigArgument)argumentI.next(); |
786 | String argumentValue = argument.getValue(); |
787 | // permit intentional null argument |
788 | // if ( null == argumentValue ) { |
789 | // NullPointerException npe = new NullPointerException( |
790 | // "platform "+jadePlatform+", agent "+agentSpecifier+ |
791 | // ", argument value "+argNum+" is null"); |
792 | // throw new JademxException( "null argument value", npe ); |
793 | // } |
794 | String argumentClassName = argument.getClassname(); |
795 | Object thisArg; |
796 | if ( ( null != argumentClassName ) && |
797 | ( argumentClassName.trim().length() > 0 ) ) { |
798 | try { |
799 | logger.log( Logger.FINER, |
800 | "constructing non-String argument \""+ |
801 | argument+"\" of class \""+argumentClassName+"\""); |
802 | Constructor constructor = |
803 | Class.forName( argumentClassName ). |
804 | getConstructor( oneStringArg ); |
805 | thisArg = |
806 | constructor.newInstance( (Object[]) |
807 | new String[] { argumentValue } ); |
808 | } |
809 | catch ( Exception e) { |
810 | String msg = |
811 | "problem constructing argument "+argNum+" "+ |
812 | argumentClassName + "("+argument+") "+ |
813 | "starting agent "+agentName+ |
814 | " in platform "+platformIndex; |
815 | logger.log( Logger.SEVERE, msg, e ); |
816 | throw new JademxException( msg, e ); |
817 | } |
818 | } |
819 | else { |
820 | thisArg = argumentValue; |
821 | } |
822 | arguments[argNum++] = thisArg; |
823 | } |
824 | } |
825 | |
826 | jadePlatform.createNewAgent( agentName, className, arguments ); |
827 | |
828 | } |
829 | |
830 | |
831 | /** |
832 | * convert XML agent specifier element to agent specifier config object |
833 | * @param agentSpecifierElement agent specifier element in xml config |
834 | * @throws JademxException problem converting |
835 | */ |
836 | private ConfigAgentSpecifier agentSpecifierElementToConfig( |
837 | Element agentSpecifierElement ) throws JademxException { |
838 | |
839 | ConfigAgentSpecifier agentSpecifier = new ConfigAgentSpecifier(); |
840 | |
841 | // get agent name |
842 | NodeList agentNameList = |
843 | agentSpecifierElement.getElementsByTagName( AGENT_NAME_TAG ); |
844 | Element agentNameElement = (Element)agentNameList.item( 0 ); |
845 | String agentName = getTextContentTrim( agentNameElement ); |
846 | validateExpectedChildTag( agentName, |
847 | AGENT_SPECIFIER_TAG, AGENT_NAME_TAG ); |
848 | logger.log( Logger.CONFIG, |
849 | "agent name" + ":\"" + agentName +"\""); |
850 | agentSpecifier.setName( agentName ); |
851 | |
852 | // get agent class name |
853 | NodeList classNameList = |
854 | agentSpecifierElement.getElementsByTagName( CLASS_NAME_TAG ); |
855 | Element classNameElement = (Element)classNameList.item( 0 ); |
856 | String className = getTextContentTrim( classNameElement ); |
857 | validateExpectedChildTag( className, |
858 | AGENT_SPECIFIER_TAG, CLASS_NAME_TAG ); |
859 | logger.log( Logger.CONFIG, |
860 | "class name" + ":\"" + className +"\""); |
861 | agentSpecifier.setClassname( className ); |
862 | |
863 | // get agent arguments |
864 | NodeList argumentsList = |
865 | agentSpecifierElement.getElementsByTagName( ARGUMENTS_TAG ); |
866 | int argumentCount = 0; |
867 | NodeList argumentList = null; |
868 | if ( null != argumentsList) { |
869 | Element argumentsElement = (Element)argumentsList.item( 0 ); |
870 | if ( null != argumentsElement ) { |
871 | argumentList = |
872 | argumentsElement.getElementsByTagName( ARGUMENT_TAG ); |
873 | argumentCount = argumentList.getLength(); |
874 | } |
875 | } |
876 | for ( int i = 0; i < argumentCount; i++ ) { |
877 | Element argumentElement = (Element)argumentList.item( i ); |
878 | ConfigArgument argument = argumentElementToConfig( argumentElement ); |
879 | agentSpecifier.addArgument( argument ); |
880 | logger.log( Logger.FINE, "argument " + i + ":\"" +argument+"\""); |
881 | } |
882 | |
883 | |
884 | return agentSpecifier; |
885 | |
886 | } |
887 | |
888 | /** |
889 | * convert argument XML element to jademx configuration agent argument |
890 | * @param argumentElement |
891 | * @return argument configuration for XML argument element |
892 | */ |
893 | private ConfigArgument argumentElementToConfig( Element argumentElement ) { |
894 | ConfigArgument argument = new ConfigArgument(); |
895 | String argumentValue = getTextContent( argumentElement ); |
896 | argument.setValue( argumentValue ); |
897 | String argumentClassName = |
898 | argumentElement.getAttribute( CLASS_NAME_ATTR ); |
899 | if ( ( argumentClassName.trim().length() > 0 ) ) { |
900 | argument.setClassname( argumentClassName ); |
901 | } |
902 | return argument; |
903 | } |
904 | |
905 | |
906 | /** |
907 | * start up specified agents for JADE platform. |
908 | * @param jadePlatform platform mbean implementation |
909 | * @param platform platform config |
910 | * @param platformIndex platform number |
911 | */ |
912 | private void instantiateAgents( JadePlatform jadePlatform, |
913 | ConfigPlatform platform, int platformIndex ) throws JademxException { |
914 | |
915 | List agentSpecifiers = platform.getAgentSpecifiers(); |
916 | if ( null != agentSpecifiers ) { |
917 | Iterator agentSpecifierI = agentSpecifiers.iterator(); |
918 | int agentCount = 0; |
919 | while ( agentSpecifierI.hasNext() ) { |
920 | ConfigAgentSpecifier agentSpecifier = |
921 | (ConfigAgentSpecifier)agentSpecifierI.next(); |
922 | instantiateAgent( |
923 | jadePlatform, agentSpecifier, |
924 | platformIndex, agentCount++ ); |
925 | } |
926 | } |
927 | |
928 | } |
929 | |
930 | /** |
931 | * start up specified JADE platform. |
932 | * each platform gets its own thread. |
933 | * @param platform platform configuration |
934 | * @param platformIndex platform number |
935 | * @return platform MBean |
936 | */ |
937 | private JadePlatform instantiatePlatform( ConfigPlatform platform, |
938 | int platformIndex ) throws JademxException{ |
939 | |
940 | Profile bootProfile = |
941 | platformProfile( platform, platformIndex ); |
942 | |
943 | JadePlatform jadePlatform = |
944 | makeAndStartPlatform( bootProfile, platformIndex ); |
945 | |
946 | instantiateAgents( jadePlatform, platform, platformIndex ); |
947 | |
948 | return jadePlatform; |
949 | |
950 | } |
951 | |
952 | |
953 | /** |
954 | * instantiate platforms for given runtime configuration |
955 | * @param runtime runtime configuration |
956 | * @return platforms for runtime |
957 | * @throws JademxException on error |
958 | */ |
959 | JadePlatform[] instantiatePlatforms( ConfigRuntime runtime ) |
960 | throws JademxException { |
961 | logger.log( Logger.CONFIG, |
962 | "instantiating platforms from runtime configuration " + runtime ); |
963 | |
964 | threadGroup = new ThreadGroup( getJadeFactory().getThreadGroupName() ); |
965 | |
966 | List platforms = runtime.getPlatforms(); |
967 | int platformCount = 0; |
968 | if ( null != platforms ) { |
969 | platformCount = platforms.size(); |
970 | } |
971 | JadePlatform jadePlatforms[] = new JadePlatform[platformCount]; |
972 | Iterator platformI = platforms.iterator(); |
973 | int i = 0; |
974 | while ( platformI.hasNext() ) { |
975 | jadePlatforms[i] = |
976 | instantiatePlatform( (ConfigPlatform)platformI.next(), i ); |
977 | i++; |
978 | } |
979 | |
980 | // return array of platform object names |
981 | return jadePlatforms; |
982 | } |
983 | |
984 | |
985 | /** |
986 | * convert configuration XML to a configuration object |
987 | * @param xml name of XML to convert |
988 | * @param isResource whether name specifies a resource |
989 | * @return object representation of input xml |
990 | */ |
991 | private JademxConfig xmlToConfig( String xml, boolean isResource ) |
992 | throws JademxException { |
993 | |
994 | // read in doc |
995 | Document document = readDocument( xml, isResource ); |
996 | |
997 | // get <jademx-config> |
998 | NodeList jademxConfigNodeList = |
999 | document.getElementsByTagName( JADEMX_CONFIG_TAG ); |
1000 | int jademxConfigCount = 0; |
1001 | if ( null != jademxConfigNodeList ) { |
1002 | jademxConfigCount = jademxConfigNodeList.getLength(); |
1003 | } |
1004 | if ( 0 == jademxConfigCount ) { |
1005 | throw new JademxException("no <jademx-config> found in "+xml); |
1006 | } |
1007 | if ( jademxConfigCount > 1 ) { |
1008 | throw new JademxException("multiple <jademx-config> in "+xml); |
1009 | } |
1010 | Element jademxConfigElement = (Element)jademxConfigNodeList.item(0); |
1011 | JademxConfig jademxConfig = new JademxConfig(); |
1012 | |
1013 | // get <runtime> |
1014 | NodeList runtimeList = |
1015 | jademxConfigElement.getElementsByTagName( RUNTIME_TAG ); |
1016 | int runtimeCount; |
1017 | if ( null == runtimeList ) { |
1018 | runtimeCount = 0; |
1019 | } |
1020 | else { |
1021 | runtimeCount = jademxConfigNodeList.getLength(); |
1022 | } |
1023 | if ( runtimeCount > 1 ) { |
1024 | throw new JademxException("multiple <runtime> tags found in "+xml); |
1025 | } |
1026 | else if ( 1 == runtimeCount ) { |
1027 | Element runtimeElement = (Element)runtimeList.item( 0 ); |
1028 | ConfigRuntime runtime = new ConfigRuntime(); |
1029 | jademxConfig.addRuntime( runtime ); |
1030 | |
1031 | // get each <platform> |
1032 | NodeList platformNodeList = |
1033 | runtimeElement.getElementsByTagName( PLATFORM_TAG ); |
1034 | int platformCount; |
1035 | if ( null == platformNodeList ) { |
1036 | platformCount = 0; |
1037 | } |
1038 | else { |
1039 | platformCount = platformNodeList.getLength(); |
1040 | for ( int i = 0; i < platformCount; i++ ) { |
1041 | Element platformElement = (Element)platformNodeList.item(i); |
1042 | ConfigPlatform platform = platformElementToConfig( platformElement ); |
1043 | runtime.addPlatform( platform ); |
1044 | } |
1045 | } |
1046 | } |
1047 | return jademxConfig; |
1048 | } |
1049 | |
1050 | |
1051 | |
1052 | /** |
1053 | * instantiate platforms described by jademx configuration XML. |
1054 | * each platform gets its own thread. |
1055 | * @param xml string description of how to get jademx XML configuration |
1056 | * @param isResource true=>it's a resource name, false=>URL string |
1057 | * @return JadePlatform array proxying instantiated platforms |
1058 | * @throws JademxException problem instantiating platforms |
1059 | */ |
1060 | private JadePlatform[] instantiatePlatforms( String xml, boolean isResource ) |
1061 | throws JademxException { |
1062 | |
1063 | JadePlatform jadePlatforms[] = null; |
1064 | |
1065 | logger.log( Logger.CONFIG, |
1066 | "instantiating platforms from " + |
1067 | (isResource?"resource":"URL") + |
1068 | " \"" + xml + "\"" ); |
1069 | |
1070 | // get config xml doc |
1071 | // for each platform |
1072 | // start the platform |
1073 | // for each agent |
1074 | // start the agent |
1075 | // add platform mbean object name to return array |
1076 | // return array of platform object names |
1077 | |
1078 | threadGroup = new ThreadGroup( getJadeFactory().getThreadGroupName() ); |
1079 | JademxConfig jademxConfig = xmlToConfig( xml, isResource ); |
1080 | if ( null != jademxConfig ) { |
1081 | List runtimes = jademxConfig.getRuntimes(); |
1082 | if ( null != runtimes ) { |
1083 | if ( runtimes.size() > 1 ) { |
1084 | throw new JademxException("multiple <runtime> tags in "+xml); |
1085 | } |
1086 | Iterator runtimeI = runtimes.iterator(); |
1087 | if ( runtimeI.hasNext() ) { |
1088 | ConfigRuntime runtime = (ConfigRuntime)runtimeI.next(); |
1089 | jadePlatforms = instantiatePlatforms( runtime ); |
1090 | } |
1091 | } |
1092 | } |
1093 | |
1094 | // return array of platform objects |
1095 | return jadePlatforms; |
1096 | } |
1097 | |
1098 | /* (non-Javadoc) |
1099 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformsFromConfigResource( |
1100 | * java.lang.String) |
1101 | */ |
1102 | public JadePlatform[] platformsFromConfigResource( String xmlResName ) |
1103 | throws JademxException { |
1104 | return instantiatePlatforms( xmlResName, true ); |
1105 | } |
1106 | |
1107 | /* (non-Javadoc) |
1108 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformsFromConfigUrl( |
1109 | * java.lang.String) |
1110 | */ |
1111 | public JadePlatform[] platformsFromConfigUrl( String xmlUrlString ) |
1112 | throws JademxException { |
1113 | return instantiatePlatforms( xmlUrlString, false ); |
1114 | } |
1115 | |
1116 | /** |
1117 | * convert array of JadePlatform to array of String |
1118 | * @param platforms input to convert |
1119 | * @return string representation of input |
1120 | */ |
1121 | private String[] platformsToStrings( JadePlatform[] platforms ) { |
1122 | int platformCount = platforms.length; |
1123 | String strings[] = new String[platformCount]; |
1124 | for ( int i = 0; i < platformCount; i++ ) { |
1125 | strings[i] = platforms[i].toString(); |
1126 | } |
1127 | return strings; |
1128 | } |
1129 | |
1130 | /* (non-Javadoc) |
1131 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformsFromConfigResource( |
1132 | * java.lang.String) |
1133 | */ |
1134 | public String[] platformNamesFromConfigResource( String xmlResName ) |
1135 | throws JademxException { |
1136 | return platformsToStrings( |
1137 | instantiatePlatforms( xmlResName, true ) ); |
1138 | } |
1139 | |
1140 | /* (non-Javadoc) |
1141 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformsFromConfigUrl( |
1142 | * java.lang.String) |
1143 | */ |
1144 | public String[] platformNamesFromConfigUrl( String xmlUrlString ) |
1145 | throws JademxException { |
1146 | return platformsToStrings( |
1147 | instantiatePlatforms( xmlUrlString, false ) ); |
1148 | } |
1149 | |
1150 | |
1151 | /* (non-Javadoc) |
1152 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformFromJadeName( |
1153 | * java.lang.String) |
1154 | */ |
1155 | public JadePlatform platformFromJadeName( String jadePlatformName ) |
1156 | throws JademxException { |
1157 | JadePlatform jadePlatform; |
1158 | logger.log(Logger.FINER, |
1159 | "looking for platform \""+jadePlatformName+"\""); |
1160 | synchronized ( platformMap ) { |
1161 | jadePlatform = (JadePlatform)platformMap.get( jadePlatformName ); |
1162 | } |
1163 | return jadePlatform; |
1164 | } |
1165 | |
1166 | /* (non-Javadoc) |
1167 | * @see jade.jademx.mbean.JadeRuntimeMBean#platformNameFromJadeName( |
1168 | * java.lang.String) |
1169 | */ |
1170 | public String platformNameFromJadeName( String jadePlatformName ) |
1171 | throws JademxException { |
1172 | JadePlatform jadePlatform = platformFromJadeName( jadePlatformName ); |
1173 | String jademxPlatformName = ( ( null != jadePlatform ) |
1174 | ? jadePlatform.toString() |
1175 | : null ); |
1176 | return jademxPlatformName; |
1177 | } |
1178 | |
1179 | |
1180 | /* (non-Javadoc) |
1181 | * @see javax.management.MBeanRegistration#postRegister(java.lang.Boolean) |
1182 | */ |
1183 | public void postRegister(Boolean registrationDone) { |
1184 | super.postRegister(registrationDone); |
1185 | if ( registrationDone.booleanValue() ) { |
1186 | System.setProperty( MBEAN_SYS_PROP_NAME, getMBeanName() ); |
1187 | } |
1188 | } |
1189 | /* (non-Javadoc) |
1190 | * @see javax.management.MBeanRegistration#preDeregister() |
1191 | */ |
1192 | public void preDeregister() throws Exception { |
1193 | try { |
1194 | System.clearProperty( MBEAN_SYS_PROP_NAME ); |
1195 | } |
1196 | catch ( NoSuchMethodError nsme ) { |
1197 | // intentionally empty |
1198 | // method arrived with J2SE 1.5 |
1199 | } |
1200 | catch ( Throwable t ) { |
1201 | logger.log( Logger.FINE, "issue clearing system property "+ |
1202 | MBEAN_SYS_PROP_NAME+": "+t, t ); |
1203 | // message is just an FYI. |
1204 | // don't throw it, it was just trying to clean up nicely. |
1205 | // for example, there might be a security exception. |
1206 | } |
1207 | super.preDeregister(); |
1208 | } |
1209 | } |