001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 019 package org.apache.commons.modeler; 020 021 022 import java.util.Enumeration; 023 import java.util.Hashtable; 024 025 import javax.management.AttributeChangeNotification; 026 import javax.management.InstanceNotFoundException; 027 import javax.management.MBeanException; 028 import javax.management.MBeanServer; 029 import javax.management.MBeanServerNotification; 030 import javax.management.Notification; 031 import javax.management.NotificationBroadcaster; 032 import javax.management.NotificationListener; 033 import javax.management.ObjectName; 034 import javax.naming.Context; 035 036 import org.apache.commons.logging.Log; 037 import org.apache.commons.logging.LogFactory; 038 039 // EXPERIMENTAL. It may fit better in tomcat jndi impl. 040 041 042 /** 043 * 044 * Link between JNDI and JMX. JNDI can be used for persistence ( it is 045 * an API for storing hierarchical data and a perfect fit for that ), as 046 * well as an alternate view of the MBean registry. 047 * 048 * If this component is enabled, all MBeans will be registered in JNDI, and 049 * all attributes that are set via JMX can be stored in a DirContext. 050 * 051 * This acts as a "recorder" for creation of mbeans and attribute changes 052 * done via JMX. 053 * 054 * XXX How can we control ( filter ) which mbeans will be registere ? Or 055 * attributes ? 056 * XXX How can we get the beans and attributes loaded before jndijmx ? 057 * 058 * The intended use: 059 * - do whatever you want to start the application 060 * - load JndiJmx as an mbean 061 * - make changes via JMX. All changes are recorded 062 * - you can use JndiJmx to save the changes in a Jndi context. 063 * - you can use JndiJmx to load changes from a JndiContext and replay them. 064 * 065 * The main benefit is that only changed attributes are saved, and the Jndi 066 * layer can preserve most of the original structure of the config file. The 067 * alternative is to override the config files with config info extracted 068 * from the live objects - but it's very hard to save only what was actually 069 * changed and preserve structure and comments. 070 * 071 * @author Costin Manolache 072 */ 073 public class JndiJmx extends BaseModelMBean implements NotificationListener { 074 075 076 private static Log log= LogFactory.getLog(JndiJmx.class); 077 078 protected Context componentContext; 079 protected Context descriptorContext; 080 protected Context configContext; 081 082 MBeanServer mserver; 083 084 /** 085 * Protected constructor to require use of the factory create method. 086 */ 087 public JndiJmx() throws MBeanException { 088 super(JndiJmx.class.getName()); 089 } 090 091 092 /** If a JNDI context is set, all components 093 * will be registered in the context. 094 * 095 * @param ctx 096 */ 097 public void setComponentContext(Context ctx) { 098 this.componentContext= ctx; 099 } 100 101 /** JNDI context for component descriptors ( metadata ). 102 * 103 * @param ctx 104 */ 105 public void setDescriptorContext(Context ctx) { 106 this.descriptorContext= ctx; 107 } 108 109 /** JNDI context where attributes will be stored for persistence 110 * 111 */ 112 public void setConfigContext( Context ctx ) { 113 this.configContext= ctx; 114 } 115 116 // -------------------- Registration/unregistration -------------------- 117 // temp - will only set in the jndi contexts 118 Hashtable attributes=new Hashtable(); 119 Hashtable instances=new Hashtable(); 120 121 public void handleNotification(Notification notification, Object handback) 122 { 123 // register/unregister mbeans in jndi 124 if( notification instanceof MBeanServerNotification ) { 125 MBeanServerNotification msnot=(MBeanServerNotification)notification; 126 127 ObjectName oname=msnot.getMBeanName(); 128 129 if( "jmx.mbean.created".equalsIgnoreCase( notification.getType() )) { 130 try { 131 Object mbean=mserver.getObjectInstance(oname); 132 133 if( log.isDebugEnabled() ) 134 log.debug( "MBean created " + oname + " " + mbean); 135 136 // XXX add filter support 137 if( mbean instanceof NotificationBroadcaster ) { 138 // register for attribute changes 139 NotificationBroadcaster nb=(NotificationBroadcaster)mbean; 140 nb.addNotificationListener(this, null, null); 141 if( log.isDebugEnabled() ) 142 log.debug( "Add attribute change listener"); 143 } 144 145 instances.put( oname.toString(), mbean ); 146 } catch( InstanceNotFoundException ex ) { 147 log.error( "Instance not found for the created object", ex ); 148 } 149 } 150 if( "jmx.mbean.deleted".equalsIgnoreCase( notification.getType() )) { 151 instances.remove(oname.toString()); 152 } 153 } 154 155 // set attributes in jndi 156 // if( "jmx.attribute.changed".equals( notification.getType() )) { 157 if( notification instanceof AttributeChangeNotification) { 158 159 AttributeChangeNotification anotif=(AttributeChangeNotification)notification; 160 String name=anotif.getAttributeName(); 161 Object value=anotif.getNewValue(); 162 Object source=anotif.getSource(); 163 String mname=null; 164 165 Hashtable mbeanAtt=(Hashtable)attributes.get( source ); 166 if( mbeanAtt==null ) { 167 mbeanAtt=new Hashtable(); 168 attributes.put( source, mbeanAtt); 169 if( log.isDebugEnabled()) 170 log.debug("First attribute for " + source ); 171 } 172 mbeanAtt.put( name, anotif ); 173 174 log.debug( "Attribute change notification " + name + " " + value + " " + source ); 175 176 } 177 178 } 179 180 public String dumpStatus() throws Exception 181 { 182 StringBuffer sb=new StringBuffer(); 183 Enumeration en=instances.keys(); 184 while (en.hasMoreElements()) { 185 String on = (String) en.nextElement(); 186 Object mbean=instances.get(on); 187 Hashtable mbeanAtt=(Hashtable)attributes.get(mbean); 188 189 sb.append( "<mbean class=\"").append(on).append("\">"); 190 sb.append( "\n"); 191 Enumeration attEn=mbeanAtt.keys(); 192 while (attEn.hasMoreElements()) { 193 String an = (String) attEn.nextElement(); 194 AttributeChangeNotification anotif= 195 (AttributeChangeNotification)mbeanAtt.get(an); 196 sb.append(" <attribute name=\"").append(an).append("\" "); 197 sb.append("value=\"").append(anotif.getNewValue()).append("\">"); 198 sb.append( "\n"); 199 } 200 201 202 sb.append( "</mbean>"); 203 sb.append( "\n"); 204 } 205 return sb.toString(); 206 } 207 208 public void replay() throws Exception 209 { 210 211 212 } 213 214 215 public void init() throws Exception 216 { 217 218 MBeanServer mserver=(MBeanServer)Registry.getRegistry().getMBeanServer(); 219 ObjectName delegate=new ObjectName("JMImplementation:type=MBeanServerDelegate"); 220 221 // XXX need to extract info about previously loaded beans 222 223 // we'll know of all registered beans 224 mserver.addNotificationListener(delegate, this, null, null ); 225 226 } 227 228 }