/*
* Copyright (c) 2007, 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.tracer.impl;
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.modules.tracer.PackageStateHandler;
import com.sun.tools.visualvm.modules.tracer.ProbeItemDescriptor;
import com.sun.tools.visualvm.modules.tracer.ProbeStateHandler;
import com.sun.tools.visualvm.modules.tracer.SessionInitializationException;
import com.sun.tools.visualvm.modules.tracer.TracerPackage;
import com.sun.tools.visualvm.modules.tracer.TracerProbe;
import com.sun.tools.visualvm.modules.tracer.TracerProgressObject;
import com.sun.tools.visualvm.modules.tracer.impl.options.TracerOptions;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
import org.openide.DialogDisplayer;
import org.openide.NotifyDescriptor;
import org.openide.util.RequestProcessor;
import org.openide.util.WeakListeners;
/**
*
* @author Jiri Sedlacek
*/
final class TracerController implements DataRemovedListener<DataSource>,
PropertyChangeListener {
private static final Logger LOGGER = Logger.getLogger(TracerController.class.getName());
private static final String PROPERTY_STATE = "state"; // NOI18N
static final int STATE_SESSION_INACTIVE = 0;
static final int STATE_SESSION_RUNNING = 1;
static final int STATE_SESSION_IMPOSSIBLE = -1;
static final int STATE_SESSION_STARTING = Integer.MAX_VALUE;
static final int STATE_SESSION_STOPPING = Integer.MIN_VALUE;
private final DataSource dataSource;
private final TracerModel model;
private final PropertyChangeSupport changeSupport;
private int state;
private TracerProgressObject progress;
private String error;
private boolean wasNegativeValue;
private boolean running;
private final Timer timer;
private RequestProcessor processor;
// --- Constructor ---------------------------------------------------------
TracerController(TracerModel model) {
this.model = model;
dataSource = model.getDataSource();
Stateful stateful = dataSource instanceof Stateful ?
(Stateful) dataSource : null;
if (stateful == null || stateful.getState() == Stateful.STATE_AVAILABLE) {
dataSource.notifyWhenRemoved(this);
dataSource.addPropertyChangeListener(Stateful.PROPERTY_STATE,
WeakListeners.propertyChange(this, dataSource));
changeSupport = new PropertyChangeSupport(this);
state = STATE_SESSION_INACTIVE;
timer = createTimer();
} else {
changeSupport = null;
state = STATE_SESSION_IMPOSSIBLE;
timer = null;
}
}
// --- Session & probes state ----------------------------------------------
private void setState(final int state) {
Runnable stateSetter = new Runnable() {
public void run() {
if (TracerController.this.state == STATE_SESSION_IMPOSSIBLE) return;
int oldState = TracerController.this.state;
TracerController.this.state = state;
changeSupport.firePropertyChange(PROPERTY_STATE, oldState, state);
if (state == STATE_SESSION_RUNNING) startTimer();
}
};
if (SwingUtilities.isEventDispatchThread()) stateSetter.run();
else SwingUtilities.invokeLater(stateSetter);
}
int getState() {
return state;
}
TracerProgressObject getProgress() {
return progress;
}
String getErrorMessage() {
return error;
}
void addListener(PropertyChangeListener listener) {
if (changeSupport != null && state != STATE_SESSION_IMPOSSIBLE)
changeSupport.addPropertyChangeListener(PROPERTY_STATE, listener);
}
void removeListener(PropertyChangeListener listener) {
if (changeSupport != null)
changeSupport.removePropertyChangeListener(PROPERTY_STATE, listener);
}
// --- Session control -----------------------------------------------------
void setRefreshRate(int refreshRate) {
if (timer != null && getRefreshRate() != refreshRate) {
Set<Map.Entry<TracerPackage, List<TracerProbe>>> toNotify =
model.getDefinedProbeSets();
notifyRefreshRateChanged(toNotify);
timer.setDelay(refreshRate);
}
}
int getRefreshRate() {
return timer != null ? timer.getDelay() : -1;
}
void startSession() {
if (!model.areProbesDefined()) return;
if (doStartSession()) setState(STATE_SESSION_RUNNING);
else setState(STATE_SESSION_INACTIVE);
}
void stopSession() {
stopTimer();
if (state == STATE_SESSION_RUNNING) setState(STATE_SESSION_STOPPING);
doStopSession();
setState(STATE_SESSION_INACTIVE);
}
private boolean doStartSession() {
wasNegativeValue = false;
SwingUtilities.invokeLater(new Runnable() {
public void run() { model.getTimelineSupport().resetValues(); }
});
Set<Map.Entry<TracerPackage, List<TracerProbe>>> toNotify =
model.getDefinedProbeSets();
notifySessionInitializing(toNotify);
setState(STATE_SESSION_STARTING);
if (!notifySessionStarting(toNotify)) return false;
notifySessionRunning(toNotify);
return true;
}
private void doStopSession() {
Set<Map.Entry<TracerPackage, List<TracerProbe>>> toNotify =
model.getDefinedProbeSets();
notifySessionStopping(toNotify);
notifySessionFinished(toNotify);
}
private void notifySessionInitializing(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
List<TracerProgressObject> progresses = new ArrayList();
int steps = 0;
Iterator<Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
int refresh = getRefreshRate();
PackageStateHandler ph = item.getKey().getStateHandler();
if (ph != null) try {
TracerProgressObject c = ph.sessionInitializing(probesArr, dataSource, refresh);
if (c != null) {
steps += c.getSteps();
progresses.add(c);
}
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package exception in sessionInitializing", t); // NOI18N
}
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) try {
TracerProgressObject c = rh.sessionInitializing(dataSource, refresh);
if (c != null) {
steps += c.getSteps();
progresses.add(c);
}
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Probe exception in sessionInitializing", t); // NOI18N
}
}
}
if (steps == 0) {
progress = null;
} else {
progress = new TracerProgressObject(steps, "Starting session...");
TracerProgressObject.Listener l = new TracerProgressObject.Listener() {
public void progressChanged(int addedSteps, int currentStep, String text) {
progress.addSteps(addedSteps, text);
}
};
for (TracerProgressObject o : progresses) o.addListener(l);
}
error = null;
}
private boolean notifySessionStarting(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
Iterator<Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
Map<TracerPackage, List<TracerProbe>> notifiedItems = new HashMap();
String notifiedName = null;
try {
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
TracerPackage pkg = item.getKey();
notifiedName = pkg.getName();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
PackageStateHandler ph = pkg.getStateHandler();
if (ph != null) ph.sessionStarting(probesArr, dataSource);
List<TracerProbe> notifiedList = new ArrayList();
notifiedItems.put(pkg, notifiedList);
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
notifiedName = model.getDescriptor(probe).getProbeName();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) rh.sessionStarting(dataSource);
notifiedList.add(probe);
}
}
return true;
} catch (SessionInitializationException sie) {
// TODO: update UI
LOGGER.log(Level.INFO, "Package or probe failed to start Tracer session", sie); // NOI18N
error = sie.getUserMessage();
if (error == null) error = notifiedName + " failed to start";
Set<Map.Entry<TracerPackage, List<TracerProbe>>> notifiedItemsE =
notifiedItems.entrySet();
notifySessionStopping(notifiedItemsE);
setState(STATE_SESSION_STOPPING);
notifySessionFinished(notifiedItemsE);
return false;
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package or probe exception in sessionStarting", t); // NOI18N
return true;
// TODO: ignore or terminate the session as for the SessionInitializationException?
}
}
private void notifySessionRunning(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
Iterator<Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
PackageStateHandler ph = item.getKey().getStateHandler();
if (ph != null) try {
ph.sessionRunning(probesArr, dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package exception in sessionRunning", t); // NOI18N
}
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) try {
rh.sessionRunning(dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Probe exception in sessionRunning", t); // NOI18N
}
}
}
}
private void notifySessionStopping(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
Iterator<Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
PackageStateHandler ph = item.getKey().getStateHandler();
if (ph != null) try {
ph.sessionStopping(probesArr, dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package exception in sessionStopping", t); // NOI18N
}
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) try {
rh.sessionStopping(dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Probe exception in sessionStopping", t); // NOI18N
}
}
}
}
private void notifySessionFinished(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
Iterator <Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
PackageStateHandler ph = item.getKey().getStateHandler();
if (ph != null) try {
ph.sessionFinished(probesArr, dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package exception in sessionFinished", t); // NOI18N
}
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) try {
rh.sessionFinished(dataSource);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Probe exception in sessionFinished", t); // NOI18N
}
}
}
}
private void notifyRefreshRateChanged(Set<Map.Entry<TracerPackage, List<TracerProbe>>> items) {
Iterator<Map.Entry<TracerPackage, List<TracerProbe>>> itemsI = items.iterator();
while (itemsI.hasNext()) {
Map.Entry<TracerPackage, List<TracerProbe>> item = itemsI.next();
List<TracerProbe> probes = item.getValue();
TracerProbe[] probesArr = probes.toArray(new TracerProbe[probes.size()]);
int refresh = getRefreshRate();
PackageStateHandler ph = item.getKey().getStateHandler();
if (ph != null) try {
ph.refreshRateChanged(probesArr, dataSource, refresh);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Package exception in refreshRateChanged", t); // NOI18N
}
Iterator<TracerProbe> probesI = probes.iterator();
while (probesI.hasNext()) {
TracerProbe probe = probesI.next();
ProbeStateHandler rh = probe.getStateHandler();
if (rh != null) try {
rh.refreshRateChanged(dataSource, refresh);
} catch (Throwable t) {
LOGGER.log(Level.INFO, "Probe exception in refreshRateChanged", t); // NOI18N
}
}
}
}
// --- Session runtime -----------------------------------------------------
private Timer createTimer() {
int rate = TracerOptions.getInstance().getRefreshRate();
Timer t = new Timer(rate, new ActionListener() {
public void actionPerformed(ActionEvent e) { fetchData(); }
});
t.setInitialDelay(0);
return t;
}
private void startTimer() {
if (timer != null) {
running = true;
timer.start();
}
}
private void stopTimer() {
if (timer != null) {
running = false;
timer.stop();
}
}
private void fetchData() {
if (!running) return;
if (processor == null)
processor = new RequestProcessor("Tracer Processor for " + dataSource); // NOI18N
final List<TracerProbe> probes = model.getDefinedProbes();
final int itemsCount = model.getTimelineSupport().getItemsCount();
processor.post(new Runnable() {
public void run() { fetchDataImpl(probes, itemsCount); }
});
}
private void fetchDataImpl(List<TracerProbe> probes, int itemsCount) {
if (!running) return;
final long[] values = new long[itemsCount];
int currentIndex = 0;
final long timestamp = System.currentTimeMillis();
for (TracerProbe probe : probes) {
long[] itemValues;
try {
itemValues = probe.getItemValues(timestamp);
} catch (Throwable t) {
itemValues = new long[probe.getItemsCount()];
Arrays.fill(itemValues, ProbeItemDescriptor.VALUE_UNDEFINED);
LOGGER.log(Level.INFO, "Probe exception in getItemValues", t); // NOI18N
}
for (int i = 0; i < itemValues.length; i++) {
long value = itemValues[i];
if (value < 0) {
if (!wasNegativeValue) {
DialogDisplayer.getDefault().notifyLater(
new NotifyDescriptor.Message("<html><b>One or more probes "
+ "returned negative value.</b><br><br>Currently this is "
+ "not supported in Tracer,<br>all negative values will be"
+ " displayed as 0.</html>", NotifyDescriptor.WARNING_MESSAGE));
LOGGER.info("Probe " + model.getDescriptor(probe).getProbeName() + // NOI18N
" returned negative value: " + value); // NOI18N
wasNegativeValue = true;
}
value = 0;
}
values[currentIndex++] = value;
}
}
if (!running) return;
SwingUtilities.invokeLater(new Runnable() {
public void run() {
if (!running) return;
model.getTimelineSupport().addValues(timestamp, values);
}
});
}
// --- DataSource & DataSourceView lifecycle -------------------------------
void viewRemoved() {
stopSession();
setState(STATE_SESSION_IMPOSSIBLE);
}
public void dataRemoved(DataSource dataSource) {
stopTimer();
setState(STATE_SESSION_IMPOSSIBLE);
}
public void propertyChange(PropertyChangeEvent evt) {
stopTimer();
setState(STATE_SESSION_IMPOSSIBLE);
}
}