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