001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jmeter.visualizers;
020:
021: import java.awt.Color;
022: import java.awt.Dimension;
023: import java.awt.Graphics;
024: import java.awt.GridBagConstraints;
025: import java.awt.GridBagLayout;
026: import java.awt.Insets;
027: import java.awt.Point;
028: import java.awt.Rectangle;
029: import java.util.Iterator;
030:
031: import javax.swing.JComponent;
032: import javax.swing.JLabel;
033: import javax.swing.JPanel;
034: import javax.swing.Scrollable;
035: import javax.swing.SwingUtilities;
036:
037: import org.apache.jmeter.samplers.SampleResult;
038: import org.apache.jmeter.util.ColorHelper;
039: import org.apache.jorphan.logging.LoggingManager;
040: import org.apache.log.Logger;
041:
042: /**
043: * Draws the graph.
044: *
045: * Created 2001/08/11
046: */
047: public class GraphAccum extends JComponent implements Scrollable,
048: GraphAccumListener {
049:
050: private static final Logger log = LoggingManager
051: .getLoggerForClass();
052:
053: private GraphAccumModel model;
054:
055: private GraphAccumVisualizer visualizer;
056:
057: /** Ensure that the legends are only drawn once. */
058: private boolean noLegendYet = true;
059:
060: /**
061: * Keep track of previous point. Needed to draw a line joining the previous
062: * point with the current one.
063: */
064: private Point[] previousPts;
065:
066: /**
067: * Ensure that previousPts is allocated once only. It'll be reused at each
068: * drawSample. It can't be allocated outside drawSample 'cos the sample is
069: * only passed in here.
070: */
071: private boolean previousPtsAlloc = false;
072:
073: protected final static int width = 2000;
074:
075: private final static int PLOT_X_WIDTH = 10;
076:
077: /**
078: * Constructor.
079: */
080: public GraphAccum() {
081: log.debug("Start : GraphAnnum1");
082: log.debug("End : GraphAnnum1");
083: }
084:
085: /**
086: * Constructor with model set.
087: *
088: * @param model
089: * model which this object represents
090: */
091: public GraphAccum(GraphAccumModel model) {
092: this ();
093: log.debug("Start : GraphAnnum2");
094: setModel(model);
095: log.debug("End : GraphAnnum2");
096: }
097:
098: /**
099: * Set model which this object represents.
100: *
101: * @param model
102: * model which this object represents
103: */
104: private void setModel(Object model) {
105: log.debug("Start : setModel1");
106: this .model = (GraphAccumModel) model;
107: this .model.addGraphAccumListener(this );
108: repaint();
109: log.debug("End : setModel1");
110: }
111:
112: /**
113: * Set the visualizer.
114: *
115: * @param visualizer
116: * visualizer of this object
117: */
118: public void setVisualizer(Object visualizer) {
119: if (log.isDebugEnabled()) {
120: log.debug("setVisualizer1 : Setting visualizer - "
121: + visualizer);
122: }
123: this .visualizer = (GraphAccumVisualizer) visualizer;
124: }
125:
126: /**
127: * The legend is only printed once during sampling. This sets the variable
128: * that indicates whether the legend has been printed yet or not.
129: *
130: * @param value
131: * variable that indicates whether the legend has been printed
132: * yet
133: */
134: public void setNoLegendYet(boolean value) {
135: noLegendYet = value;
136: }
137:
138: /**
139: * Gets the PreferredScrollableViewportSize attribute of the Graph object.
140: *
141: * @return the PreferredScrollableViewportSize value
142: */
143: public Dimension getPreferredScrollableViewportSize() {
144: return this .getPreferredSize();
145: }
146:
147: /**
148: * Gets the ScrollableUnitIncrement attribute of the Graph object.
149: *
150: * @return the ScrollableUnitIncrement value
151: */
152: public int getScrollableUnitIncrement(Rectangle visibleRect,
153: int orientation, int direction) {
154: return 5;
155: }
156:
157: /**
158: * Gets the ScrollableBlockIncrement attribute of the Graph object.
159: *
160: * @return the ScrollableBlockIncrement value
161: */
162: public int getScrollableBlockIncrement(Rectangle visibleRect,
163: int orientation, int direction) {
164: return (int) (visibleRect.width * .9);
165: }
166:
167: /**
168: * Gets the ScrollableTracksViewportWidth attribute of the Graph object.
169: *
170: * @return the ScrollableTracksViewportWidth value
171: */
172: public boolean getScrollableTracksViewportWidth() {
173: return false;
174: }
175:
176: /**
177: * Gets the ScrollableTracksViewportHeight attribute of the Graph object.
178: *
179: * @return the ScrollableTracksViewportHeight value
180: */
181: public boolean getScrollableTracksViewportHeight() {
182: return true;
183: }
184:
185: /**
186: * The legend is only printed once during sampling. This returns the
187: * variable that indicates whether the legend has been printed yet or not.
188: *
189: * @return value variable that indicates whether the legend has been printed
190: * yet
191: */
192: public boolean getNoLegendYet() {
193: return noLegendYet;
194: }
195:
196: /**
197: * Redraws the gui.
198: */
199: public void updateGui() {
200: log.debug("Start : updateGui1");
201: repaint();
202: log.debug("End : updateGui1");
203: }
204:
205: /**
206: * Redraws the gui if no rescaling of the graph is needed.
207: *
208: * @param oneSample
209: * sample to be added
210: */
211: public void updateGui(final SampleResult oneSample) {
212: log.debug("Start : updateGui2");
213: final int xPos = model.getSampleCount();
214:
215: SwingUtilities.invokeLater(new Runnable() {
216: public void run() {
217: Graphics g = getGraphics();
218:
219: if (g != null) {
220: drawSample(xPos * PLOT_X_WIDTH, oneSample, g);
221: }
222: }
223: });
224: log.debug("End : updateGui2");
225: }
226:
227: public void paintComponent(Graphics g) {
228: super .paintComponent(g);
229: log.debug("Start : paintComponent1");
230:
231: synchronized (model.getList()) {
232: // For repainting set this to false because all the points needs to
233: // be redrawn so no need(shouldn't) use the previousPts.
234: previousPtsAlloc = false;
235: Iterator e = model.getList().iterator();
236:
237: for (int i = 0; e.hasNext(); i++) {
238: SampleResult s = (SampleResult) e.next();
239:
240: drawSample(i * PLOT_X_WIDTH, s, g);
241: }
242: }
243: log.debug("End : paintComponent1");
244: }
245:
246: /**
247: * Clears this graph.
248: */
249: public void clearData() {
250: setNoLegendYet(true);
251: ((JPanel) visualizer.getWhiteCanvas()).removeAll();
252: previousPts = null;
253: }
254:
255: private void drawSample(int x, SampleResult oneSample, Graphics g) {
256: log.debug("Start : drawSample1");
257:
258: // Used to keep track of accumulated load times of components.
259: int lastLevel = 0;
260:
261: // Number of components
262: int compCount = 0;
263:
264: SampleResult[] resultList = oneSample.getSubResults();
265: int resultListCount = 0;
266:
267: // Allocate previousPts only the first time
268: if (!previousPtsAlloc) {
269: resultListCount += resultList.length;
270: previousPts = new Point[resultListCount + 2];
271: }
272:
273: Color currColor = Color.black;
274: JPanel lPanel = (JPanel) visualizer.getWhiteCanvas();
275: JPanel legendPanel = new JPanel();
276: GridBagLayout gridBag = new GridBagLayout();
277: GridBagConstraints gbc = new GridBagConstraints();
278:
279: legendPanel.setLayout(gridBag);
280: lPanel.add(legendPanel);
281: Dimension d = this .getSize();
282:
283: // Set the total time to load the sample
284: long totalTime = oneSample.getTime();
285:
286: // If the page has other components then set the total time to be that
287: // including all its components' load time.
288: if (log.isDebugEnabled()) {
289: log.debug("drawSample1 : total time - " + totalTime);
290: }
291: int data = (int) (totalTime * d.height / model.getMax());
292:
293: g.setColor(currColor);
294: if (!previousPtsAlloc) {
295: // If first dot, just draw the point.
296: g.drawLine(x % width, d.height - data, x % width, d.height
297: - data - 1);
298: } else {
299: // Otherwise, draw from previous point.
300: g.drawLine((previousPts[0].x) % width, previousPts[0].y, x
301: % width, d.height - data);
302: }
303:
304: // Store current total time point
305: previousPts[0] = new Point(x % width, d.height - data);
306: if (noLegendYet) {
307: gbc.gridx = 0;
308: gbc.gridy = compCount++;
309: gbc.anchor = GridBagConstraints.WEST;
310: gbc.weightx = 1.0;
311: gbc.insets = new Insets(0, 10, 0, 0);
312: JLabel totalTimeLabel = new JLabel("Total time - "
313: + oneSample.toString());
314:
315: totalTimeLabel.setForeground(currColor);
316: gridBag.setConstraints(totalTimeLabel, gbc);
317: legendPanel.add(totalTimeLabel);
318: }
319:
320: // Plot the time of the page itself without all its components
321: if (log.isDebugEnabled()) {
322: log.debug("drawSample1 : main page load time - "
323: + oneSample.getTime());
324: }
325: data = (int) (oneSample.getTime() * d.height / model.getMax());
326: currColor = ColorHelper.changeColorCyclicIncrement(currColor,
327: 40);
328: g.setColor(currColor);
329: if (!previousPtsAlloc) {
330: // If first dot, just draw the point
331: g.drawLine(x % width, d.height - data, x % width, d.height
332: - data - 1);
333: } else {
334: // Otherwise, draw from previous point
335: g.drawLine((previousPts[1].x) % width, previousPts[1].y, x
336: % width, d.height - data);
337: }
338: // Store load time without components
339: previousPts[1] = new Point(x % width, d.height - data);
340: if (noLegendYet) {
341: gbc.gridx = 0;
342: gbc.gridy = compCount++;
343: gbc.anchor = GridBagConstraints.WEST;
344: gbc.weightx = 1.0;
345: gbc.insets = new Insets(0, 10, 0, 0);
346: JLabel mainTimeLabel = new JLabel(oneSample.toString());
347:
348: mainTimeLabel.setForeground(currColor);
349: gridBag.setConstraints(mainTimeLabel, gbc);
350: legendPanel.add(mainTimeLabel);
351: }
352: lastLevel += data;
353: // Plot the times of the total times components
354: int currPreviousPts = 2;
355:
356: if (resultList != null) {
357: for (int i = 0; i < resultList.length; i++) {
358: SampleResult componentRes = resultList[i];
359:
360: if (log.isDebugEnabled()) {
361: log.debug("drawSample1 : componentRes - "
362: + componentRes.getSampleLabel()
363: + " loading time - "
364: + componentRes.getTime());
365: }
366: data = (int) (componentRes.getTime() * d.height / model
367: .getMax());
368: data += lastLevel;
369: currColor = ColorHelper.changeColorCyclicIncrement(
370: currColor, 100);
371: g.setColor(currColor);
372: if (!previousPtsAlloc) {
373: // If first dot, just draw the point
374: g.drawLine(x % width, d.height - data, x % width,
375: d.height - data - 1);
376: } else {
377: // Otherwise, draw from previous point
378: g.drawLine(
379: (previousPts[currPreviousPts].x) % width,
380: previousPts[currPreviousPts].y, x % width,
381: d.height - data);
382: }
383: // Store the current plot
384: previousPts[currPreviousPts++] = new Point(x % width,
385: d.height - data);
386: if (noLegendYet) {
387: gbc.gridx = 0;
388: gbc.gridy = compCount++;
389: gbc.anchor = GridBagConstraints.WEST;
390: gbc.weightx = 1.0;
391: gbc.insets = new Insets(0, 10, 0, 0);
392: JLabel compTimeLabel = new JLabel(componentRes
393: .getSampleLabel());
394:
395: compTimeLabel.setForeground(currColor);
396: gridBag.setConstraints(compTimeLabel, gbc);
397: legendPanel.add(compTimeLabel);
398: }
399: lastLevel = data;
400: }
401: }
402:
403: if (noLegendYet) {
404: noLegendYet = false;
405: lPanel.repaint();
406: lPanel.revalidate();
407: }
408:
409: // Set the previousPtsAlloc to true here and not after allocation
410: // because the rest of the codes also depend on previousPtsAlloc to be
411: // false if first time plotting the graph i.e. there are no previous
412: // points.
413: if (!previousPtsAlloc) {
414: previousPtsAlloc = true;
415: }
416: log.debug("End : drawSample1");
417: }
418: }
|