1. /*
  2. * ====================================================================
  3. *
  4. * The Apache Software License, Version 1.1
  5. *
  6. * Copyright (c) 2001-2002 The Apache Software Foundation. All rights
  7. * reserved.
  8. *
  9. * Redistribution and use in source and binary forms, with or without
  10. * modification, are permitted provided that the following conditions
  11. * are met:
  12. *
  13. * 1. Redistributions of source code must retain the above copyright
  14. * notice, this list of conditions and the following disclaimer.
  15. *
  16. * 2. Redistributions in binary form must reproduce the above copyright
  17. * notice, this list of conditions and the following disclaimer in
  18. * the documentation and/or other materials provided with the
  19. * distribution.
  20. *
  21. * 3. The end-user documentation included with the redistribution, if
  22. * any, must include the following acknowlegement:
  23. * "This product includes software developed by the
  24. * Apache Software Foundation (http://www.apache.org/)."
  25. * Alternately, this acknowlegement may appear in the software itself,
  26. * if and wherever such third-party acknowlegements normally appear.
  27. *
  28. * 4. The names "The Jakarta Project", "Commons", and "Apache Software
  29. * Foundation" must not be used to endorse or promote products derived
  30. * from this software without prior written permission. For written
  31. * permission, please contact apache@apache.org.
  32. *
  33. * 5. Products derived from this software may not be called "Apache"
  34. * nor may "Apache" appear in their names without prior written
  35. * permission of the Apache Group.
  36. *
  37. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  38. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  39. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  40. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  41. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  42. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  43. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  44. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  45. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  46. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  47. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  48. * SUCH DAMAGE.
  49. * ====================================================================
  50. *
  51. * This software consists of voluntary contributions made by many
  52. * individuals on behalf of the Apache Software Foundation. For more
  53. * information on the Apache Software Foundation, please see
  54. * <http://www.apache.org/>.
  55. *
  56. * [Additional notices, if required by prior licensing conditions]
  57. *
  58. */
  59. package org.apache.commons.modeler;
  60. import java.util.Enumeration;
  61. import java.util.Hashtable;
  62. import javax.management.AttributeChangeNotification;
  63. import javax.management.InstanceNotFoundException;
  64. import javax.management.MBeanException;
  65. import javax.management.MBeanServer;
  66. import javax.management.MBeanServerNotification;
  67. import javax.management.Notification;
  68. import javax.management.NotificationBroadcaster;
  69. import javax.management.NotificationListener;
  70. import javax.management.ObjectName;
  71. import javax.naming.Context;
  72. import org.apache.commons.logging.Log;
  73. import org.apache.commons.logging.LogFactory;
  74. // EXPERIMENTAL. It may fit better in tomcat jndi impl.
  75. /**
  76. *
  77. * Link between JNDI and JMX. JNDI can be used for persistence ( it is
  78. * an API for storing hierarchical data and a perfect fit for that ), as
  79. * well as an alternate view of the MBean registry.
  80. *
  81. * If this component is enabled, all MBeans will be registered in JNDI, and
  82. * all attributes that are set via JMX can be stored in a DirContext.
  83. *
  84. * This acts as a "recorder" for creation of mbeans and attribute changes
  85. * done via JMX.
  86. *
  87. * XXX How can we control ( filter ) which mbeans will be registere ? Or
  88. * attributes ?
  89. * XXX How can we get the beans and attributes loaded before jndijmx ?
  90. *
  91. * The intended use:
  92. * - do whatever you want to start the application
  93. * - load JndiJmx as an mbean
  94. * - make changes via JMX. All changes are recorded
  95. * - you can use JndiJmx to save the changes in a Jndi context.
  96. * - you can use JndiJmx to load changes from a JndiContext and replay them.
  97. *
  98. * The main benefit is that only changed attributes are saved, and the Jndi
  99. * layer can preserve most of the original structure of the config file. The
  100. * alternative is to override the config files with config info extracted
  101. * from the live objects - but it's very hard to save only what was actually
  102. * changed and preserve structure and comments.
  103. *
  104. * @author Costin Manolache
  105. */
  106. public class JndiJmx extends BaseModelMBean implements NotificationListener {
  107. private static Log log= LogFactory.getLog(JndiJmx.class);
  108. protected Context componentContext;
  109. protected Context descriptorContext;
  110. protected Context configContext;
  111. MBeanServer mserver;
  112. /**
  113. * Protected constructor to require use of the factory create method.
  114. */
  115. public JndiJmx() throws MBeanException {
  116. super(JndiJmx.class.getName());
  117. }
  118. /** If a JNDI context is set, all components
  119. * will be registered in the context.
  120. *
  121. * @param ctx
  122. */
  123. public void setComponentContext(Context ctx) {
  124. this.componentContext= ctx;
  125. }
  126. /** JNDI context for component descriptors ( metadata ).
  127. *
  128. * @param ctx
  129. */
  130. public void setDescriptorContext(Context ctx) {
  131. this.descriptorContext= ctx;
  132. }
  133. /** JNDI context where attributes will be stored for persistence
  134. *
  135. */
  136. public void setConfigContext( Context ctx ) {
  137. this.configContext= ctx;
  138. }
  139. // -------------------- Registration/unregistration --------------------
  140. // temp - will only set in the jndi contexts
  141. Hashtable attributes=new Hashtable();
  142. Hashtable instances=new Hashtable();
  143. public void handleNotification(Notification notification, Object handback)
  144. {
  145. // register/unregister mbeans in jndi
  146. if( notification instanceof MBeanServerNotification ) {
  147. MBeanServerNotification msnot=(MBeanServerNotification)notification;
  148. ObjectName oname=msnot.getMBeanName();
  149. if( "jmx.mbean.created".equalsIgnoreCase( notification.getType() )) {
  150. try {
  151. Object mbean=mserver.getObjectInstance(oname);
  152. if( log.isDebugEnabled() )
  153. log.debug( "MBean created " + oname + " " + mbean);
  154. // XXX add filter support
  155. if( mbean instanceof NotificationBroadcaster ) {
  156. // register for attribute changes
  157. NotificationBroadcaster nb=(NotificationBroadcaster)mbean;
  158. nb.addNotificationListener(this, null, null);
  159. if( log.isDebugEnabled() )
  160. log.debug( "Add attribute change listener");
  161. }
  162. instances.put( oname.toString(), mbean );
  163. } catch( InstanceNotFoundException ex ) {
  164. log.error( "Instance not found for the created object", ex );
  165. }
  166. }
  167. if( "jmx.mbean.deleted".equalsIgnoreCase( notification.getType() )) {
  168. instances.remove(oname.toString());
  169. }
  170. }
  171. // set attributes in jndi
  172. // if( "jmx.attribute.changed".equals( notification.getType() )) {
  173. if( notification instanceof AttributeChangeNotification) {
  174. AttributeChangeNotification anotif=(AttributeChangeNotification)notification;
  175. String name=anotif.getAttributeName();
  176. Object value=anotif.getNewValue();
  177. Object source=anotif.getSource();
  178. String mname=null;
  179. Hashtable mbeanAtt=(Hashtable)attributes.get( source );
  180. if( mbeanAtt==null ) {
  181. mbeanAtt=new Hashtable();
  182. attributes.put( source, mbeanAtt);
  183. if( log.isDebugEnabled())
  184. log.debug("First attribute for " + source );
  185. }
  186. mbeanAtt.put( name, anotif );
  187. log.debug( "Attribute change notification " + name + " " + value + " " + source );
  188. }
  189. }
  190. public String dumpStatus() throws Exception
  191. {
  192. StringBuffer sb=new StringBuffer();
  193. Enumeration en=instances.keys();
  194. while (en.hasMoreElements()) {
  195. String on = (String) en.nextElement();
  196. Object mbean=instances.get(on);
  197. Hashtable mbeanAtt=(Hashtable)attributes.get(mbean);
  198. sb.append( "<mbean class=\"").append(on).append("\">");
  199. sb.append( "\n");
  200. Enumeration attEn=mbeanAtt.keys();
  201. while (attEn.hasMoreElements()) {
  202. String an = (String) attEn.nextElement();
  203. AttributeChangeNotification anotif=
  204. (AttributeChangeNotification)mbeanAtt.get(an);
  205. sb.append(" <attribute name=\"").append(an).append("\" ");
  206. sb.append("value=\"").append(anotif.getNewValue()).append("\">");
  207. sb.append( "\n");
  208. }
  209. sb.append( "</mbean>");
  210. sb.append( "\n");
  211. }
  212. return sb.toString();
  213. }
  214. public void replay() throws Exception
  215. {
  216. }
  217. public void init() throws Exception
  218. {
  219. MBeanServer mserver=(MBeanServer)Registry.getRegistry().getMBeanServer();
  220. ObjectName delegate=new ObjectName("JMImplementation:type=MBeanServerDelegate");
  221. // XXX need to extract info about previously loaded beans
  222. // we'll know of all registered beans
  223. mserver.addNotificationListener(delegate, this, null, null );
  224. }
  225. }