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: package org.apache.commons.modeler.modules;
018:
019: import java.io.FileNotFoundException;
020: import java.io.FileOutputStream;
021: import java.io.InputStream;
022: import java.net.URL;
023: import java.util.ArrayList;
024: import java.util.HashMap;
025: import java.util.List;
026:
027: import javax.management.Attribute;
028: import javax.management.MBeanServer;
029: import javax.management.ObjectName;
030: import javax.management.loading.MLet;
031: import javax.xml.transform.TransformerException;
032:
033: import org.apache.commons.logging.Log;
034: import org.apache.commons.logging.LogFactory;
035: import org.apache.commons.modeler.AttributeInfo;
036: import org.apache.commons.modeler.BaseModelMBean;
037: import org.apache.commons.modeler.ManagedBean;
038: import org.apache.commons.modeler.Registry;
039: import org.apache.commons.modeler.util.DomUtil;
040: import org.w3c.dom.Document;
041: import org.w3c.dom.Node;
042:
043: /** This will create mbeans based on a config file.
044: * The format is an extended version of MLET.
045: *
046: * Classloading. We don't support any explicit classloader tag.
047: * A ClassLoader is just an mbean ( it can be the standard MLetMBean or
048: * a custom one ).
049: *
050: * XXX add a special attribute to reference the loader mbean,
051: * XXX figure out how to deal with private loaders
052: */
053: public class MbeansSource extends ModelerSource implements
054: MbeansSourceMBean {
055: private static Log log = LogFactory.getLog(MbeansSource.class);
056: Registry registry;
057: String type;
058:
059: // true if we are during the original loading
060: boolean loading = true;
061: List mbeans = new ArrayList();
062: static boolean loaderLoaded = false;
063: private Document document;
064: private HashMap object2Node = new HashMap();
065:
066: long lastUpdate;
067: long updateInterval = 10000; // 10s
068:
069: public void setRegistry(Registry reg) {
070: this .registry = reg;
071: }
072:
073: public void setLocation(String loc) {
074: this .location = loc;
075: }
076:
077: /** Used if a single component is loaded
078: *
079: * @param type
080: */
081: public void setType(String type) {
082: this .type = type;
083: }
084:
085: public void setSource(Object source) {
086: this .source = source;
087: }
088:
089: public Object getSource() {
090: return source;
091: }
092:
093: public String getLocation() {
094: return location;
095: }
096:
097: /** Return the list of mbeans created by this source.
098: * It can be used to implement runtime services.
099: */
100: public List getMBeans() {
101: return mbeans;
102: }
103:
104: public List loadDescriptors(Registry registry, String location,
105: String type, Object source) throws Exception {
106: setRegistry(registry);
107: setLocation(location);
108: setType(type);
109: setSource(source);
110: execute();
111: return mbeans;
112: }
113:
114: public void start() throws Exception {
115: registry.invoke(mbeans, "start", false);
116: }
117:
118: public void stop() throws Exception {
119: registry.invoke(mbeans, "stop", false);
120: }
121:
122: public void init() throws Exception {
123: if (mbeans == null)
124: execute();
125: if (registry == null)
126: registry = Registry.getRegistry();
127:
128: registry.invoke(mbeans, "init", false);
129: }
130:
131: public void destroy() throws Exception {
132: registry.invoke(mbeans, "destroy", false);
133: }
134:
135: public void load() throws Exception {
136: execute(); // backward compat
137: }
138:
139: public void execute() throws Exception {
140: if (registry == null)
141: registry = Registry.getRegistry();
142: try {
143: InputStream stream = getInputStream();
144: long t1 = System.currentTimeMillis();
145: document = DomUtil.readXml(stream);
146:
147: // We don't care what the root node is.
148: Node descriptorsN = document.getDocumentElement();
149:
150: if (descriptorsN == null) {
151: log.error("No descriptors found");
152: return;
153: }
154:
155: Node firstMbeanN = DomUtil.getChild(descriptorsN, null);
156:
157: if (firstMbeanN == null) {
158: // maybe we have a single mlet
159: if (log.isDebugEnabled())
160: log.debug("No child " + descriptorsN);
161: firstMbeanN = descriptorsN;
162: }
163:
164: MBeanServer server = (MBeanServer) Registry.getServer();
165:
166: // XXX Not very clean... Just a workaround
167: if (!loaderLoaded) {
168: // Register a loader that will be find ant classes.
169: ObjectName defaultLoader = new ObjectName("modeler",
170: "loader", "modeler");
171: MLet mlet = new MLet(new URL[0], this .getClass()
172: .getClassLoader());
173: server.registerMBean(mlet, defaultLoader);
174: loaderLoaded = true;
175: }
176:
177: // Process nodes
178: for (Node mbeanN = firstMbeanN; mbeanN != null; mbeanN = DomUtil
179: .getNext(mbeanN, null, Node.ELEMENT_NODE)) {
180: String nodeName = mbeanN.getNodeName();
181:
182: // mbean is the "official" name
183: if ("mbean".equals(nodeName) || "MLET".equals(nodeName)) {
184: String code = DomUtil.getAttribute(mbeanN, "code");
185: String objectName = DomUtil.getAttribute(mbeanN,
186: "objectName");
187: if (objectName == null) {
188: objectName = DomUtil.getAttribute(mbeanN,
189: "name");
190: }
191:
192: if (log.isDebugEnabled())
193: log.debug("Processing mbean objectName="
194: + objectName + " code=" + code);
195:
196: // args can be grouped in constructor or direct childs
197: Node constructorN = DomUtil.getChild(mbeanN,
198: "constructor");
199: if (constructorN == null)
200: constructorN = mbeanN;
201:
202: ArgsInfo info = processArg(constructorN);
203:
204: try {
205: ObjectName oname = new ObjectName(objectName);
206: if (!server.isRegistered(oname)) {
207: // We wrap everything in a model mbean.
208: // XXX need to support "StandardMBeanDescriptorsSource"
209: String modelMBean = BaseModelMBean.class
210: .getName();
211: if (info == null) {
212: server.createMBean(modelMBean, oname,
213: new Object[] { code, this },
214: new String[] {
215: String.class.getName(),
216: ModelerSource.class
217: .getName() });
218: } else {
219: server.createMBean(modelMBean, oname,
220: new Object[] { code, this ,
221: info.getValues(),
222: info.getSigs() },
223: new String[] {
224: String.class.getName(),
225: ModelerSource.class
226: .getName(),
227: Object[].class
228: .getName(),
229: String[].class
230: .getName() });
231: }
232:
233: mbeans.add(oname);
234: }
235: object2Node.put(oname, mbeanN);
236: // XXX Arguments, loader !!!
237: } catch (Exception ex) {
238: log.error("Error creating mbean " + objectName,
239: ex);
240: }
241:
242: Node firstAttN = DomUtil.getChild(mbeanN,
243: "attribute");
244: for (Node descN = firstAttN; descN != null; descN = DomUtil
245: .getNext(descN)) {
246: processAttribute(server, descN, objectName);
247: }
248: } else if ("jmx-operation".equals(nodeName)) {
249: String name = DomUtil.getAttribute(mbeanN,
250: "objectName");
251: if (name == null)
252: name = DomUtil.getAttribute(mbeanN, "name");
253:
254: String operation = DomUtil.getAttribute(mbeanN,
255: "operation");
256:
257: if (log.isDebugEnabled())
258: log.debug("Processing invoke objectName="
259: + name + " code=" + operation);
260: try {
261: ObjectName oname = new ObjectName(name);
262:
263: ArgsInfo info = processArg(mbeanN);
264: if (info == null) {
265: server.invoke(oname, operation, null, null);
266: } else {
267: server.invoke(oname, operation, info
268: .getValues(), info.getSigs());
269: }
270: } catch (Exception e) {
271: log.error("Error in invoke " + name + " "
272: + operation);
273: }
274: }
275:
276: ManagedBean managed = new ManagedBean();
277: DomUtil.setAttributes(managed, mbeanN);
278: Node firstN;
279:
280: // process attribute info
281: firstN = DomUtil.getChild(mbeanN, "attribute");
282: for (Node descN = firstN; descN != null; descN = DomUtil
283: .getNext(descN)) {
284: AttributeInfo ci = new AttributeInfo();
285: DomUtil.setAttributes(ci, descN);
286: managed.addAttribute(ci);
287: }
288:
289: }
290:
291: long t2 = System.currentTimeMillis();
292: log.info("Reading mbeans " + (t2 - t1));
293: loading = false;
294: } catch (Exception ex) {
295: log.error("Error reading mbeans ", ex);
296: }
297: }
298:
299: public void updateField(ObjectName oname, String name, Object value) {
300: if (loading)
301: return;
302: // nothing by default
303: //log.info( "XXX UpdateField " + oname + " " + name + " " + value);
304: Node n = (Node) object2Node.get(oname);
305: if (n == null) {
306: log.info("Node not found " + oname);
307: return;
308: }
309: Node attNode = DomUtil.findChildWithAtt(n, "attribute", "name",
310: name);
311: if (attNode == null) {
312: // found no existing attribute with this name
313: attNode = n.getOwnerDocument().createElement("attribute");
314: DomUtil.setAttribute(attNode, "name", name);
315: n.appendChild(attNode);
316: }
317: String oldValue = DomUtil.getAttribute(attNode, "value");
318: if (oldValue != null) {
319: // we'll convert all values to text content
320: DomUtil.removeAttribute(attNode, "value");
321: }
322: DomUtil.setText(attNode, value.toString());
323:
324: //store();
325: }
326:
327: /** Store the mbeans.
328: * XXX add a background thread to store it periodically
329: */
330: public void save() {
331: // XXX customize no often than ( based on standard descriptor ), etc.
332: // It doesn't work very well if we call this on each set att -
333: // the triger will work for the first att, but all others will be delayed
334: long time = System.currentTimeMillis();
335: if (location != null && time - lastUpdate > updateInterval) {
336: lastUpdate = time;
337: try {
338: FileOutputStream fos = new FileOutputStream(location);
339: DomUtil.writeXml(document, fos);
340: } catch (TransformerException e) {
341: log.error("Error writing");
342: } catch (FileNotFoundException e) {
343: log.error("Error writing", e);
344: }
345: }
346: }
347:
348: private void processAttribute(MBeanServer server, Node descN,
349: String objectName) {
350: String attName = DomUtil.getAttribute(descN, "name");
351: String value = DomUtil.getAttribute(descN, "value");
352: String type = null; // DomUtil.getAttribute(descN, "type");
353: if (value == null) {
354: // The value may be specified as CDATA
355: value = DomUtil.getContent(descN);
356: }
357: try {
358: if (log.isDebugEnabled())
359: log.debug("Set attribute " + objectName + " " + attName
360: + " " + value);
361: ObjectName oname = new ObjectName(objectName);
362: // find the type
363: if (type == null)
364: type = registry.getType(oname, attName);
365:
366: if (type == null) {
367: log.info("Can't find attribute " + objectName + " "
368: + attName);
369:
370: } else {
371: Object valueO = registry.convertValue(type, value);
372: server.setAttribute(oname, new Attribute(attName,
373: valueO));
374: }
375: } catch (Exception ex) {
376: log.error("Error processing attribute " + objectName + " "
377: + attName + " " + value, ex);
378: }
379:
380: }
381:
382: private ArgsInfo processArg(Node mbeanN) {
383: Node firstArgN = DomUtil.getChild(mbeanN, "arg");
384: if (firstArgN == null) {
385: return null;
386: }
387: ArgsInfo info = new ArgsInfo();
388: // process all args
389: for (Node argN = firstArgN; argN != null; argN = DomUtil
390: .getNext(argN)) {
391: String type = DomUtil.getAttribute(argN, "type");
392: String value = DomUtil.getAttribute(argN, "value");
393: if (value == null) {
394: // The value may be specified as CDATA
395: value = DomUtil.getContent(argN);
396: }
397: info.addArgPair(type, registry.convertValue(type, value));
398: }
399: return info;
400: }
401:
402: private static class ArgsInfo {
403: private List sigs = new ArrayList();
404: private List values = new ArrayList();
405:
406: ArgsInfo() {
407: }
408:
409: public String[] getSigs() {
410: return (String[]) sigs.toArray(new String[sigs.size()]);
411: }
412:
413: public Object[] getValues() {
414: return values.toArray(new Object[values.size()]);
415: }
416:
417: public void addArgPair(String name, Object val) {
418: sigs.add(name);
419: values.add(val);
420: }
421: }
422: }
|