JConsolePlugin.java revision 13430:5e8370fb3ed9
1/* 2 * Copyright (c) 2006, 2013, 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 com.sun.tools.jconsole; 27 28import java.beans.PropertyChangeEvent; 29import java.beans.PropertyChangeListener; 30import java.util.ArrayList; 31import java.util.List; 32import javax.swing.JPanel; 33import javax.swing.SwingWorker; 34 35/** 36 * A JConsole plugin class. JConsole uses the 37 * <a href="{@docRoot}/../../../../api/java/util/ServiceLoader.html"> 38 * service provider</a> mechanism to search the JConsole plugins. 39 * Users can provide their JConsole plugins in a jar file 40 * containing a file named 41 * 42 * <blockquote><pre> 43 * META-INF/services/com.sun.tools.jconsole.JConsolePlugin</pre></blockquote> 44 * 45 * <p> This file contains one line for each plugin, for example, 46 * 47 * <blockquote><pre> 48 * com.sun.example.JTop</pre></blockquote> 49 * <p> which is the fully qualified class name of the class implementing 50 * {@code JConsolePlugin}. 51 * 52 * <p> To load the JConsole plugins in JConsole, run: 53 * 54 * <blockquote><pre> 55 * jconsole -pluginpath <plugin-path> </pre></blockquote> 56 * 57 * <p> where {@code <plugin-path>} specifies the paths of JConsole 58 * plugins to look up which can be a directory or a jar file. Multiple 59 * paths are separated by the path separator character of the platform. 60 * 61 * <p> When a new JConsole window is created for a connection, 62 * an instance of each {@code JConsolePlugin} will be created. 63 * The {@code JConsoleContext} object is not available at its 64 * construction time. 65 * JConsole will set the {@link JConsoleContext} object for 66 * a plugin after the plugin object is created. It will then 67 * call its {@link #getTabs getTabs} method and add the returned 68 * tabs to the JConsole window. 69 * 70 * @see <a href="{@docRoot}/../../../../api/java/util/ServiceLoader.html"> 71 * java.util.ServiceLoader</a> 72 * 73 * @since 1.6 74 */ 75public abstract class JConsolePlugin { 76 private volatile JConsoleContext context = null; 77 private List<PropertyChangeListener> listeners = null; 78 79 /** 80 * Constructor. 81 */ 82 protected JConsolePlugin() { 83 } 84 85 /** 86 * Sets the {@link JConsoleContext JConsoleContext} object representing 87 * the connection to an application. This method will be called 88 * only once after the plugin is created and before the {@link #getTabs} 89 * is called. The given {@code context} can be in any 90 * {@link JConsoleContext#getConnectionState connection state} when 91 * this method is called. 92 * 93 * @param context a {@code JConsoleContext} object 94 */ 95 public final synchronized void setContext(JConsoleContext context) { 96 this.context = context; 97 if (listeners != null) { 98 for (PropertyChangeListener l : listeners) { 99 context.addPropertyChangeListener(l); 100 } 101 // throw away the listener list 102 listeners = null; 103 } 104 } 105 106 /** 107 * Returns the {@link JConsoleContext JConsoleContext} object representing 108 * the connection to an application. This method may return {@code null} 109 * if it is called before the {@link #setContext context} is initialized. 110 * 111 * @return the {@link JConsoleContext JConsoleContext} object representing 112 * the connection to an application. 113 */ 114 public final JConsoleContext getContext() { 115 return context; 116 } 117 118 /** 119 * Returns the tabs to be added in JConsole window. 120 * <p> 121 * The returned map contains one entry for each tab 122 * to be added in the tabbed pane in a JConsole window with 123 * the tab name as the key 124 * and the {@link JPanel} object as the value. 125 * This method returns an empty map if no tab is added by this plugin. 126 * This method will be called from the <i>Event Dispatch Thread</i> 127 * once at the new connection time. 128 * 129 * @return a map of a tab name and a {@link JPanel} object 130 * representing the tabs to be added in the JConsole window; 131 * or an empty map. 132 */ 133 public abstract java.util.Map<String, JPanel> getTabs(); 134 135 /** 136 * Returns a {@link SwingWorker} to perform 137 * the GUI update for this plugin at the same interval 138 * as JConsole updates the GUI. 139 * <p> 140 * JConsole schedules the GUI update at an interval specified 141 * for a connection. This method will be called at every 142 * update to obtain a {@code SwingWorker} for each plugin. 143 * <p> 144 * JConsole will invoke the {@link SwingWorker#execute execute()} 145 * method to schedule the returned {@code SwingWorker} for execution 146 * if: 147 * <ul> 148 * <li> the {@code SwingWorker} object has not been executed 149 * (i.e. the {@link SwingWorker#getState} method 150 * returns {@link javax.swing.SwingWorker.StateValue#PENDING PENDING} 151 * state); and</li> 152 * <li> the {@code SwingWorker} object returned in the previous 153 * update has completed the task if it was not {@code null} 154 * (i.e. the {@link SwingWorker#isDone SwingWorker.isDone} method 155 * returns {@code true}).</li> 156 * </ul> 157 * <br> 158 * Otherwise, {@code SwingWorker} object will not be scheduled to work. 159 * 160 * <p> 161 * A plugin can schedule its own GUI update and this method 162 * will return {@code null}. 163 * 164 * @return a {@code SwingWorker} to perform the GUI update; or 165 * {@code null}. 166 */ 167 public abstract SwingWorker<?,?> newSwingWorker(); 168 169 /** 170 * Dispose this plugin. This method is called by JConsole to inform 171 * that this plugin will be discarded and that it should free 172 * any resources that it has allocated. 173 * The {@link #getContext JConsoleContext} can be in any 174 * {@link JConsoleContext#getConnectionState connection state} when 175 * this method is called. 176 */ 177 public void dispose() { 178 // Default nop implementation 179 } 180 181 /** 182 * Adds a {@link PropertyChangeListener PropertyChangeListener} 183 * to the {@link #getContext JConsoleContext} object for this plugin. 184 * This method is a convenient method for this plugin to register 185 * a listener when the {@code JConsoleContext} object may or 186 * may not be available. 187 * 188 * <p>For example, a plugin constructor can 189 * call this method to register a listener to listen to the 190 * {@link JConsoleContext.ConnectionState connectionState} 191 * property changes and the listener will be added to the 192 * {@link JConsoleContext#addPropertyChangeListener JConsoleContext} 193 * object when it is available. 194 * 195 * @param listener The {@code PropertyChangeListener} to be added 196 * 197 * @throws NullPointerException if {@code listener} is {@code null}. 198 */ 199 public final void addContextPropertyChangeListener(PropertyChangeListener listener) { 200 if (listener == null) { 201 throw new NullPointerException("listener is null"); 202 } 203 204 if (context == null) { 205 // defer registration of the listener until setContext() is called 206 synchronized (this) { 207 // check again if context is not set 208 if (context == null) { 209 // maintain a listener list to be added later 210 if (listeners == null) { 211 listeners = new ArrayList<PropertyChangeListener>(); 212 } 213 listeners.add(listener); 214 return; 215 } 216 } 217 } 218 context.addPropertyChangeListener(listener); 219 } 220 221 /** 222 * Removes a {@link PropertyChangeListener PropertyChangeListener} 223 * from the listener list of the {@link #getContext JConsoleContext} 224 * object for this plugin. 225 * If {@code listener} was never added, no exception is 226 * thrown and no action is taken. 227 * 228 * @param listener the {@code PropertyChangeListener} to be removed 229 * 230 * @throws NullPointerException if {@code listener} is {@code null}. 231 */ 232 public final void removeContextPropertyChangeListener(PropertyChangeListener listener) { 233 if (listener == null) { 234 throw new NullPointerException("listener is null"); 235 } 236 237 if (context == null) { 238 // defer registration of the listener until setContext() is called 239 synchronized (this) { 240 // check again if context is not set 241 if (context == null) { 242 if (listeners != null) { 243 listeners.remove(listener); 244 } 245 return; 246 } 247 } 248 } 249 context.removePropertyChangeListener(listener); 250 } 251} 252