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