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 26 package com.sun.tools.jconsole; 27 28 import java.beans.PropertyChangeEvent; 29 import java.beans.PropertyChangeListener; 30 import java.util.ArrayList; 31 import java.util.List; 32 import javax.swing.JPanel; 33 import 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 */ 75 @jdk.Exported 76 public abstract class JConsolePlugin { 77 private volatile JConsoleContext context = null; 78 private List<PropertyChangeListener> listeners = null; 79 80 /** 81 * Constructor. 82 */ 83 protected JConsolePlugin() { 84 } 85 86 /** 87 * Sets the {@link JConsoleContext JConsoleContext} object representing 88 * the connection to an application. This method will be called 89 * only once after the plugin is created and before the {@link #getTabs} 90 * is called. The given {@code context} can be in any 91 * {@link JConsoleContext#getConnectionState connection state} when 92 * this method is called. 93 * 94 * @param context a {@code JConsoleContext} object 95 */ 96 public final synchronized void setContext(JConsoleContext context) { 97 this.context = context; 98 if (listeners != null) { 99 for (PropertyChangeListener l : listeners) { 100 context.addPropertyChangeListener(l); 101 } 102 // throw away the listener list 103 listeners = null; 104 } 105 } 106 107 /** 108 * Returns the {@link JConsoleContext JConsoleContext} object representing 109 * the connection to an application. This method may return {@code null} 110 * if it is called before the {@link #setContext context} is initialized. 111 * 112 * @return the {@link JConsoleContext JConsoleContext} object representing 113 * the connection to an application. 114 */ 115 public final JConsoleContext getContext() { 116 return context; 117 } 118 119 /** 120 * Returns the tabs to be added in JConsole window. 121 * <p> 122 * The returned map contains one entry for each tab 123 * to be added in the tabbed pane in a JConsole window with 124 * the tab name as the key 125 * and the {@link JPanel} object as the value. 126 * This method returns an empty map if no tab is added by this plugin. 127 * This method will be called from the <i>Event Dispatch Thread</i> 128 * once at the new connection time. 129 * 130 * @return a map of a tab name and a {@link JPanel} object 131 * representing the tabs to be added in the JConsole window; 132 * or an empty map. 133 */ 134 public abstract java.util.Map<String, JPanel> getTabs(); 135 136 /** 137 * Returns a {@link SwingWorker} to perform 138 * the GUI update for this plugin at the same interval 139 * as JConsole updates the GUI. 140 * <p> 141 * JConsole schedules the GUI update at an interval specified 142 * for a connection. This method will be called at every 143 * update to obtain a {@code SwingWorker} for each plugin. 144 * <p> 145 * JConsole will invoke the {@link SwingWorker#execute execute()} 146 * method to schedule the returned {@code SwingWorker} for execution 147 * if: 148 * <ul> 149 * <li> the {@code SwingWorker} object has not been executed 150 * (i.e. the {@link SwingWorker#getState} method 151 * returns {@link javax.swing.SwingWorker.StateValue#PENDING PENDING} 152 * state); and</li> 153 * <li> the {@code SwingWorker} object returned in the previous 154 * update has completed the task if it was not {@code null} 155 * (i.e. the {@link SwingWorker#isDone SwingWorker.isDone} method 156 * returns {@code true}).</li> 157 * </ul> 158 * <br> 159 * Otherwise, {@code SwingWorker} object will not be scheduled to work. 160 * 161 * <p> 162 * A plugin can schedule its own GUI update and this method 163 * will return {@code null}. 164 * 165 * @return a {@code SwingWorker} to perform the GUI update; or 166 * {@code null}. 167 */ 168 public abstract SwingWorker<?,?> newSwingWorker(); 169 170 /** 171 * Dispose this plugin. This method is called by JConsole to inform 172 * that this plugin will be discarded and that it should free 173 * any resources that it has allocated. 174 * The {@link #getContext JConsoleContext} can be in any 175 * {@link JConsoleContext#getConnectionState connection state} when 176 * this method is called. 177 */ 178 public void dispose() { 179 // Default nop implementation 180 } 181 182 /** 183 * Adds a {@link PropertyChangeListener PropertyChangeListener} 184 * to the {@link #getContext JConsoleContext} object for this plugin. 185 * This method is a convenient method for this plugin to register 186 * a listener when the {@code JConsoleContext} object may or 187 * may not be available. 188 * 189 * <p>For example, a plugin constructor can 190 * call this method to register a listener to listen to the 191 * {@link JConsoleContext.ConnectionState connectionState} 192 * property changes and the listener will be added to the 193 * {@link JConsoleContext#addPropertyChangeListener JConsoleContext} 194 * object when it is available. 195 * 196 * @param listener The {@code PropertyChangeListener} to be added 197 * 198 * @throws NullPointerException if {@code listener} is {@code null}. 199 */ 200 public final void addContextPropertyChangeListener(PropertyChangeListener listener) { 201 if (listener == null) { 202 throw new NullPointerException("listener is null"); 203 } 204 205 if (context == null) { 206 // defer registration of the listener until setContext() is called 207 synchronized (this) { 208 // check again if context is not set 209 if (context == null) { 210 // maintain a listener list to be added later 211 if (listeners == null) { 212 listeners = new ArrayList<PropertyChangeListener>(); 213 } 214 listeners.add(listener); 215 return; 216 } 217 } 218 } 219 context.addPropertyChangeListener(listener); 220 } 221 222 /** 223 * Removes a {@link PropertyChangeListener PropertyChangeListener} 224 * from the listener list of the {@link #getContext JConsoleContext} 225 * object for this plugin. 226 * If {@code listener} was never added, no exception is 227 * thrown and no action is taken. 228 * 229 * @param listener the {@code PropertyChangeListener} to be removed 230 * 231 * @throws NullPointerException if {@code listener} is {@code null}. 232 */ 233 public final void removeContextPropertyChangeListener(PropertyChangeListener listener) { 234 if (listener == null) { 235 throw new NullPointerException("listener is null"); 236 } 237 238 if (context == null) { 239 // defer registration of the listener until setContext() is called 240 synchronized (this) { 241 // check again if context is not set 242 if (context == null) { 243 if (listeners != null) { 244 listeners.remove(listener); 245 } 246 return; 247 } 248 } 249 } 250 context.removePropertyChangeListener(listener); 251 } 252 }