View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  
19  package org.apache.commons.modeler;
20  
21  
22  import java.util.Enumeration;
23  import java.util.Hashtable;
24  
25  import javax.management.AttributeChangeNotification;
26  import javax.management.InstanceNotFoundException;
27  import javax.management.MBeanException;
28  import javax.management.MBeanServer;
29  import javax.management.MBeanServerNotification;
30  import javax.management.Notification;
31  import javax.management.NotificationBroadcaster;
32  import javax.management.NotificationListener;
33  import javax.management.ObjectName;
34  import javax.naming.Context;
35  
36  import org.apache.commons.logging.Log;
37  import org.apache.commons.logging.LogFactory;
38  
39  // EXPERIMENTAL. It may fit better in tomcat jndi impl.
40  
41  
42  /***
43   *
44   * Link between JNDI and JMX. JNDI can be used for persistence ( it is
45   * an API for storing hierarchical data and a perfect fit for that ), as
46   * well as an alternate view of the MBean registry.
47   *
48   * If this component is enabled, all MBeans will be registered in JNDI, and
49   * all attributes that are set via JMX can be stored in a DirContext.
50   *
51   * This acts as a "recorder" for creation of mbeans and attribute changes
52   * done via JMX.
53   *
54   * XXX How can we control ( filter ) which mbeans will be registere ? Or
55   * attributes ?
56   * XXX How can we get the beans and attributes loaded before jndijmx ?
57   *
58   * The intended use:
59   * - do whatever you want to start the application
60   * - load JndiJmx as an mbean
61   * - make changes via JMX. All changes are recorded
62   * - you can use JndiJmx to save the changes in a Jndi context.
63   * - you can use JndiJmx to load changes from a JndiContext and replay them.
64   *
65   * The main benefit is that only changed attributes are saved, and the Jndi
66   * layer can preserve most of the original structure of the config file. The
67   * alternative is to override the config files with config info extracted
68   * from the live objects - but it's very hard to save only what was actually
69   * changed and preserve structure and comments.
70   *
71   * @author Costin Manolache
72   */
73  public class JndiJmx extends BaseModelMBean implements NotificationListener {
74  
75  
76      private static Log log= LogFactory.getLog(JndiJmx.class);
77  
78      protected Context componentContext;
79      protected Context descriptorContext;
80      protected Context configContext;
81  
82      MBeanServer mserver;
83  
84      /***
85       * Protected constructor to require use of the factory create method.
86       */
87      public JndiJmx() throws MBeanException {
88          super(JndiJmx.class.getName());
89      }
90  
91  
92      /*** If a JNDI context is set, all components
93       * will be registered in the context.
94       *
95       * @param ctx
96       */
97      public void setComponentContext(Context ctx) {
98          this.componentContext= ctx;
99      }
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 }