001: /*
002: * Copyright 2004 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package sun.jvmstat.monitor;
027:
028: import java.util.*;
029: import java.net.*;
030: import java.lang.reflect.*;
031:
032: import sun.jvmstat.monitor.event.HostListener;
033:
034: /**
035: * An abstraction for a host that contains instrumented Java Virtual
036: * Machines. The class provides abstract factory methods for creating
037: * concrete instances of this class and factory methods for creating
038: * {@link MonitoredVm} instances. Concrete implementations of this class
039: * provide methods for managing the communications protocols and provide
040: * for event notification.
041: *
042: * @author Brian Doherty
043: * @version 1.10, 05/09/07
044: * @since 1.5
045: *
046: * @see HostIdentifier
047: * @see VmIdentifier
048: * @see MonitoredVm
049: * @see HostListener
050: */
051: public abstract class MonitoredHost {
052: private static Map<HostIdentifier, MonitoredHost> monitoredHosts = new HashMap<HostIdentifier, MonitoredHost>();
053:
054: /*
055: * The monitoring implementation override mechanism. The value of
056: * this property is used as the class name for the concrete MonitoredHost
057: * subclass that implements the monitoring APIs. Setting this property
058: * will cause the remaining override mechanisms to be ignored. When
059: * this mechanism is used, the HostIdentifier scheme name, which
060: * indicates the communications protocol, is not used to locate a
061: * the protocol specific package. However, the HostIdentifier is
062: * still passed to the corresponding single arg constructor.
063: * This property is not expected to be set in normal circumstances.
064: */
065: private static final String IMPL_OVERRIDE_PROP_NAME = "sun.jvmstat.monitor.MonitoredHost";
066:
067: /*
068: * The monitoring package name override mechanism. The value
069: * the this property is used as base package name for the
070: * monitoring implementation package. This property is not
071: * expected to be set under normal circumstances.
072: */
073: private static final String IMPL_PKG_PROP_NAME = "sun.jvmstat.monitor.package";
074: private static final String IMPL_PACKAGE = System.getProperty(
075: IMPL_PKG_PROP_NAME, "sun.jvmstat.perfdata");
076:
077: /*
078: * The default optimized local protocol override mechanism. The value
079: * of this property is used to construct the default package name
080: * for the default optimized local protocol as follows:
081: * <IMPL_PACKAGE>.monitor.<LOCAL_PROTOCOL>
082: * This property is not expected to be set under normal circumstances.
083: */
084: private static final String LOCAL_PROTOCOL_PROP_NAME = "sun.jvmstat.monitor.local";
085: private static final String LOCAL_PROTOCOL = System.getProperty(
086: LOCAL_PROTOCOL_PROP_NAME, "local");
087:
088: /*
089: * The default remote protocol override mechanism. The value of
090: * this property is used to construct the default package name
091: * for the default remote protocol protocol as follows:
092: * <IMPL_PACKAGE>.monitor.protocol.<REMOTE_PROTOCOL>
093: * This property is not expected to be set under normal circumstances.
094: */
095: private static final String REMOTE_PROTOCOL_PROP_NAME = "sun.jvmstat.monitor.remote";
096: private static final String REMOTE_PROTOCOL = System.getProperty(
097: REMOTE_PROTOCOL_PROP_NAME, "rmi");
098:
099: /*
100: * The default class name of the MonitoredHost implementation subclass.
101: * There is no override mechanism for this variable, other than the
102: * IMPL_OVERRIDE_PROP_NAME override, which is larger in scope. A concrete
103: * instance of this class is expected to be found in:
104: * <IMPL_PACKAGE>.monitor.protocol.<protocol>.<MONITORED_HOST_CLASS>
105: */
106: private static final String MONITORED_HOST_CLASS = "MonitoredHostProvider";
107:
108: /**
109: * The HostIdentifier for this MonitoredHost instance.
110: */
111: protected HostIdentifier hostId;
112:
113: /**
114: * The polling interval, in milliseconds, for this MonitoredHost instance.
115: */
116: protected int interval;
117:
118: /**
119: * The last Exception encountered while polling this MonitoredHost.
120: */
121: protected Exception lastException;
122:
123: /**
124: * Factory method to construct MonitoredHost instances to manage
125: * connections to the host indicated by <tt>hostIdString</tt>
126: *
127: * @param hostIdString a String representation of a {@link HostIdentifier}
128: * @return MonitoredHost - the MonitoredHost instance for communicating
129: * with the indicated host using the protocol
130: * specified in hostIdString.
131: * @throws MonitorException Thrown if monitoring errors occur.
132: * @throws URISyntaxException Thrown when the hostIdString is poorly
133: * formed. This exception may get encapsulated
134: * into MonitorException in a future revision.
135: */
136: public static MonitoredHost getMonitoredHost(String hostIdString)
137: throws MonitorException, URISyntaxException {
138: HostIdentifier hostId = new HostIdentifier(hostIdString);
139: return getMonitoredHost(hostId);
140: }
141:
142: /**
143: * Factory method to construct a MonitoredHost instance to manage the
144: * connection to the Java Virtual Machine indicated by <tt>vmid</tt>.
145: *
146: * This method provide a convenient short cut for attaching to a specific
147: * instrumented Java Virtual Machine. The information in the VmIdentifier
148: * is used to construct a corresponding HostIdentifier, which in turn is
149: * used to create the MonitoredHost instance.
150: *
151: * @param vmid The identifier for the target Java Virtual Machine.
152: * @return MonitoredHost - The MonitoredHost object needed to attach to
153: * the target Java Virtual Machine.
154: *
155: * @throws MonitorException Thrown if monitoring errors occur.
156: */
157: public static MonitoredHost getMonitoredHost(VmIdentifier vmid)
158: throws MonitorException {
159: // use the VmIdentifier to construct the corresponding HostIdentifier
160: HostIdentifier hostId = new HostIdentifier(vmid);
161: return getMonitoredHost(hostId);
162: }
163:
164: /**
165: * Factory method to construct a MonitoredHost instance to manage the
166: * connection to the host indicated by <tt>hostId</tt>.
167: *
168: * @param hostId the identifier for the target host.
169: * @return MonitoredHost - The MonitoredHost object needed to attach to
170: * the target host.
171: *
172: * @throws MonitorException Thrown if monitoring errors occur.
173: */
174: public static MonitoredHost getMonitoredHost(HostIdentifier hostId)
175: throws MonitorException {
176: /*
177: * determine the class name to load. If the system property is set,
178: * use the indicated class. otherwise, use the default class.
179: */
180: String classname = System.getProperty(IMPL_OVERRIDE_PROP_NAME);
181: MonitoredHost mh = null;
182:
183: synchronized (monitoredHosts) {
184: mh = monitoredHosts.get(hostId);
185: if (mh != null) {
186: if (mh.isErrored()) {
187: monitoredHosts.remove(hostId);
188: } else {
189: return mh;
190: }
191: }
192: }
193:
194: hostId = resolveHostId(hostId);
195:
196: if (classname == null) {
197: // construct the class name
198: classname = IMPL_PACKAGE + ".monitor.protocol."
199: + hostId.getScheme() + "." + MONITORED_HOST_CLASS;
200: }
201:
202: try {
203: // run the constructor taking a single String parameter.
204: Class<?> c = Class.forName(classname);
205:
206: Constructor cons = c.getConstructor(new Class[] { hostId
207: .getClass() });
208:
209: mh = (MonitoredHost) cons
210: .newInstance(new Object[] { hostId });
211:
212: synchronized (monitoredHosts) {
213: monitoredHosts.put(mh.hostId, mh);
214: }
215: return mh;
216: } catch (ClassNotFoundException e) {
217: // from Class.forName();
218: throw new IllegalArgumentException("Could not find "
219: + classname + ": " + e.getMessage(), e);
220: } catch (NoSuchMethodException e) {
221: // from Class.getConstructor();
222: throw new IllegalArgumentException(
223: "Expected constructor missing in " + classname
224: + ": " + e.getMessage(), e);
225: } catch (IllegalAccessException e) {
226: // from Constructor.newInstance()
227: throw new IllegalArgumentException(
228: "Unexpected constructor access in " + classname
229: + ": " + e.getMessage(), e);
230: } catch (InstantiationException e) {
231: throw new IllegalArgumentException(classname
232: + "is abstract: " + e.getMessage(), e);
233: } catch (InvocationTargetException e) {
234: Throwable cause = e.getCause();
235: if (cause instanceof MonitorException) {
236: throw (MonitorException) cause;
237: }
238: throw new RuntimeException("Unexpected exception", e);
239: }
240: }
241:
242: /**
243: * Method to resolve unspecified components of the given HostIdentifier
244: * by constructing a new HostIdentifier that replaces the unspecified
245: * components with the default values.
246: *
247: * @param hostId the unresolved HostIdentifier.
248: * @return HostIdentifier - a resolved HostIdentifier.
249: *
250: * @throws MonitorException Thrown if monitoring errors occur.
251: */
252: protected static HostIdentifier resolveHostId(HostIdentifier hostId)
253: throws MonitorException {
254: String hostname = hostId.getHost();
255: String scheme = hostId.getScheme();
256: StringBuffer sb = new StringBuffer();
257:
258: assert hostname != null;
259:
260: if (scheme == null) {
261: if (hostname.compareTo("localhost") == 0) {
262: scheme = LOCAL_PROTOCOL;
263: } else {
264: scheme = REMOTE_PROTOCOL;
265: }
266: }
267:
268: sb.append(scheme).append(":").append(
269: hostId.getSchemeSpecificPart());
270:
271: String frag = hostId.getFragment();
272: if (frag != null) {
273: sb.append("#").append(frag);
274: }
275:
276: try {
277: return new HostIdentifier(sb.toString());
278: } catch (URISyntaxException e) {
279: // programming error - HostIdentifier was valid.
280: assert false;
281: throw new IllegalArgumentException(
282: "Malformed URI created: " + sb.toString());
283: }
284: }
285:
286: /**
287: * Return the resolved HostIdentifier for this MonitoredHost.
288: *
289: * @return HostIdentifier - the resolved HostIdentifier.
290: */
291: public HostIdentifier getHostIdentifier() {
292: return hostId;
293: }
294:
295: /* ---- Methods to support polled MonitoredHost Implementations ----- */
296:
297: /**
298: * Set the polling interval for this MonitoredHost.
299: *
300: * @param interval the polling interval, in milliseconds
301: */
302: public void setInterval(int interval) {
303: this .interval = interval;
304: }
305:
306: /**
307: * Get the polling interval.
308: *
309: * @return int - the polling interval in milliseconds for this MonitoredHost
310: */
311: public int getInterval() {
312: return interval;
313: }
314:
315: /**
316: * Set the last exception encountered while polling this MonitoredHost.
317: *
318: * @param lastException the last exception encountered;
319: */
320: public void setLastException(Exception lastException) {
321: this .lastException = lastException;
322: }
323:
324: /**
325: * Get the last exception encountered while polling this MonitoredHost.
326: *
327: * @return Exception - the last exception occurred while polling this
328: * MonitoredHost, or <tt>null</tt> if no exception
329: * has occurred or the exception has been cleared,
330: */
331: public Exception getLastException() {
332: return lastException;
333: }
334:
335: /**
336: * Clear the last exception.
337: */
338: public void clearLastException() {
339: lastException = null;
340: }
341:
342: /**
343: * Test if this MonitoredHost is in the errored state. If this method
344: * returns true, then the Exception returned by getLastException()
345: * indicates the Exception that caused the error condition.
346: *
347: * @return boolean - true if the MonitoredHost instance has experienced
348: * an error, or false if it hasn't or if any past
349: * error has been cleared.
350: */
351: public boolean isErrored() {
352: return lastException != null;
353: }
354:
355: /**
356: * Get the MonitoredVm for the given Java Virtual Machine. The default
357: * sampling interval is used for the MonitoredVm instance.
358: *
359: * @param id the VmIdentifier specifying the target Java Virtual Machine.
360: * @return MonitoredVm - the MonitoredVm instance for the target Java
361: * Virtual Machine.
362: * @throws MonitorException Thrown if monitoring errors occur.
363: */
364: public abstract MonitoredVm getMonitoredVm(VmIdentifier id)
365: throws MonitorException;
366:
367: /**
368: * Get the MonitoredVm for the given Java Virtual Machine. The sampling
369: * interval is set to the given interval.
370: *
371: * @param id the VmIdentifier specifying the target Java Virtual Machine.
372: * @param interval the sampling interval for the target Java Virtual Machine.
373: * @return MonitoredVm - the MonitoredVm instance for the target Java
374: * Virtual Machine.
375: * @throws MonitorException Thrown if monitoring errors occur.
376: */
377: public abstract MonitoredVm getMonitoredVm(VmIdentifier id,
378: int interval) throws MonitorException;
379:
380: /**
381: * Detach from the indicated MonitoredVm.
382: *
383: * @param vm the monitored Java Virtual Machine.
384: * @throws MonitorException Thrown if monitoring errors occur.
385: */
386: public abstract void detach(MonitoredVm vm) throws MonitorException;
387:
388: /**
389: * Add a HostListener. The given listener is added to the list
390: * of HostListener objects to be notified of MonitoredHost related events.
391: *
392: * @param listener the HostListener to add.
393: * @throws MonitorException Thrown if monitoring errors occur.
394: */
395: public abstract void addHostListener(HostListener listener)
396: throws MonitorException;
397:
398: /**
399: * Remove a HostListener. The given listener is removed from the list
400: * of HostListener objects to be notified of MonitoredHost related events.
401: *
402: * @param listener the HostListener to add.
403: * @throws MonitorException Thrown if monitoring errors occur.
404: */
405: public abstract void removeHostListener(HostListener listener)
406: throws MonitorException;
407:
408: /**
409: * Return the current set of active Java Virtual Machines for this
410: * MonitoredHost. The returned Set contains {@link Integer} instances
411: * holding the local virtual machine identifier, or <em>lvmid</em>
412: * for each instrumented Java Virtual Machine currently available.
413: *
414: * @return Set - the current set of active Java Virtual Machines associated
415: * with this MonitoredHost, or the empty set of none.
416: * @throws MonitorException Thrown if monitoring errors occur.
417: */
418: public abstract Set<Integer> activeVms() throws MonitorException;
419: }
|