net.sf.profiler4j.console.Console.java Source code

Java tutorial

Introduction

Here is the source code for net.sf.profiler4j.console.Console.java

Source

/*
 * Copyright 2006 Antonio S. R. Gomes
 * Copyright 2009 Murat Knecht
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License. You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software distributed under
 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */
package net.sf.profiler4j.console;

import static javax.swing.JOptionPane.showMessageDialog;

import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.prefs.BackingStoreException;

import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.filechooser.FileFilter;

import net.sf.profiler4j.agent.AgentConstants;
import net.sf.profiler4j.commons.Snapshot;
import net.sf.profiler4j.console.client.Client;
import net.sf.profiler4j.console.client.ClientException;
import net.sf.profiler4j.console.client.ProgressCallback;
import net.sf.profiler4j.console.util.task.ExportCallgraphTask;
import net.sf.profiler4j.console.util.task.LongTask;
import net.sf.profiler4j.console.util.task.LongTaskExecutorDialog;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.Format;
import org.jdom.output.XMLOutputter;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * Application controller.
 * 
 * @author Antonio S. R. Gomes
 */
public class Console {

    private static final String PROJECT_XML_ATTRIBUTE__ENABLED = "enabled";
    private static final String PROJECT_XML_ATTRIBUTE__PATTERN = "pattern";

    private static final String PROJECT_XML_ELEMENT__EXPORT_PATTERN = "export";

    public static final String SNAPSHOT_EXTENSION = ".p4j-snapshot";

    private Client client;
    private Project project;
    private MainFrame mainFrame;
    private StatusBarPanel statusBar;
    private File lastDir;
    private Prefs prefs;
    private Document tipDoc;
    private Snapshot snapshot = null;

    private Timer memoryPanelTimer = new Timer(1000, new ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent e) {
            if (client.isConnected()) {
                try {
                    sendEvent(AppEventType.GOT_MEMORY_INFO, client.getMemoryInfo());
                } catch (ClientException re) {
                    error("Could not refresh memory info", re);
                    memoryPanelTimer.stop();
                }
            }
        };
    });

    private List<AppEventListener> listeners = new ArrayList<AppEventListener>();

    private FileFilter projectFilter = new FileFilter() {
        public boolean accept(File file) {
            String filename = file.getName();
            return filename.endsWith(".p4j");
        }

        public String getDescription() {
            return "*.p4j (Profiling Project)";
        }
    };

    private Console(Prefs prefs) {
        this.prefs = prefs;
        project = new Project();
        lastDir = new File(System.getProperty("user.home"));
        client = new Client();
        SAXBuilder builder = new SAXBuilder();
        try {
            tipDoc = builder.build(getClass().getResource("tips.xml"));
        } catch (JDOMException e) {
            error("Could not read tips (XML Error)", e);
        } catch (IOException e) {
            error("Could not read tips (I/O Error)", e);
        }

    }

    /**
     * @return Returns the tipDoc.
     */
    public Document getTipDoc() {
        return this.tipDoc;
    }

    /**
     * @return Returns the prefs.
     */
    public Prefs getPrefs() {
        return this.prefs;
    }

    public void error(String message) {
        showMessageDialog(mainFrame, message, "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void error(String message, Throwable t) {
        showMessageDialog(mainFrame, message + "\nMessage: " + t.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
    }

    public void connect() {
        LongTask t = new LongTask() {
            public void executeInBackground() throws Exception {
                setTaskMessage("Establishing connection with remote JVM...");
                client.connect(project.getHostname(), project.getPort());
                setTaskMessage("Activating profiling rules...");
                client.applyRules(project.formatRules(), project.formatOptions(), new ProgressCallback() {
                    private int max;

                    public void operationStarted(int amount) {
                        max = amount;
                    }

                    public void update(int value) {
                        setTaskProgress((value * 100) / max);
                        setTaskMessage("Activating profiling rules... (class " + value + " of " + max + ")");
                    }
                });
                SwingUtilities.invokeLater(new Runnable() {
                    public void run() {
                        sendEvent(AppEventType.CONNECTED);
                        memoryPanelTimer.start();
                    }
                });

            }
        };
        runInBackground(t);
    }

    public void disconnect() {
        if (!client.isConnected()) {
            return;
        }
        int ret = JOptionPane.showConfirmDialog(mainFrame,
                "Do you want to undo any changes made to classes before\n" + "you disconnect?\n\n"
                        + "(This requires some time to complete but leaves\n"
                        + "the remote JVM running at 100% of the original speed)",
                "Disconnection", JOptionPane.YES_NO_CANCEL_OPTION);
        if (ret == JOptionPane.CANCEL_OPTION) {
            return;
        }
        final boolean undoChanges = ret == JOptionPane.YES_OPTION;
        LongTask t = new LongTask() {
            public void executeInBackground() throws Exception {
                if (undoChanges) {
                    setTaskMessage("Undoing changes to classes...");
                    client.restoreClasses(new ProgressCallback() {
                        private int max;

                        public void operationStarted(int amount) {
                            max = amount;
                            update(0);
                        }

                        public void update(int value) {
                            setTaskProgress((value * 100) / max);
                            setTaskMessage("Undoing changes to classes... (class " + value + " of " + max + ")");
                        }
                    });
                }
            };
        };
        runInBackground(t);

        memoryPanelTimer.stop();
        sendEvent(AppEventType.TO_DISCONNECT);

        try {
            client.disconnect();
        } catch (ClientException e) {
            error("Connection was close with error: (" + e.getMessage() + ")", e);
        }

        sendEvent(AppEventType.DISCONNECTED);
    }

    /**
     * @return <code>true</code> if a new project was created (the user could have been
     *         cancelled)
     */
    public boolean newProject() {
        if (client.isConnected()) {
            int ret = JOptionPane.showConfirmDialog(mainFrame, "Proceed and disconnect?", "New Profiling Project",
                    JOptionPane.YES_NO_OPTION);
            if (ret == JOptionPane.NO_OPTION) {
                return false;
            }
        }

        if (checkUnsavedChanges()) {
            return false;
        }
        if (client.isConnected()) {
            disconnect();
            if (client.isConnected()) {
                return false;
            }
        }
        Project p = new Project();
        ProjectDialog d = new ProjectDialog(mainFrame, this);
        if (d.edit(p)) {
            this.project = p;
            return true;
        }
        return false;
    }

    public void openProject() {

        if (client.isConnected()) {
            int ret = JOptionPane.showConfirmDialog(mainFrame, "Proceed and disconnect?", "Open Profiling Project",
                    JOptionPane.YES_NO_OPTION);
            if (ret == JOptionPane.NO_OPTION) {
                return;
            }
        }

        if (checkUnsavedChanges()) {
            return;
        }

        if (client.isConnected()) {
            disconnect();
            if (client.isConnected()) {
                return;
            }
        }
        JFileChooser fc = new JFileChooser(lastDir);
        fc.addChoosableFileFilter(projectFilter);
        if (fc.showOpenDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
            File selFile = fc.getSelectedFile();
            SAXBuilder builder = new SAXBuilder();
            Document doc = null;
            try {
                doc = builder.build(selFile);
            } catch (JDOMException e) {
                error("XML Error", e);
            } catch (IOException e) {
                error("I/O Error", e);
            }
            if (doc != null) {
                Project p = new Project();
                Element el = doc.getRootElement();
                p.setHostname(el.getChildText("Host"));
                p.setPort(Integer.parseInt(el.getChildText("Port")));
                Element rulesEl = el.getChild("Rules");

                p.setAccess(Rule.AccessOption.valueOf(rulesEl.getAttributeValue("access")));
                p.setBeanprops(Boolean.parseBoolean(rulesEl.getAttributeValue("beanProps")));

                p.getRules().clear();
                for (Iterator i = rulesEl.getChildren("Rule").iterator(); i.hasNext();) {
                    Element r = (Element) i.next();
                    Rule rule = new Rule(r.getText(), Rule.Action.valueOf(r.getAttributeValue("action")));
                    p.getRules().add(rule);
                }

                // Backwards compatible way to read the export pattern
                // If it is not there, we leave the defaults as they are,
                // otherwise we set the saved setting.
                Element export = el.getChild(PROJECT_XML_ELEMENT__EXPORT_PATTERN);
                if (null != export) {
                    String enabled = export.getAttributeValue(PROJECT_XML_ATTRIBUTE__ENABLED);
                    p.setExportAutomaticallyEnabled(Boolean.valueOf(enabled));
                    p.setExportPattern(export.getAttributeValue(PROJECT_XML_ATTRIBUTE__PATTERN));
                }

                p.setFile(selFile);
                p.clearChanged();
                this.project = p;
                lastDir = selFile.getParentFile();
            }
        }

    }

    public void close() {
        if (checkUnsavedChanges()) {
            return;
        }
        if (client.isConnected()) {
            disconnect();
            if (client.isConnected()) {
                return;
            }
        }
    }

    /**
     * Saves the current project.
     * 
     * @param saveAs force the user to select a file name even
     * @return <code>true</code> if the user has cancelled (only in the case of save as)
     */
    public boolean saveProject(boolean saveAs) {
        if (project.getFile() == null || saveAs) {
            JFileChooser fc = new JFileChooser(lastDir);

            fc.setDialogTitle("Save Project As");

            fc.addChoosableFileFilter(projectFilter);
            if (fc.showSaveDialog(mainFrame) == JFileChooser.APPROVE_OPTION) {
                File f = fc.getSelectedFile();
                if (!f.getName().endsWith(".p4j")) {
                    f = new File(f.getAbsolutePath() + ".p4j");
                }
                project.setFile(f);
            } else {
                return true;
            }
        }

        Element rootEl = new Element("Profiler4jProject");
        Document doc = new Document(rootEl);

        rootEl.addContent(new Element("Host").setText(project.getHostname()));
        rootEl.addContent(new Element("Port").setText(String.valueOf(project.getPort())));

        Element rulesEl = new Element("Rules");
        rootEl.addContent(rulesEl);

        rulesEl.setAttribute("access", project.getAccess().name());
        rulesEl.setAttribute("beanProps", String.valueOf(project.isBeanprops()));

        for (Rule rule : project.getRules()) {
            rulesEl.addContent(
                    new Element("Rule").setText(rule.getPattern()).setAttribute("action", rule.getAction().name()));
        }

        Element exportPatternEl = new Element(PROJECT_XML_ELEMENT__EXPORT_PATTERN);
        String enabled = String.valueOf(project.isExportAutomaticallyEnabled());
        exportPatternEl.setAttribute(PROJECT_XML_ATTRIBUTE__ENABLED, enabled);
        exportPatternEl.setAttribute(PROJECT_XML_ATTRIBUTE__PATTERN, project.getExportPattern());
        rootEl.addContent(exportPatternEl);

        try {
            FileWriter fw = new FileWriter(project.getFile());
            XMLOutputter outputter = new XMLOutputter(Format.getPrettyFormat());
            outputter.output(doc, fw);
            fw.close();
            project.clearChanged();
        } catch (IOException e) {
            error("I/O Error", e);
        }
        return false;
    }

    public boolean editProject() {
        ProjectDialog d = new ProjectDialog(mainFrame, this);
        return d.edit(project);
    }

    public void applyRules() {
        int ret = JOptionPane.showConfirmDialog(mainFrame,
                "Request activation of profiling rules now? (This may take some time)", "Activate Profiling Rules",
                JOptionPane.OK_CANCEL_OPTION);
        if (ret != JOptionPane.OK_OPTION) {
            return;
        }
        LongTask t = new LongTask() {
            public void executeInBackground() throws Exception {
                setTaskMessage("Activating profiling rules...");
                client.applyRules(project.formatRules(), project.formatOptions(), new ProgressCallback() {
                    private int max;

                    public void operationStarted(int amount) {
                        max = amount;
                        setTaskProgress(0);
                    }

                    public void update(int value) {
                        setTaskProgress((value * 100) / max);
                        setTaskMessage("Activating profiling rules... (class " + value + " of " + max + ")");
                    }
                });
            };
        };
        runInBackground(t);
        if (t.getError() == null) {
            sendEvent(AppEventType.RULES_APPLIED);
        }
    }

    public void takeSnapshot() {
        LongTask t = new LongTask() {
            public void executeInBackground() throws Exception {
                setTaskMessage("Retrieving snapshot...");
                setValue(client.getSnapshot());
            };
        };
        runInBackground(t);
        if (t.getError() == null) {
            snapshot = (Snapshot) t.getValue();
            sendEvent(AppEventType.SNAPSHOT, snapshot);

            // In case the project settings specify this, dump a snapshot using the filename pattern
            // specified by the user.
            if (project.isExportAutomaticallyEnabled()) {

                t = new ExportCallgraphTask(snapshot, project, getMainFrame());
                runInBackground(t);
            }
        }
    }

    /**
     * 
     * @return <code>true</code> if the user has cancelled
     */
    private boolean checkUnsavedChanges() {
        if (project.isChanged()) {
            int ret = JOptionPane.showConfirmDialog(mainFrame, "Project has unsaved changes? Save before exit?",
                    "Unsaved Changes", JOptionPane.YES_NO_CANCEL_OPTION);
            if (ret == JOptionPane.CANCEL_OPTION) {
                return true;
            } else if (ret == JOptionPane.YES_OPTION) {
                return saveProject(false);
            }
        }
        return false;
    }

    public void exit() {

        try {
            prefs.flush();
        } catch (BackingStoreException e) {
            System.err.println("ERROR: could not flush preferences to disk");
            e.printStackTrace();
        }

        if (checkUnsavedChanges()) {
            return;
        }

        if (sendEvent(AppEventType.APP_CLOSING)) {
            return;
        }
        if (client.isConnected()) {
            disconnect();
            if (!client.isConnected()) {
                sendEvent(AppEventType.APP_CLOSED);
                System.exit(0);
            }
        } else {
            sendEvent(AppEventType.APP_CLOSED);
            System.exit(0);
        }
    }

    public void runInBackground(LongTask task) {
        memoryPanelTimer.stop();
        try {
            LongTaskExecutorDialog d = new LongTaskExecutorDialog(mainFrame);
            d.setLocationRelativeTo(mainFrame);
            d.runTask(task);
        } finally {
            memoryPanelTimer.restart();
        }
    }

    public void addListener(AppEventListener l) {
        listeners.add(l);
    }

    public void removeListener(AppEventListener l) {
        listeners.remove(l);
    }

    private boolean sendEvent(AppEventType evType) {
        return sendEvent(new AppEvent(evType));
    }

    private boolean sendEvent(AppEventType evType, Object arg) {
        return sendEvent(new AppEvent(evType, arg));
    }

    private boolean sendEvent(AppEvent ev) {
        for (AppEventListener l : listeners) {
            boolean vetoed = l.receiveEvent(ev);
            if (vetoed) {
                if (!ev.getType().isVetoable()) {
                    throw new IllegalArgumentException("AppEventType " + ev.getType() + " is not vetoable");
                }
                return true;
            }
        }
        return false;
    }

    /**
     * @return Returns the project.
     */
    public Project getProject() {
        return this.project;
    }

    /**
     * @param project The project to set.
     */
    public void setProject(Project project) {
        this.project = project;
    }

    /**
     * @return Returns the mainFrame.
     */
    public MainFrame getMainFrame() {
        return this.mainFrame;
    }

    /**
     * @param mainFrame The mainFrame to set.
     */
    public void setMainFrame(MainFrame mainFrame) {
        this.mainFrame = mainFrame;
        this.statusBar = mainFrame.getStatusBarPanel();
    }

    public void setStatusMessage(String msg) {
        statusBar.setMessage(msg);
    }

    /**
     * @return Returns the client.
     */
    public Client getClient() {
        return this.client;
    }

    public void saveCurrentSnapshotToFile(File destination) {
        XStream xstream = new XStream(new DomDriver());
        String xml = xstream.toXML(snapshot);
        try {
            FileWriter fwriter = new FileWriter(destination);
            fwriter.write(xml);
            fwriter.close();
        } catch (IOException exc) {
            error("I/O Error", exc);
        }
    }

    public void loadSnapshotFromFile(File source) {
        XStream xstream = new XStream(new DomDriver());
        try {
            snapshot = (Snapshot) xstream.fromXML(new FileReader(source));
        } catch (IOException exc) {
            error("I/O Error", exc);
            return;
        }
        sendEvent(AppEventType.SNAPSHOT, snapshot);
    }

    public static void main(String[] args) {

        System.out.println();
        System.out.println("+--------------------------------------------------------+");
        System.out.println("| Profiler4j-Fork Console " + String.format("%-31s", AgentConstants.VERSION) + "|");
        System.out.println("| Copyright 2006 Antonio S. R. Gomes                     |");
        System.out.println("| Copyright 2009 Murat Knecht                            |");
        System.out.println("| See LICENSE-2.0.txt for details                        |");
        System.out.println("+--------------------------------------------------------+");

        Prefs prefs = new Prefs();

        System.setProperty("swing.aatext", String.valueOf(prefs.isAntialiasing()));

        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (Exception e) {
            // ignore
        }
        final Console app = new Console(prefs);
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                MainFrame f = new MainFrame(app);
                app.setMainFrame(f);
                f.pack();
                f.setVisible(true);
            }
        });

    }

}