/*
* Copyright (c) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.sun.tools.visualvm.modules.saplugin;
import com.sun.tools.visualvm.application.Application;
import com.sun.tools.visualvm.core.datasource.DataSource;
import com.sun.tools.visualvm.core.datasupport.DataRemovedListener;
import com.sun.tools.visualvm.core.datasupport.Stateful;
import com.sun.tools.visualvm.core.ui.DataSourceView;
import com.sun.tools.visualvm.core.ui.components.DataViewComponent;
import com.sun.tools.visualvm.coredump.CoreDump;
import java.beans.PropertyChangeEvent;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
import org.openide.util.Utilities;
import com.sun.tools.visualvm.tools.sa.SaModelFactory;
import java.awt.BorderLayout;
import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.HierarchyListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.beans.PropertyChangeListener;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.WeakListeners;
/**
*
* @author poonam
*/
public class SAView extends DataSourceView implements PropertyChangeListener, DataRemovedListener<DataSource>, ActionListener {
private DataViewComponent dvc;
DataSource dataSource;
SAModelImpl saModel = null;
private boolean isAttached = false;
MasterViewSupport master;
ThreadsView threadsView;
OopInspectorView oopInspector;
StackTraceViewer stackTraceViewer;
CodeViewer codeviewer;
FindPanel findPointer;
FindPanel findInHeap;
FindPanel findInCode;
//Reusing an image from the sources:
private static final String IMAGE_PATH = "com/sun/tools/visualvm/modules/saplugin/resources/SA.png"; // NOI18N
private String dsString = "Process";
private boolean doNotShowMessage = false;
public static boolean isClosing = false;
public SAView(DataSource ds) {
super(ds, "SA Plugin", new ImageIcon(Utilities.loadImage(IMAGE_PATH, true)).getImage(), 60, false);
dataSource = ds;
isAttached = false;
ds.notifyWhenRemoved(this);
ds.addPropertyChangeListener(Stateful.PROPERTY_STATE, WeakListeners.propertyChange(this, ds));
if (ds instanceof Application) {
dsString = "Process";
} else if (ds instanceof CoreDump){
dsString = "CoreDump";
}
}
public DataViewComponent getDataViewComponent() {
return dvc;
}
@Override
protected void removed() {
detachFromProcess(dataSource);
dataSource = null;
}
public void propertyChange(PropertyChangeEvent evt) {
dataRemoved(dataSource);
}
public void dataRemoved(DataSource ds) {
this.dataSource = null;
}
public void actionPerformed(ActionEvent e) {
throw new UnsupportedOperationException("Not supported yet.");
}
@Override
protected DataViewComponent createComponent() {
// attachToProcess(dataSource);
master = new MasterViewSupport(dataSource);
//Master view:
DataViewComponent.MasterView masterView = new DataViewComponent.MasterView("SA Plugin", null, master);
DataViewComponent.MasterViewConfiguration masterConfiguration =
new DataViewComponent.MasterViewConfiguration(false);
dvc = new DataViewComponent(masterView, masterConfiguration);
//addDetailsViews();
if (saModel != null)
saModel.setView(this);
return dvc;
}
private void addDetailsViews() {
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
//Add detail views to the component:
threadsView = new ThreadsView("Java Threads", 10);
stackTraceViewer = new StackTraceViewer("Java Stack Trace", 20);
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Java Threads / Java Stack Trace", true), DataViewComponent.TOP_LEFT);
dvc.addDetailsView(threadsView.getDetailsView(), DataViewComponent.TOP_LEFT);
dvc.addDetailsView(stackTraceViewer.getDetailsView(), DataViewComponent.TOP_LEFT);
oopInspector = new OopInspectorView(saModel, "Oop Inspector", 10);
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Oop Inspector", true), DataViewComponent.TOP_RIGHT);
dvc.addDetailsView(oopInspector.getDetailsView(), DataViewComponent.TOP_RIGHT);
codeviewer = new CodeViewer("Code Viewer", 10);
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Code Viewer", true), DataViewComponent.BOTTOM_LEFT);
dvc.addDetailsView(codeviewer.getDetailsView(), DataViewComponent.BOTTOM_LEFT);
findPointer = new FindPanel("Find Pointer", 10, 1);
findInHeap = new FindPanel("Find Value in Heap", 20, 2);
findInCode = new FindPanel("Find Value in CodeCache", 30, 3);
dvc.configureDetailsArea(new DataViewComponent.DetailsAreaConfiguration(
"Find Panel", true), DataViewComponent.BOTTOM_RIGHT);
dvc.addDetailsView(findPointer.getDetailsView(), DataViewComponent.BOTTOM_RIGHT);
dvc.addDetailsView(findInHeap.getDetailsView(), DataViewComponent.BOTTOM_RIGHT);
dvc.addDetailsView(findInCode.getDetailsView(), DataViewComponent.BOTTOM_RIGHT);
dvc.hideDetailsArea(DataViewComponent.BOTTOM_LEFT);
dvc.hideDetailsArea(DataViewComponent.BOTTOM_RIGHT);
dvc.setCursor(cur);
}
public OopInspectorView getOopInspectorView() {
return oopInspector;
}
public void updateOopInspectorView(Object oop) {
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
oopInspector.refresh(oop);
dvc.showDetailsArea(DataViewComponent.TOP_RIGHT);
dvc.selectDetailsView(oopInspector.getDetailsView());
dvc.setCursor(cur);
}
public void updateStackTraceView(Object thread) {
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
stackTraceViewer.refresh(thread);
dvc.selectDetailsView(stackTraceViewer.getDetailsView());
dvc.showDetailsArea(DataViewComponent.TOP_LEFT);
dvc.setCursor(cur);
}
public boolean attachToProcess(DataSource ds) {
if (isAttached) {
return true;
}
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
try {
saModel = (SAModelImpl) SaModelFactory.getSAAgentFor(ds);
isAttached = saModel.attach();
dvc.setCursor(cur);
return isAttached;
} catch (Exception e) {
e.printStackTrace();
NotifyDescriptor nd = new NotifyDescriptor.Message(e.getCause().toString(), NotifyDescriptor.INFORMATION_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
// System.out.println(e.getCause().toString());
// if (e.getCause().toString().contains("Windbg Error: AttachProcess failed!")) {
detachFromProcess(ds);
// }
}
dvc.setCursor(cur);
return false;
}
public void detachFromProcess(DataSource ds) {
try {
saModel = (SAModelImpl) SaModelFactory.getSAAgentFor(ds);
saModel.detach();
isAttached = false;
} catch (Exception e) {
e.printStackTrace();
}
}
public void refreshView(DataSource ds) {
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
threadsView.refresh();
oopInspector.refresh(null);
stackTraceViewer.refresh(null);
codeviewer.refresh();
findPointer.refresh();
findInHeap.refresh();
findInCode.refresh();
dvc.showDetailsArea(DataViewComponent.TOP_LEFT);
dvc.selectDetailsView(threadsView.getDetailsView());
dvc.hideDetailsArea(DataViewComponent.BOTTOM_LEFT);
dvc.hideDetailsArea(DataViewComponent.BOTTOM_RIGHT);
dvc.hideDetailsArea(DataViewComponent.TOP_RIGHT);
dvc.showDetailsArea(DataViewComponent.TOP_LEFT);
dvc.setCursor(cur);
}
public void removeDetailsViews(DataSource ds) {
threadsView.remove();
oopInspector.remove();
stackTraceViewer.remove();
codeviewer.remove();
findPointer.remove();
findInHeap.remove();
findInCode.remove();
dvc.hideDetailsArea(DataViewComponent.TOP_LEFT);
dvc.hideDetailsArea(DataViewComponent.TOP_RIGHT);
dvc.hideDetailsArea(DataViewComponent.BOTTOM_LEFT);
dvc.hideDetailsArea(DataViewComponent.BOTTOM_RIGHT);
}
// ----------Master View----------------------------
private class MasterViewSupport extends JPanel {
private JButton attachButton;
JLabel vmInformation;
DataSource dataSource;
boolean firstTimeShow = true;
public MasterViewSupport(DataSource ds) {
this.dataSource = ds;
saModel = (SAModelImpl) SaModelFactory.getSAAgentFor(dataSource);
initComponents(dataSource);
if (dataSource instanceof Application) {
addHierarchyListener(new HierarchyListener() {
public void hierarchyChanged(HierarchyEvent e) {
if ((e.getChangeFlags() & HierarchyEvent.SHOWING_CHANGED) != 0) {
if (isShowing()) {
/* if (!isAttached && (dataSource != null)) {
if (attachToProcess(dataSource)) {
if (firstTimeShow) {
try {
saModel.readData();
} catch (Exception ex) {
ex.printStackTrace();
}
addVMInfo(dataSource);
addDetailsViews();
firstTimeShow = false;
}
}
setAttachButtonText("Detach from process");
refreshView(dataSource);
} */
} else {/*
if (!dvc.isVisible())
return;
Component parent = dvc.getParent();
Component top = null;
while (parent != null) {
top = parent;
parent = top.getParent();
}
if (!top.isShowing())
return;
if (!top.isVisible())
return;
if (isAttached && (dataSource != null) && (doNotShowMessage == false) ) {
if (isClosing == false) {
MessagePanel messagePanel = new MessagePanel();
DialogDescriptor dd = new DialogDescriptor(messagePanel, "Warning!");
Object result = DialogDisplayer.getDefault().notify(dd);
if (result == NotifyDescriptor.OK_OPTION) {
detachFromProcess(dataSource);
removeDetailsViews(dataSource);
setAttachButtonText("Attach to " + dsString);
}
}
}*/
}
}
}
});
}
}
public DataViewComponent.MasterView getMasterView() {
return new DataViewComponent.MasterView("SAPlugin", null, this); // NOI18N
}
private void addVMInfo(final DataSource ds) {
JLabel label = new JLabel();
String vminfo = null;
vminfo = saModel.getVmVersion() + " " + saModel.getVmName() + " " + saModel.getVmInfo();
label.setText(vminfo);
add(label);
}
private void initComponents(final DataSource ds) {
setLayout(new BorderLayout());
setOpaque(false);
String buttonLabel = null;
if (isAttached) {
buttonLabel = "Detach from " + dsString;
} else {
buttonLabel = "Attach to " + dsString;
}
attachButton = new JButton(new AbstractAction(buttonLabel) {
public void actionPerformed(ActionEvent e) {
if (ds == null) {
return;
}
Cursor cur = dvc.getCursor();
dvc.setCursor(new Cursor(Cursor.WAIT_CURSOR));
if (isAttached) {
detachFromProcess(ds);
// removeWarning();
removeDetailsViews(ds);
setAttachButtonText("Attach to " + dsString);
} else {
if (attachToProcess(ds)) {
if (firstTimeShow) {
try {
saModel.readData();
} catch (Exception ex) {
ex.printStackTrace();
}
addVMInfo(dataSource);
addDetailsViews();
firstTimeShow = false;
}
refreshView(ds);
if (isAttached && (ds instanceof Application) && (doNotShowMessage == false)) {
if (isClosing == false) {
MessagePanel messagePanel = new MessagePanel();
//DialogDescriptor dd = new DialogDescriptor(messagePanel, "SAPlugin Warning !");
NotifyDescriptor nd = new NotifyDescriptor.Message((Object) messagePanel, NotifyDescriptor.INFORMATION_MESSAGE) ;
Object result = DialogDisplayer.getDefault().notify(nd);
/*if (result == NotifyDescriptor.OK_OPTION) {
detachFromProcess(dataSource);
removeDetailsViews(dataSource);
setAttachButtonText("Attach to " + dsString);
}
* */
}
}
setAttachButtonText("Detach from " + dsString);
}
}
dvc.setCursor(cur);
}
});
attachButton.setEnabled(true);
JPanel buttonsArea = new JPanel(new BorderLayout());
buttonsArea.setOpaque(false);
JPanel buttonsContainer = new JPanel(new BorderLayout(3, 0));
buttonsContainer.setOpaque(false);
buttonsContainer.setBorder(BorderFactory.createEmptyBorder(9, 5, 20, 10));
buttonsContainer.add(attachButton, BorderLayout.WEST);
buttonsArea.add(buttonsContainer, BorderLayout.NORTH);
add(buttonsArea, BorderLayout.EAST);
}
void setAttachButtonText(String s) {
if (attachButton != null) {
attachButton.setText(s);
}
}
}
//Details views
private class ThreadsView extends JComponent {
private JPanel threads;
private String caption;
private int position;
private DataViewComponent.DetailsView detailsView;
private Object saListener = null;
private JavaThreadsPanel threadsPanel = null;
public ThreadsView(String caption, int position) {
this.caption = caption;
this.position = position;
detailsView = null;
initComponents();
}
public DataViewComponent.DetailsView getDetailsView() {
if (detailsView == null) {
detailsView = new DataViewComponent.DetailsView(caption, null, position, this, null);
}
return detailsView;
}
public void refresh() {
this.removeAll();
threadsPanel = saModel.createJavaThreadsPanel();
if (threadsPanel != null) {
saListener = saModel.getSAListener();
threadsPanel.setListener(saListener);
threads = threadsPanel.getPanel();
add(threads, BorderLayout.CENTER);
}
}
private void initComponents() {
setLayout(new BorderLayout());
}
public void remove() {
// threadsPanel.removeListener(saListener);
this.removeAll();
}
}
private class CodeViewer extends JComponent {
private JPanel codeviewer;
private String caption;
private int position;
private DataViewComponent.DetailsView detailsView;
public CodeViewer(String caption, int position) {
this.caption = caption;
this.position = position;
initComponents();
}
public DataViewComponent.DetailsView getDetailsView() {
if (detailsView == null) {
detailsView = new DataViewComponent.DetailsView(caption, null, position, this, null);
}
return detailsView;
}
public void refresh() {
this.removeAll();
try {
codeviewer = saModel.createCodeViewerPanel().getPanel();
} catch (Throwable t) {
NotifyDescriptor nd = new NotifyDescriptor.Message("Code Viewer is not yet implemented for this platform", NotifyDescriptor.INFORMATION_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
dvc.removeDetailsView(detailsView);
return;
}
add(codeviewer, BorderLayout.CENTER);
}
private void initComponents() {
setLayout(new BorderLayout());
}
public void remove() {
this.removeAll();
}
}
private class FindPanel extends JComponent {
private JPanel findPanel;
private String caption;
private int position;
private int mode;
public FindPanel(String caption, int position, int mode) {
this.caption = caption;
this.position = position;
this.mode = mode;
initComponents();
}
public DataViewComponent.DetailsView getDetailsView() {
return new DataViewComponent.DetailsView(caption, null, position, this, null);
}
public void refresh() {
this.removeAll();
switch (mode) {
case 1:
findPanel = saModel.createFindPointerPanel().getPanel();
break;
case 2:
findPanel = saModel.createFindInHeapPanel().getPanel();
break;
case 3:
findPanel = saModel.createFindInCodeCachePanel().getPanel();
break;
}
add(findPanel, BorderLayout.CENTER);
}
private void initComponents() {
setLayout(new BorderLayout());
}
public void remove() {
this.removeAll();
}
}
private class StackTraceViewer extends JComponent {
private JPanel traceViewer;
private String caption;
private int position;
private DataViewComponent.DetailsView detailsView;
public StackTraceViewer(String caption, int position) {
this.caption = caption;
this.position = position;
detailsView = null;
initComponents();
}
public DataViewComponent.DetailsView getDetailsView() {
if (detailsView == null) {
detailsView = new DataViewComponent.DetailsView(caption, null, position, this, null);
}
return detailsView;
}
public void refresh(Object thread) {
this.removeAll();
try {
JavaStackTracePanel tracePanel = saModel.createJavaStackTracePanel();
if (thread != null) {
tracePanel.setJavaThread(thread);
}
traceViewer = tracePanel.getPanel();
} catch (Throwable t) {
NotifyDescriptor nd = new NotifyDescriptor.Message("Java Stack Trace Viewer is not yet implemented for this platform", NotifyDescriptor.INFORMATION_MESSAGE);
DialogDisplayer.getDefault().notify(nd);
dvc.removeDetailsView(detailsView);
return;
}
add(traceViewer, BorderLayout.CENTER);
}
private void initComponents() {
setLayout(new BorderLayout());
}
public void remove() {
this.removeAll();
}
}
public class MessagePanel extends JPanel implements ItemListener {
private String warning = new String("" +
"<html><br>  Other tabs for this process will not be usable as long as<br>" +
"  SAPlugin is attached to it. Please detach SAPlugin from<br>" +
"  the process before using other tabs.<br><br></html>");
public MessagePanel() {
initComponents();
}
void initComponents() {
setLayout(new BorderLayout());
//setSize(150,100);
JLabel msg = new JLabel(warning);
this.add(msg,BorderLayout.NORTH);
JCheckBox check = new JCheckBox("Do not show this message again");
add(check, BorderLayout.SOUTH);
check.addItemListener(this);
}
public void itemStateChanged(ItemEvent e) {
if (e.getStateChange() == ItemEvent.SELECTED) {
doNotShowMessage = true;
}
}
}
}