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.tomcat.util.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.juli.logging.Log;
034: import org.apache.juli.logging.LogFactory;
035: import org.apache.tomcat.util.DomUtil;
036: import org.apache.tomcat.util.modeler.AttributeInfo;
037: import org.apache.tomcat.util.modeler.BaseModelMBean;
038: import org.apache.tomcat.util.modeler.ManagedBean;
039: import org.apache.tomcat.util.modeler.Registry;
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: 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: server.createMBean(modelMBean, oname,
212: new Object[] { code, this },
213: new String[] {
214: String.class.getName(),
215: ModelerSource.class
216: .getName() });
217: mbeans.add(oname);
218: }
219: object2Node.put(oname, mbeanN);
220: // XXX Arguments, loader !!!
221: } catch (Exception ex) {
222: log.error("Error creating mbean " + objectName,
223: ex);
224: }
225:
226: Node firstAttN = DomUtil.getChild(mbeanN,
227: "attribute");
228: for (Node descN = firstAttN; descN != null; descN = DomUtil
229: .getNext(descN)) {
230: processAttribute(server, descN, objectName);
231: }
232: } else if ("jmx-operation".equals(nodeName)) {
233: String name = DomUtil.getAttribute(mbeanN,
234: "objectName");
235: if (name == null)
236: name = DomUtil.getAttribute(mbeanN, "name");
237:
238: String operation = DomUtil.getAttribute(mbeanN,
239: "operation");
240:
241: if (log.isDebugEnabled())
242: log.debug("Processing invoke objectName="
243: + name + " code=" + operation);
244: try {
245: ObjectName oname = new ObjectName(name);
246:
247: processArg(mbeanN);
248: server.invoke(oname, operation, null, null);
249: } catch (Exception e) {
250: log.error("Error in invoke " + name + " "
251: + operation);
252: }
253: }
254:
255: ManagedBean managed = new ManagedBean();
256: DomUtil.setAttributes(managed, mbeanN);
257: Node firstN;
258:
259: // process attribute info
260: firstN = DomUtil.getChild(mbeanN, "attribute");
261: for (Node descN = firstN; descN != null; descN = DomUtil
262: .getNext(descN)) {
263: AttributeInfo ci = new AttributeInfo();
264: DomUtil.setAttributes(ci, descN);
265: managed.addAttribute(ci);
266: }
267:
268: }
269:
270: long t2 = System.currentTimeMillis();
271: log.info("Reading mbeans " + (t2 - t1));
272: loading = false;
273: } catch (Exception ex) {
274: log.error("Error reading mbeans ", ex);
275: }
276: }
277:
278: public void updateField(ObjectName oname, String name, Object value) {
279: if (loading)
280: return;
281: // nothing by default
282: //log.info( "XXX UpdateField " + oname + " " + name + " " + value);
283: Node n = (Node) object2Node.get(oname);
284: if (n == null) {
285: log.info("Node not found " + oname);
286: return;
287: }
288: Node attNode = DomUtil.findChildWithAtt(n, "attribute", "name",
289: name);
290: if (attNode == null) {
291: // found no existing attribute with this name
292: attNode = n.getOwnerDocument().createElement("attribute");
293: DomUtil.setAttribute(attNode, "name", name);
294: n.appendChild(attNode);
295: }
296: String oldValue = DomUtil.getAttribute(attNode, "value");
297: if (oldValue != null) {
298: // we'll convert all values to text content
299: DomUtil.removeAttribute(attNode, "value");
300: }
301: DomUtil.setText(attNode, value.toString());
302:
303: //store();
304: }
305:
306: /** Store the mbeans.
307: * XXX add a background thread to store it periodically
308: */
309: public void save() {
310: // XXX customize no often than ( based on standard descriptor ), etc.
311: // It doesn't work very well if we call this on each set att -
312: // the triger will work for the first att, but all others will be delayed
313: long time = System.currentTimeMillis();
314: if (location != null && time - lastUpdate > updateInterval) {
315: lastUpdate = time;
316: try {
317: FileOutputStream fos = new FileOutputStream(location);
318: DomUtil.writeXml(document, fos);
319: } catch (TransformerException e) {
320: log.error("Error writing");
321: } catch (FileNotFoundException e) {
322: log.error("Error writing", e);
323: }
324: }
325: }
326:
327: private void processAttribute(MBeanServer server, Node descN,
328: String objectName) {
329: String attName = DomUtil.getAttribute(descN, "name");
330: String value = DomUtil.getAttribute(descN, "value");
331: String type = null; // DomUtil.getAttribute(descN, "type");
332: if (value == null) {
333: // The value may be specified as CDATA
334: value = DomUtil.getContent(descN);
335: }
336: try {
337: if (log.isDebugEnabled())
338: log.debug("Set attribute " + objectName + " " + attName
339: + " " + value);
340: ObjectName oname = new ObjectName(objectName);
341: // find the type
342: if (type == null)
343: type = registry.getType(oname, attName);
344:
345: if (type == null) {
346: log.info("Can't find attribute " + objectName + " "
347: + attName);
348:
349: } else {
350: Object valueO = registry.convertValue(type, value);
351: server.setAttribute(oname, new Attribute(attName,
352: valueO));
353: }
354: } catch (Exception ex) {
355: log.error("Error processing attribute " + objectName + " "
356: + attName + " " + value, ex);
357: }
358:
359: }
360:
361: private void processArg(Node mbeanN) {
362: Node firstArgN = DomUtil.getChild(mbeanN, "arg");
363: // process all args
364: for (Node argN = firstArgN; argN != null; argN = DomUtil
365: .getNext(argN)) {
366: String type = DomUtil.getAttribute(argN, "type");
367: String value = DomUtil.getAttribute(argN, "value");
368: if (value == null) {
369: // The value may be specified as CDATA
370: value = DomUtil.getContent(argN);
371: }
372: }
373: }
374: }
|