1 /*
2  * Copyright (c) 2004, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.jvmstat.monitor;
27 
28 import java.net.URISyntaxException;
29 import java.util.HashMap;
30 import java.util.Map;
31 import java.util.ServiceLoader;
32 import java.util.Set;
33 
34 import sun.jvmstat.monitor.event.HostListener;
35 
36 /**
37  * An abstraction for a host that contains instrumented Java Virtual
38  * Machines. The class provides abstract factory methods for creating
39  * concrete instances of this class and factory methods for creating
40  * {@link MonitoredVm} instances. Concrete implementations of this class
41  * provide methods for managing the communications protocols and provide
42  * for event notification.
43  *
44  * @author Brian Doherty
45  * @since 1.5
46  *
47  * @see HostIdentifier
48  * @see VmIdentifier
49  * @see MonitoredVm
50  * @see HostListener
51  */
52 public abstract class MonitoredHost {
53     private static Map<HostIdentifier, MonitoredHost> monitoredHosts =
54                 new HashMap<HostIdentifier, MonitoredHost>();
55 
56     /*
57      * The default optimized local protocol override mechanism. The value
58      * of this property is used to construct the default package name
59      * for the default optimized local protocol as follows:
60      *        <IMPL_PACKAGE>.monitor.<LOCAL_PROTOCOL>
61      * This property is not expected to be set under normal circumstances.
62      */
63     private static final String LOCAL_PROTOCOL_PROP_NAME =
64             "sun.jvmstat.monitor.local";
65     private static final String LOCAL_PROTOCOL =
66             System.getProperty(LOCAL_PROTOCOL_PROP_NAME, "local");
67 
68     /*
69      * The default remote protocol override mechanism. The value of
70      * this property is used to construct the default package name
71      * for the default remote protocol protocol as follows:
72      *        <IMPL_PACKAGE>.monitor.protocol.<REMOTE_PROTOCOL>
73      * This property is not expected to be set under normal circumstances.
74      */
75     private static final String REMOTE_PROTOCOL_PROP_NAME =
76             "sun.jvmstat.monitor.remote";
77     private static final String REMOTE_PROTOCOL =
78             System.getProperty(REMOTE_PROTOCOL_PROP_NAME, "rmi");
79 
80     /**
81      * The HostIdentifier for this MonitoredHost instance.
82      */
83     protected HostIdentifier hostId;
84 
85     /**
86      * The polling interval, in milliseconds, for this MonitoredHost instance.
87      */
88     protected int interval;
89 
90     /**
91      * The last Exception encountered while polling this MonitoredHost.
92      */
93     protected Exception lastException;
94 
95     /**
96      * Factory method to construct MonitoredHost instances to manage
97      * connections to the host indicated by {@code hostIdString}
98      *
99      * @param hostIdString a String representation of a {@link HostIdentifier}
100      * @return MonitoredHost - the MonitoredHost instance for communicating
101      *                         with the indicated host using the protocol
102      *                         specified in hostIdString.
103      * @throws MonitorException  Thrown if monitoring errors occur.
104      * @throws URISyntaxException Thrown when the hostIdString is poorly
105      *                            formed. This exception may get encapsulated
106      *                            into MonitorException in a future revision.
107      */
getMonitoredHost(String hostIdString)108     public static MonitoredHost getMonitoredHost(String hostIdString)
109                   throws MonitorException, URISyntaxException {
110         HostIdentifier hostId = new HostIdentifier(hostIdString);
111         return getMonitoredHost(hostId);
112     }
113 
114     /**
115      * Factory method to construct a MonitoredHost instance to manage the
116      * connection to the Java Virtual Machine indicated by {@code vmid}.
117      *
118      * This method provide a convenient short cut for attaching to a specific
119      * instrumented Java Virtual Machine. The information in the VmIdentifier
120      * is used to construct a corresponding HostIdentifier, which in turn is
121      * used to create the MonitoredHost instance.
122      *
123      * @param vmid The identifier for the target Java Virtual Machine.
124      * @return MonitoredHost - The MonitoredHost object needed to attach to
125      *                         the target Java Virtual Machine.
126      *
127      * @throws MonitorException Thrown if monitoring errors occur.
128      */
getMonitoredHost(VmIdentifier vmid)129     public static MonitoredHost getMonitoredHost(VmIdentifier vmid)
130                  throws MonitorException {
131         // use the VmIdentifier to construct the corresponding HostIdentifier
132         HostIdentifier hostId = new HostIdentifier(vmid);
133         return getMonitoredHost(hostId);
134     }
135 
136 
137     /*
138      * Load the MonitoredHostServices
139      */
140     private static ServiceLoader<MonitoredHostService> monitoredHostServiceLoader =
141         ServiceLoader.load(MonitoredHostService.class, MonitoredHostService.class.getClassLoader());
142 
143     /**
144      * Factory method to construct a MonitoredHost instance to manage the
145      * connection to the host indicated by {@code hostId}.
146      *
147      * @param hostId the identifier for the target host.
148      * @return MonitoredHost - The MonitoredHost object needed to attach to
149      *                         the target host.
150      *
151      * @throws MonitorException Thrown if monitoring errors occur.
152      */
getMonitoredHost(HostIdentifier hostId)153     public static MonitoredHost getMonitoredHost(HostIdentifier hostId)
154                   throws MonitorException {
155         MonitoredHost mh = null;
156 
157         synchronized(monitoredHosts) {
158             mh = monitoredHosts.get(hostId);
159             if (mh != null) {
160                 if (mh.isErrored()) {
161                     monitoredHosts.remove(hostId);
162                 } else {
163                     return mh;
164                 }
165             }
166         }
167 
168         hostId = resolveHostId(hostId);
169 
170         for (MonitoredHostService mhs : monitoredHostServiceLoader) {
171             if (mhs.getScheme().equals(hostId.getScheme())) {
172                 mh = mhs.getMonitoredHost(hostId);
173             }
174         }
175 
176         if (mh == null) {
177             throw new IllegalArgumentException("Could not find MonitoredHost for scheme: " + hostId.getScheme());
178         }
179 
180         synchronized(monitoredHosts) {
181             monitoredHosts.put(mh.hostId, mh);
182         }
183 
184         return mh;
185     }
186 
187     /**
188      * Method to resolve unspecified components of the given HostIdentifier
189      * by constructing a new HostIdentifier that replaces the unspecified
190      * components with the default values.
191      *
192      * @param hostId the unresolved HostIdentifier.
193      * @return HostIdentifier - a resolved HostIdentifier.
194      *
195      * @throws MonitorException Thrown if monitoring errors occur.
196      */
resolveHostId(HostIdentifier hostId)197     protected static HostIdentifier resolveHostId(HostIdentifier hostId)
198                      throws MonitorException {
199         String hostname = hostId.getHost();
200         String scheme = hostId.getScheme();
201         StringBuilder sb = new StringBuilder();
202 
203         assert hostname != null;
204 
205         if (scheme == null) {
206             if (hostname.compareTo("localhost") == 0) {
207                 scheme = LOCAL_PROTOCOL;
208             } else {
209                 scheme = REMOTE_PROTOCOL;
210             }
211         }
212 
213         sb.append(scheme).append(":").append(hostId.getSchemeSpecificPart());
214 
215         String frag = hostId.getFragment();
216         if (frag != null) {
217             sb.append("#").append(frag);
218         }
219 
220         try {
221             return new HostIdentifier(sb.toString());
222         } catch (URISyntaxException e) {
223             // programming error - HostIdentifier was valid.
224             assert false;
225             throw new IllegalArgumentException("Malformed URI created: "
226                                                + sb.toString());
227         }
228     }
229 
230     /**
231      * Return the resolved HostIdentifier for this MonitoredHost.
232      *
233      * @return HostIdentifier - the resolved HostIdentifier.
234      */
getHostIdentifier()235     public HostIdentifier getHostIdentifier() {
236         return hostId;
237     }
238 
239     /* ---- Methods to support polled MonitoredHost Implementations ----- */
240 
241     /**
242      * Set the polling interval for this MonitoredHost.
243      *
244      * @param interval the polling interval, in milliseconds
245      */
setInterval(int interval)246     public void setInterval(int interval) {
247         this.interval = interval;
248     }
249 
250     /**
251      * Get the polling interval.
252      *
253      * @return int - the polling interval in milliseconds for this MonitoredHost
254      */
getInterval()255     public int getInterval() {
256         return interval;
257     }
258 
259     /**
260      * Set the last exception encountered while polling this MonitoredHost.
261      *
262      * @param lastException the last exception encountered;
263      */
setLastException(Exception lastException)264     public void setLastException(Exception lastException) {
265         this.lastException = lastException;
266     }
267 
268     /**
269      * Get the last exception encountered while polling this MonitoredHost.
270      *
271      * @return Exception - the last exception occurred while polling this
272      *                     MonitoredHost, or {@code null} if no exception
273      *                     has occurred or the exception has been cleared,
274      */
getLastException()275     public Exception getLastException() {
276         return lastException;
277     }
278 
279     /**
280      * Clear the last exception.
281      */
clearLastException()282     public void clearLastException() {
283         lastException = null;
284     }
285 
286     /**
287      * Test if this MonitoredHost is in the errored state. If this method
288      * returns true, then the Exception returned by getLastException()
289      * indicates the Exception that caused the error condition.
290      *
291      * @return boolean - true if the MonitoredHost instance has experienced
292      *                   an error, or false if it hasn't or if any past
293      *                   error has been cleared.
294      */
isErrored()295     public boolean isErrored() {
296         return lastException != null;
297     }
298 
299     /**
300      * Get the MonitoredVm for the given Java Virtual Machine. The default
301      * sampling interval is used for the MonitoredVm instance.
302      *
303      * @param id the VmIdentifier specifying the target Java Virtual Machine.
304      * @return MonitoredVm - the MonitoredVm instance for the target Java
305      *                       Virtual Machine.
306      * @throws MonitorException Thrown if monitoring errors occur.
307      */
getMonitoredVm(VmIdentifier id)308     public abstract MonitoredVm getMonitoredVm(VmIdentifier id)
309                                 throws MonitorException;
310 
311     /**
312      * Get the MonitoredVm for the given Java Virtual Machine. The sampling
313      * interval is set to the given interval.
314      *
315      * @param id the VmIdentifier specifying the target Java Virtual Machine.
316      * @param interval the sampling interval for the target Java Virtual Machine.
317      * @return MonitoredVm - the MonitoredVm instance for the target Java
318      *                       Virtual Machine.
319      * @throws MonitorException Thrown if monitoring errors occur.
320      */
getMonitoredVm(VmIdentifier id, int interval)321     public abstract MonitoredVm getMonitoredVm(VmIdentifier id, int interval)
322                                 throws MonitorException;
323 
324     /**
325      * Detach from the indicated MonitoredVm.
326      *
327      * @param vm the monitored Java Virtual Machine.
328      * @throws MonitorException Thrown if monitoring errors occur.
329      */
detach(MonitoredVm vm)330     public abstract void detach(MonitoredVm vm) throws MonitorException;
331 
332     /**
333      * Add a HostListener. The given listener is added to the list
334      * of HostListener objects to be notified of MonitoredHost related events.
335      *
336      * @param listener the HostListener to add.
337      * @throws MonitorException Thrown if monitoring errors occur.
338      */
addHostListener(HostListener listener)339     public abstract void addHostListener(HostListener listener)
340                          throws MonitorException;
341 
342     /**
343      * Remove a HostListener. The given listener is removed from the list
344      * of HostListener objects to be notified of MonitoredHost related events.
345      *
346      * @param listener the HostListener to add.
347      * @throws MonitorException Thrown if monitoring errors occur.
348      */
removeHostListener(HostListener listener)349     public abstract void removeHostListener(HostListener listener)
350                          throws MonitorException;
351 
352     /**
353      * Return the current set of active Java Virtual Machines for this
354      * MonitoredHost. The returned Set contains {@link Integer} instances
355      * holding the local virtual machine identifier, or <em>lvmid</em>
356      * for each instrumented Java Virtual Machine currently available.
357      *
358      * @return Set - the current set of active Java Virtual Machines associated
359      *               with this MonitoredHost, or the empty set of none.
360      * @throws MonitorException Thrown if monitoring errors occur.
361      */
activeVms()362     public abstract Set<Integer> activeVms() throws MonitorException;
363 }
364