/*
* SoapUI, Copyright (C) 2004-2016 SmartBear Software
*
* Licensed under the EUPL, Version 1.1 or - as soon as they will be approved by the European Commission - subsequent
* versions of the EUPL (the "Licence");
* You may not use this work except in compliance with the Licence.
* You may obtain a copy of the Licence at:
*
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed under the Licence is
* distributed on an "AS IS" basis, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the Licence for the specific language governing permissions and limitations
* under the Licence.
*/
package com.eviware.soapui.support.monitor;
import javax.swing.AbstractAction;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
/**
* @author Angelo De Caro
*/
public class MonitorPanel extends JPanel {
public Surface surf;
public MonitorPanel(MonitorSource monitorSource) {
setLayout(new BorderLayout());
add(surf = createSurface());
setMonitorSource(monitorSource);
JPopupMenu popup = new JPopupMenu();
popup.add(new GCAction());
setComponentPopupMenu(popup);
}
public void start() {
surf.start();
}
public void stop() {
surf.stop();
}
public MonitorSource getMonitorSource() {
return surf.getMonitorSource();
}
public void setMonitorSource(MonitorSource monitorSource) {
surf.setMonitorSource(monitorSource);
}
protected Surface createSurface() {
return new Surface(null);
}
protected class Surface extends JPanel implements Runnable {
private Thread thread;
private MonitorSource monitorSource;
private long sleepAmount = 1000;
private int w, h;
private BufferedImage backImage;
private Graphics2D backImageGrfx;
private Font font = new Font("Times New Roman", Font.PLAIN, 11);
private int columnInc;
private double[] points;
private int validPoints;
private int ascent, descent;
private Rectangle2D mfRect = new Rectangle2D.Float();
private Rectangle2D muRect = new Rectangle2D.Float();
private Line2D graphLine = new Line2D.Float();
private Color graphColor = new Color(46, 139, 87);
private Color mfColor = new Color(0, 100, 0);
public Surface(MonitorSource monitorSource) {
this.monitorSource = monitorSource;
setBackground(Color.black);
}
public void paint(Graphics g) {
Dimension d = getSize();
if (d.width != w || d.height != h) {
w = d.width;
h = d.height;
backImage = (BufferedImage) createImage(w, h);
backImageGrfx = backImage.createGraphics();
backImageGrfx.setFont(font);
FontMetrics fm = backImageGrfx.getFontMetrics(font);
ascent = fm.getAscent();
descent = fm.getDescent();
}
backImageGrfx.setBackground(Color.DARK_GRAY);
backImageGrfx.clearRect(0, 0, w, h);
long totalMemory = monitorSource.getTotal();
long usedMemory = monitorSource.getUsed();
long freeMemory = totalMemory - usedMemory;
// Draw allocated and used strings
backImageGrfx.setColor(Color.green);
backImageGrfx.drawString(String.valueOf(totalMemory >> 10) + "K allocated", 4.0f,
(float) ascent + 0.5f);
String usedStr = String.valueOf(((int) usedMemory) >> 10) + "K used";
backImageGrfx.drawString(usedStr, 4, h - descent);
// Calculate remaining size
float ssH = ascent + descent;
float remainingHeight = (h - ssH * 2 - 0.5f);
float blockHeight = remainingHeight / 10;
float blockWidth = 20.0f;
// Memory Free
backImageGrfx.setColor(mfColor);
float memUsage = (float) freeMemory / (float) totalMemory * 10;
int i = 0;
for (; i < memUsage; i++) {
mfRect.setRect(5, ssH + i * blockHeight, blockWidth, blockHeight - 1);
backImageGrfx.fill(mfRect);
}
// Memory Used
backImageGrfx.setColor(Color.green);
for (; i < 10; i++) {
muRect.setRect(5, ssH + i * blockHeight, blockWidth, blockHeight - 1);
backImageGrfx.fill(muRect);
}
// Draw History Graph
backImageGrfx.setColor(graphColor);
int graphX = 30;
int graphY = (int) ssH;
int graphW = w - graphX - 5;
if (graphW < 0) {
graphW = 0;
}
int graphH = (int) (ssH + (9 * blockHeight) + blockHeight - 1);
i = 0;
for (; i < 10; i++) {
muRect.setRect(graphX, ssH + i * blockHeight - 1, graphW, blockHeight);
backImageGrfx.draw(muRect);
}
// Draw animated column movement
int graphColumn = graphW / 15;
if (columnInc == 0) {
columnInc = graphColumn;
}
for (int j = graphX + columnInc; j < graphW + graphX; j += graphColumn) {
graphLine.setLine(j, graphY, j, ssH + i * blockHeight - 1);
backImageGrfx.draw(graphLine);
}
--columnInc;
if (points == null) {
points = new double[graphW];
validPoints = 0;
} else if (points.length != graphW) {
double[] tmp;
if (validPoints < graphW) {
tmp = new double[validPoints];
System.arraycopy(points, 0, tmp, 0, tmp.length);
} else {
tmp = new double[graphW];
System.arraycopy(points, points.length - tmp.length, tmp, 0, tmp.length);
validPoints = tmp.length - 2;
}
points = new double[graphW];
System.arraycopy(tmp, 0, points, 0, tmp.length);
} else {
backImageGrfx.setColor(Color.yellow);
int x = w - 5;
int sum = graphH - (ascent + descent);
for (int j = x - validPoints, k = 0; k < validPoints; k++, j++) {
if (k != 0) {
if (points[k] != points[k - 1]) {
backImageGrfx.drawLine(j - 1, graphY + (int) (sum * points[k - 1]), j, graphY
+ (int) (sum * points[k]));
} else {
backImageGrfx.fillRect(j, graphY + (int) (sum * points[k]), 1, 1);
}
}
}
}
g.drawImage(backImage, 0, 0, this);
}
public void run() {
while (thread != null) {
float totalMemory = monitorSource.getTotal();
float usedMemory = monitorSource.getUsed();
float freeMemory = totalMemory - usedMemory;
if (points == null) {
points = new double[1];
validPoints = 0;
} else if (points.length < validPoints + 1) {
double[] tmp;
int graphW = validPoints + 1;
if (validPoints < graphW) {
tmp = new double[validPoints];
System.arraycopy(points, 0, tmp, 0, tmp.length);
} else {
tmp = new double[graphW];
System.arraycopy(points, points.length - tmp.length, tmp, 0, tmp.length);
validPoints = tmp.length - 2;
}
points = new double[graphW];
System.arraycopy(tmp, 0, points, 0, tmp.length);
} else {
points[validPoints] = (freeMemory / totalMemory);
if (validPoints + 2 == points.length) {
// throw out oldest point
System.arraycopy(points, 1, points, 0, validPoints);
--validPoints;
} else {
validPoints++;
}
}
repaint();
try {
Thread.sleep(sleepAmount);
} catch (InterruptedException e) {
break;
}
}
}
public void start() {
if (monitorSource == null) {
throw new IllegalStateException("Monitor Source cannot be null.");
}
thread = new Thread(this);
thread.setPriority(Thread.MIN_PRIORITY);
thread.setDaemon(true);
thread.setName("MemoryMonitor");
thread.start();
}
public synchronized void stop() {
thread = null;
notify();
}
public MonitorSource getMonitorSource() {
return monitorSource;
}
public void setMonitorSource(MonitorSource monitorSource) {
this.monitorSource = monitorSource;
}
public Dimension getMinimumSize() {
return getPreferredSize();
}
public Dimension getMaximumSize() {
return getPreferredSize();
}
public Dimension getPreferredSize() {
return new Dimension(135, 80);
}
}
private class GCAction extends AbstractAction {
private GCAction() {
super("Run GC");
putValue(SHORT_DESCRIPTION, "Runs finalization and garbage collector");
}
public void actionPerformed(ActionEvent e) {
System.runFinalization();
Runtime.getRuntime().gc();
}
}
}