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.mbeans;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.net.URL;
022: import java.net.URLConnection;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.Map;
026: import java.util.jar.Attributes;
027: import java.util.jar.Manifest;
028:
029: import javax.management.Attribute;
030: import javax.management.AttributeNotFoundException;
031: import javax.management.MBeanException;
032: import javax.management.MBeanServer;
033: import javax.management.ObjectName;
034: import javax.management.ReflectionException;
035:
036: import org.apache.commons.logging.Log;
037: import org.apache.commons.logging.LogFactory;
038: import org.apache.commons.modeler.Registry;
039:
040: /**
041: * Based on jk2 proxy.
042: *
043: * Proxy using a very simple HTTP based protocol.
044: *
045: * For efficiency, it'll get bulk results and cache them - you
046: * can force an update by calling the refreshAttributes and refreshMetadata
047: * operations on this mbean.
048: *
049: * TODO: implement the user/pass auth ( right now you must use IP based security )
050: * TODO: eventually support https
051: * TODO: support for metadata ( mbean-descriptors ) for description and type conversions
052: * TODO: filter out trivial components ( mutexes, etc )
053: *
054: * @author Costin Manolache
055: */
056: public class SimpleRemoteConnector {
057: private static Log log = LogFactory
058: .getLog(SimpleRemoteConnector.class);
059:
060: // HTTP port of the remote JMX
061: String webServerHost = "localhost";
062: int webServerPort = 8080;
063:
064: // URL of the remote JMX servlet ( or equivalent )
065: String statusPath = "/jkstatus";
066:
067: // Not used right now
068: String user;
069: String pass;
070:
071: // Domain we mirror
072: String domain;
073:
074: // XXX Not used - allow translations
075: String localDomain;
076: String filter;
077:
078: //
079: long lastRefresh = 0;
080: long updateInterval = 5000; // 5 sec - it's min time between updates
081:
082: String prefix = "";
083:
084: Registry reg;
085:
086: MBeanServer mserver;
087:
088: // Last view
089: HashMap mbeans = new HashMap();
090:
091: public SimpleRemoteConnector() {
092: }
093:
094: /* -------------------- Public methods -------------------- */
095:
096: public String getWebServerHost() {
097: return webServerHost;
098: }
099:
100: public void setWebServerHost(String webServerHost) {
101: this .webServerHost = webServerHost;
102: }
103:
104: public int getWebServerPort() {
105: return webServerPort;
106: }
107:
108: public void setWebServerPort(int webServerPort) {
109: this .webServerPort = webServerPort;
110: }
111:
112: public long getUpdateInterval() {
113: return updateInterval;
114: }
115:
116: public void setUpdateInterval(long updateInterval) {
117: this .updateInterval = updateInterval;
118: }
119:
120: public String getUser() {
121: return user;
122: }
123:
124: public void setUser(String user) {
125: this .user = user;
126: }
127:
128: public String getPass() {
129: return pass;
130: }
131:
132: public String getDomain() {
133: return domain;
134: }
135:
136: public void setDomain(String domain) {
137: this .domain = domain;
138: }
139:
140: public void setPass(String pass) {
141: this .pass = pass;
142: }
143:
144: public String getStatusPath() {
145: return statusPath;
146: }
147:
148: public void setStatusPath(String statusPath) {
149: this .statusPath = statusPath;
150: }
151:
152: public String getFilter() {
153: return filter;
154: }
155:
156: public void setFilter(String filter) {
157: this .filter = filter;
158: }
159:
160: /* ==================== Start/stop ==================== */
161:
162: public void destroy() {
163: try {
164: // We should keep track of loaded beans and call stop.
165: // Modeler should do it...
166: Iterator mbeansIt = mbeans.values().iterator();
167: while (mbeansIt.hasNext()) {
168: MBeanProxy proxy = (MBeanProxy) mbeansIt.next();
169: ObjectName oname = proxy.getJmxName();
170: Registry.getRegistry().getMBeanServer()
171: .unregisterMBean(oname);
172: }
173: } catch (Throwable t) {
174: log.error("Destroy error", t);
175: }
176: }
177:
178: public void init() throws IOException {
179: try {
180: //if( log.isDebugEnabled() )
181: log.info("init " + webServerHost + " " + webServerPort);
182: reg = Registry.getRegistry();
183: // Get metadata for all mbeans on the remote side
184: //refreshMetadata();
185: // Get current values and mbeans
186: refreshAttributes();
187: } catch (Throwable t) {
188: log.error("Init error", t);
189: }
190: }
191:
192: public void start() throws IOException {
193: System.out.println("XXX start");
194: if (reg == null)
195: init();
196: }
197:
198: /** Refresh the proxies, if updateInterval passed
199: *
200: */
201: public void refresh() {
202: long time = System.currentTimeMillis();
203: if (time - lastRefresh < updateInterval) {
204: return;
205: }
206: System.out.println("refresh... ");
207: lastRefresh = time;
208: //refreshMetadata();
209: refreshAttributes();
210: }
211:
212: public void refreshAttributes() {
213: try {
214: int cnt = 0;
215: // connect to apache, get a list of mbeans
216: if (filter == null) {
217: filter = domain + ":*";
218: }
219:
220: InputStream is = getStream("qry=" + filter);
221: if (is == null)
222: return;
223:
224: Manifest mf = new Manifest(is);
225:
226: HashMap currentObjects = new HashMap(); // used to remove older ones
227: Map entries = mf.getEntries();
228: Iterator it = entries.keySet().iterator();
229: while (it.hasNext()) {
230: String name = (String) it.next();
231: Attributes attrs = (Attributes) entries.get(name);
232:
233: ObjectName oname = new ObjectName(name);
234: currentObjects.put(oname, "");
235: MBeanProxy proxy = (MBeanProxy) mbeans.get(oname);
236: if (proxy == null) {
237: log.debug("New object " + name);
238: String code = attrs.getValue("modelerType");
239: if (log.isDebugEnabled())
240: log.debug("Register " + name + " " + code);
241:
242: proxy = new MBeanProxy(this , code);
243: mbeans.put(oname, proxy);
244:
245: // Register
246: MBeanServer mserver = Registry.getRegistry()
247: .getMBeanServer();
248: if (!mserver.isRegistered(oname)) {
249: mserver.registerMBean(proxy, oname);
250: }
251: }
252: Iterator it2 = attrs.keySet().iterator();
253: while (it2.hasNext()) {
254: Object o = it2.next();
255: String att = (o == null) ? null : o.toString();
256: if ("modelerType".equals(att))
257: continue;
258: String val = attrs.getValue(att);
259: proxy.update(att, val);
260: cnt++;
261: }
262: }
263:
264: // Now we have to remove all the mbeans that are no longer there
265: Iterator existingIt = mbeans.keySet().iterator();
266: while (existingIt.hasNext()) {
267: ObjectName on = (ObjectName) existingIt.next();
268: if (currentObjects.get(on) != null)
269: continue; // still alive
270: if (log.isDebugEnabled())
271: log.debug("No longer alive " + on);
272: try {
273: mserver.unregisterMBean(on);
274: } catch (Throwable t) {
275: log.info("Error unregistering " + on + " "
276: + t.toString());
277: }
278: }
279:
280: log.info("Refreshing attributes " + cnt);
281: } catch (Exception ex) {
282: log.info("Error ", ex);
283: }
284: }
285:
286: // Not used right now - assume the metadata is available locally
287: // Could use mbeans-descriptors.xml or other formats.
288: // Will be called if code= is not found locally
289: public void refreshMetadata() {
290: try {
291: int cnt = 0;
292: int newCnt = 0;
293: InputStream is = getStream("getMetadata=" + domain + ":*");
294: if (is == null)
295: return;
296:
297: log.info("Refreshing metadata " + cnt + " " + newCnt);
298: } catch (Exception ex) {
299: log.info("Error ", ex);
300: }
301: }
302:
303: public Object invoke(Object oname, String name, Object params[],
304: String signature[]) throws MBeanException,
305: ReflectionException {
306: try {
307: // we support only string values
308: InputStream is = this .getStream("invoke=" + name + "&name="
309: + oname.toString());
310: if (is == null)
311: return null;
312: // String res=is.readLine();
313: // if( log.isDebugEnabled())
314: // log.debug( "Invoking " + jkName + " " + name + " result " + res);
315:
316: //this.refreshMetadata();
317: this .refreshAttributes();
318: } catch (Exception ex) {
319: throw new MBeanException(ex);
320: }
321: return null;
322: }
323:
324: public void setAttribute(ObjectName oname, Attribute attribute)
325: throws AttributeNotFoundException, MBeanException,
326: ReflectionException {
327: try {
328: // we support only string values
329: String val = (String) attribute.getValue();
330: String name = attribute.getName();
331: InputStream is = this .getStream("set=" + name + "&name="
332: + oname.toString() + "&value=" + val);
333: if (is == null)
334: return;
335: // String res=is.readLine();
336: // if( log.isDebugEnabled())
337: // log.debug( "Setting " + jkName + " " + name + " result " + res);
338:
339: //this.refreshMetadata();
340: this .refreshAttributes();
341: } catch (Exception ex) {
342: throw new MBeanException(ex);
343: }
344: }
345:
346: /** connect to apache using http, get a list of mbeans. Can be
347: * overriten to support different protocols ( jk/Unix domain sockets, etc )
348: */
349: protected InputStream getStream(String qry) throws Exception {
350: try {
351: String path = statusPath + "?" + qry;
352: URL url = new URL("http", webServerHost, webServerPort,
353: path);
354: log.debug("Connecting to " + url);
355: URLConnection urlc = url.openConnection();
356: InputStream is = urlc.getInputStream();
357: return is;
358: } catch (IOException e) {
359: log.info("Can't connect to jkstatus " + webServerHost + ":"
360: + webServerPort + " " + e.toString());
361: return null;
362: }
363: }
364:
365: }
|