001: /*
002: * Copyright 1999-2004 The Apache Software Foundation
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.jk.common;
018:
019: import java.io.IOException;
020: import java.io.BufferedReader;
021: import java.io.InputStreamReader;
022: import java.net.URLConnection;
023: import java.net.URL;
024: import java.util.List;
025: import java.util.ArrayList;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import javax.management.MBeanServer;
029: import javax.management.AttributeNotFoundException;
030: import javax.management.MBeanException;
031: import javax.management.ReflectionException;
032: import javax.management.Attribute;
033: import javax.management.ObjectName;
034:
035: import org.apache.jk.core.JkHandler;
036: import org.apache.commons.modeler.Registry;
037: import org.apache.commons.modeler.BaseModelMBean;
038: import org.apache.commons.modeler.ManagedBean;
039: import org.apache.commons.modeler.AttributeInfo;
040: import org.apache.commons.modeler.OperationInfo;
041: import org.apache.commons.logging.Log;
042: import org.apache.commons.logging.LogFactory;
043:
044: /**
045: * A small mbean that will act as a proxy for mod_jk2.
046: *
047: * For efficiency, it'll get bulk results and cache them - you
048: * can force an update by calling the refreshAttributes and refreshMetadata
049: * operations on this mbean.
050: *
051: * TODO: implement the user/pass auth ( right now you must use IP based security )
052: * TODO: eventually support https
053: * TODO: support for metadata ( mbean-descriptors ) for description and type conversions
054: * TODO: filter out trivial components ( mutexes, etc )
055: *
056: * @author Costin Manolache
057: */
058: public class ModJkMX extends JkHandler {
059: private static Log log = LogFactory.getLog(ModJkMX.class);
060:
061: MBeanServer mserver;
062: String webServerHost = "localhost";
063: int webServerPort = 80;
064: String statusPath = "/jkstatus";
065: String user;
066: String pass;
067: Registry reg;
068:
069: HashMap mbeans = new HashMap();
070: long lastRefresh = 0;
071: long updateInterval = 5000; // 5 sec - it's min time between updates
072:
073: public ModJkMX() {
074: }
075:
076: /* -------------------- Public methods -------------------- */
077:
078: public String getWebServerHost() {
079: return webServerHost;
080: }
081:
082: public void setWebServerHost(String webServerHost) {
083: this .webServerHost = webServerHost;
084: }
085:
086: public int getWebServerPort() {
087: return webServerPort;
088: }
089:
090: public void setWebServerPort(int webServerPort) {
091: this .webServerPort = webServerPort;
092: }
093:
094: public long getUpdateInterval() {
095: return updateInterval;
096: }
097:
098: public void setUpdateInterval(long updateInterval) {
099: this .updateInterval = updateInterval;
100: }
101:
102: public String getUser() {
103: return user;
104: }
105:
106: public void setUser(String user) {
107: this .user = user;
108: }
109:
110: public String getPass() {
111: return pass;
112: }
113:
114: public void setPass(String pass) {
115: this .pass = pass;
116: }
117:
118: public String getStatusPath() {
119: return statusPath;
120: }
121:
122: public void setStatusPath(String statusPath) {
123: this .statusPath = statusPath;
124: }
125:
126: /* ==================== Start/stop ==================== */
127:
128: public void destroy() {
129: try {
130: // We should keep track of loaded beans and call stop.
131: // Modeler should do it...
132: Iterator mbeansIt = mbeans.values().iterator();
133: MBeanServer mbserver = Registry.getRegistry()
134: .getMBeanServer();
135: while (mbeansIt.hasNext()) {
136: MBeanProxy proxy = (MBeanProxy) mbeansIt.next();
137: Object ooname = proxy.getObjectName();
138: if (ooname != null) {
139: ObjectName oname = null;
140: if (ooname instanceof ObjectName) {
141: oname = (ObjectName) ooname;
142: } else if (ooname instanceof String) {
143: oname = new ObjectName((String) ooname);
144: }
145: if (oname != null) {
146: mbserver.unregisterMBean(oname);
147: }
148: }
149: }
150: } catch (Throwable t) {
151: log.error("Destroy error", t);
152: }
153: }
154:
155: public void init() throws IOException {
156: try {
157: //if( log.isDebugEnabled() )
158: log.info("init " + webServerHost + " " + webServerPort);
159: reg = Registry.getRegistry();
160: refreshMetadata();
161: refreshAttributes();
162: } catch (Throwable t) {
163: log.error("Init error", t);
164: }
165: }
166:
167: public void start() throws IOException {
168: if (reg == null)
169: init();
170: }
171:
172: /** Refresh the proxies, if updateInterval passed
173: *
174: */
175: public void refresh() {
176: long time = System.currentTimeMillis();
177: if (time - lastRefresh < updateInterval) {
178: return;
179: }
180: lastRefresh = time;
181: refreshMetadata();
182: refreshAttributes();
183: }
184:
185: public void refreshAttributes() {
186: try {
187: int cnt = 0;
188: // connect to apache, get a list of mbeans
189: BufferedReader is = getStream("dmp=*");
190: if (is == null)
191: return;
192:
193: String name = null;
194: String att = null;
195: String val = null;
196: while (true) {
197: String line = is.readLine();
198: if (line == null)
199: break;
200: line = line.trim();
201: if ("".equals(line) || line.startsWith("#"))
202: continue;
203:
204: // for each mbean, create a proxy
205: if (log.isDebugEnabled())
206: log.debug("Read " + line);
207:
208: if (line.startsWith("[")) {
209: name = line.substring(1);
210: if (name.endsWith("]")) {
211: name = name.substring(0, name.length() - 1);
212: }
213: }
214: // Name/value pair
215: int idx = line.indexOf('=');
216: if (idx < 0)
217: continue;
218: att = line.substring(0, idx);
219: val = line.substring(idx + 1);
220:
221: if (log.isDebugEnabled())
222: log.debug("name: " + name + " att=" + att + " val="
223: + val);
224:
225: MBeanProxy proxy = (MBeanProxy) mbeans.get(name);
226: if (proxy == null) {
227: log.info("Unknown object " + name);
228: } else {
229: proxy.update(att, val);
230: cnt++;
231: }
232: }
233: log.info("Refreshing attributes " + cnt);
234: } catch (Exception ex) {
235: log.info("Error ", ex);
236: }
237: }
238:
239: /** connect to apache, get a list of mbeans
240: */
241: BufferedReader getStream(String qry) throws Exception {
242: try {
243: String path = statusPath + "?" + qry;
244: URL url = new URL("http", webServerHost, webServerPort,
245: path);
246: URLConnection urlc = url.openConnection();
247: BufferedReader is = new BufferedReader(
248: new InputStreamReader(urlc.getInputStream()));
249: return is;
250: } catch (IOException e) {
251: log.info("Can't connect to jkstatus " + webServerHost + ":"
252: + webServerPort + " " + e.toString());
253: return null;
254: }
255: }
256:
257: public void refreshMetadata() {
258: try {
259: int cnt = 0;
260: int newCnt = 0;
261: BufferedReader is = getStream("lst=*");
262: if (is == null)
263: return;
264: String name = null;
265: String type = null;
266: ArrayList getters = new ArrayList();
267: ArrayList setters = new ArrayList();
268: ArrayList methods = new ArrayList();
269: while (true) {
270: String line = is.readLine();
271: if (log.isDebugEnabled())
272: log.debug("Read " + line);
273:
274: // end of section
275: if (line == null || line.startsWith("[")) {
276: if (name != null) {
277: cnt++;
278: if (mbeans.get(name) == null) {
279: // New component
280: newCnt++;
281: MBeanProxy mproxy = new MBeanProxy(this );
282: mproxy
283: .init(name, getters, setters,
284: methods);
285: mbeans.put(name, mproxy);
286: }
287: if (log.isDebugEnabled())
288: log.debug("mbean name: " + name + " type="
289: + type);
290:
291: getters.clear();
292: setters.clear();
293: methods.clear();
294: }
295: }
296: // end of data
297: if (line == null)
298: break;
299:
300: line = line.trim();
301: if ("".equals(line) || line.startsWith("#"))
302: continue;
303:
304: // for each mbean, create a proxy
305:
306: if (line.startsWith("[") && line.endsWith("]")) {
307: name = line.substring(1, line.length() - 1);
308: }
309: if (line.startsWith("T=")) {
310: type = line.substring(2);
311: }
312: if (line.startsWith("G=")) {
313: getters.add(line.substring(2));
314: }
315: if (line.startsWith("S=")) {
316: setters.add(line.substring(2));
317: }
318: if (line.startsWith("M=")) {
319: methods.add(line.substring(2));
320: }
321: }
322: log.info("Refreshing metadata " + cnt + " " + newCnt);
323: } catch (Exception ex) {
324: log.info("Error ", ex);
325: }
326: }
327:
328: /** Use the same metadata, except that we replace the attribute
329: * get/set methods.
330: */
331: static class MBeanProxy extends BaseModelMBean {
332: private static Log log = LogFactory.getLog(MBeanProxy.class);
333:
334: String jkName;
335: List getAttNames;
336: List setAttNames;
337: HashMap atts = new HashMap();
338: ModJkMX jkmx;
339:
340: public MBeanProxy(ModJkMX jkmx) throws Exception {
341: this .jkmx = jkmx;
342: }
343:
344: void init(String name, List getters, List setters, List methods)
345: throws Exception {
346: if (log.isDebugEnabled())
347: log.debug("Register " + name);
348: int col = name.indexOf(':');
349: this .jkName = name;
350: String type = name.substring(0, col);
351: String id = name.substring(col + 1);
352: id = id.replace('*', '%');
353: id = id.replace(':', '%');
354: if (id.length() == 0) {
355: id = "default";
356: }
357: ManagedBean mbean = new ManagedBean();
358:
359: AttributeInfo ai = new AttributeInfo();
360: ai.setName("jkName");
361: ai.setType("java.lang.String");
362: ai.setWriteable(false);
363: mbean.addAttribute(ai);
364:
365: for (int i = 0; i < getters.size(); i++) {
366: String att = (String) getters.get(i);
367: // Register metadata
368: ai = new AttributeInfo();
369: ai.setName(att);
370: ai.setType("java.lang.String");
371: if (!setters.contains(att))
372: ai.setWriteable(false);
373: mbean.addAttribute(ai);
374: }
375: for (int i = 0; i < setters.size(); i++) {
376: String att = (String) setters.get(i);
377: if (getters.contains(att))
378: continue;
379: // Register metadata
380: ai = new AttributeInfo();
381: ai.setName(att);
382: ai.setType("java.lang.String");
383: ai.setReadable(false);
384: mbean.addAttribute(ai);
385: }
386: for (int i = 0; i < methods.size(); i++) {
387: String att = (String) methods.get(i);
388: // Register metadata
389: OperationInfo oi = new OperationInfo();
390: oi.setName(att);
391: oi.setReturnType("void");
392: mbean.addOperation(oi);
393: }
394:
395: this .setModelMBeanInfo(mbean.createMBeanInfo());
396:
397: MBeanServer mserver = Registry.getRegistry()
398: .getMBeanServer();
399: oname = new ObjectName("apache:type=" + type + ",id=" + id);
400: mserver.registerMBean(this , oname);
401: }
402:
403: private void update(String name, String val) {
404: log.debug("Updating " + jkName + " " + name + " " + val);
405: atts.put(name, val);
406: }
407:
408: public Object getAttribute(String name)
409: throws AttributeNotFoundException, MBeanException,
410: ReflectionException {
411: if ("jkName".equals(name)) {
412: return jkName;
413: }
414: jkmx.refresh();
415: return atts.get(name);
416: }
417:
418: public void setAttribute(Attribute attribute)
419: throws AttributeNotFoundException, MBeanException,
420: ReflectionException {
421: try {
422: // we support only string values
423: String val = (String) attribute.getValue();
424: String name = attribute.getName();
425: BufferedReader is = jkmx.getStream("set=" + jkName
426: + "|" + name + "|" + val);
427: if (is == null)
428: return;
429: String res = is.readLine();
430: if (log.isDebugEnabled())
431: log.debug("Setting " + jkName + " " + name
432: + " result " + res);
433:
434: jkmx.refreshMetadata();
435: jkmx.refreshAttributes();
436: } catch (Exception ex) {
437: throw new MBeanException(ex);
438: }
439: }
440:
441: public Object invoke(String name, Object params[],
442: String signature[]) throws MBeanException,
443: ReflectionException {
444: try {
445: // we support only string values
446: BufferedReader is = jkmx.getStream("inv=" + jkName
447: + "|" + name);
448: if (is == null)
449: return null;
450: String res = is.readLine();
451: if (log.isDebugEnabled())
452: log.debug("Invoking " + jkName + " " + name
453: + " result " + res);
454:
455: jkmx.refreshMetadata();
456: jkmx.refreshAttributes();
457: } catch (Exception ex) {
458: throw new MBeanException(ex);
459: }
460: return null;
461: }
462:
463: }
464:
465: }
|