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
26package sun.jvmstat.monitor;
27
28import java.net.URISyntaxException;
29import java.util.HashMap;
30import java.util.Map;
31import java.util.ServiceLoader;
32import java.util.Set;
33
34import 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 */
52public 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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
275    public Exception getLastException() {
276        return lastException;
277    }
278
279    /**
280     * Clear the last exception.
281     */
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     */
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     */
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     */
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     */
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     */
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     */
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     */
362    public abstract Set<Integer> activeVms() throws MonitorException;
363}
364