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.Rectangle;
025: import java.util.Arrays;
026: import java.util.Collection;
027:
028: import javax.swing.JComponent;
029: import javax.swing.Scrollable;
030:
031: import org.apache.jmeter.samplers.Clearable; // import org.apache.jorphan.logging.LoggingManager;
032: import org.apache.jorphan.math.NumberComparator;
033:
034: /**
035: * New graph for drawing distribution graph of the results. It is intended as a
036: * way to view the data after the stress has been performed. Although it can be
037: * used at runtime, it is not recommended, since it is rather intensive. The
038: * graph will draw a red line at 90% and an orange line at 50%. I like
039: * distribution graphs because they allow me to see how the data clumps. In
040: * general, the data will tend to clump in predictable ways when the application
041: * is well designed and implemented. Data that generates erratic graphs are
042: * generally not desirable.
043: *
044: */
045: public class DistributionGraph extends JComponent implements
046: Scrollable, Clearable {
047:
048: private SamplingStatCalculator model;
049:
050: private static int width = 600;
051:
052: private int xborder = 30;
053:
054: /**
055: * the granularity for the distribution graph. it can only be whole numbers.
056: */
057: // NOTREAD private int granularity = 5;
058: /**
059: * there's an option to delay drawing
060: */
061: // NOTREAD private boolean delay = false;
062: /**
063: * delay redraw
064: */
065: // NOTREAD private int counter = 20;
066: private static int total = -1;
067:
068: /**
069: * Constructor for the Graph object.
070: */
071: public DistributionGraph() {
072: init();
073: }
074:
075: /**
076: * Constructor for the Graph object.
077: */
078: public DistributionGraph(SamplingStatCalculator model) {
079: this ();
080: setModel(model);
081: }
082:
083: public void init() {
084: repaint();
085: }
086:
087: /**
088: * Gets the ScrollableTracksViewportWidth attribute of the Graph object.
089: *
090: * @return the ScrollableTracksViewportWidth value
091: */
092: public boolean getScrollableTracksViewportWidth() {
093: return true;
094: }
095:
096: /**
097: * Gets the ScrollableTracksViewportHeight attribute of the Graph object.
098: *
099: * @return the ScrollableTracksViewportHeight value
100: */
101: public boolean getScrollableTracksViewportHeight() {
102: return true;
103: }
104:
105: /**
106: * Sets the Model attribute of the Graph object.
107: */
108: private void setModel(Object model) {
109: this .model = (SamplingStatCalculator) model;
110: repaint();
111: }
112:
113: /**
114: * Gets the PreferredScrollableViewportSize attribute of the Graph object.
115: *
116: * @return the PreferredScrollableViewportSize value
117: */
118: public Dimension getPreferredScrollableViewportSize() {
119: return this .getPreferredSize();
120: }
121:
122: /**
123: * Gets the ScrollableUnitIncrement attribute of the Graph object.
124: *
125: * @return the ScrollableUnitIncrement value
126: */
127: public int getScrollableUnitIncrement(Rectangle visibleRect,
128: int orientation, int direction) {
129: return 5;
130: }
131:
132: /**
133: * Gets the ScrollableBlockIncrement attribute of the Graph object.
134: *
135: * @return the ScrollableBlockIncrement value
136: */
137: public int getScrollableBlockIncrement(Rectangle visibleRect,
138: int orientation, int direction) {
139: return (int) (visibleRect.width * .9);
140: }
141:
142: /**
143: * Clears this graph.
144: */
145: public void clearData() {
146: model.clear();
147: }
148:
149: /**
150: * Method is responsible for calling drawSample and updating the graph.
151: */
152: public void paintComponent(Graphics g) {
153: super .paintComponent(g);
154: final SamplingStatCalculator m = this .model;
155: synchronized (m) {
156: drawSample(m, g);
157: }
158: }
159:
160: private void drawSample(SamplingStatCalculator p_model, Graphics g) {
161: width = getWidth();
162: double height = getHeight() - 1.0;
163:
164: // first lets draw the grid
165: for (int y = 0; y < 4; y++) {
166: int q1 = (int) (height - (height * 0.25 * y));
167: g.setColor(Color.lightGray);
168: g.drawLine(xborder, q1, width, q1);
169: g.setColor(Color.black);
170: g.drawString(String.valueOf((25 * y) + "%"), 0, q1);
171: }
172: g.setColor(Color.black);
173: // draw the X axis
174: g.drawLine(xborder, (int) height, width, (int) height);
175: // draw the Y axis
176: g.drawLine(xborder, 0, xborder, (int) height);
177: // the test plan has to have more than 200 samples
178: // for it to generate half way decent distribution
179: // graph. the larger the sample, the better the
180: // results.
181: if (p_model != null && p_model.getCount() > 50) {
182: // now draw the bar chart
183: Number ninety = p_model.getPercentPoint(0.90);
184: Number fifty = p_model.getPercentPoint(0.50);
185:
186: total = p_model.getCount();
187: Collection values = p_model.getDistribution().values();
188: Object[] objval = new Object[values.size()];
189: objval = values.toArray(objval);
190: // we sort the objects
191: Arrays.sort(objval, new NumberComparator());
192: int len = objval.length;
193: for (int count = 0; count < len; count++) {
194: // calculate the height
195: Number[] num = (Number[]) objval[count];
196: double iper = (double) num[1].intValue()
197: / (double) total;
198: double iheight = height * iper;
199: // if the height is less than one, we set it
200: // to one pixel
201: if (iheight < 1) {
202: iheight = 1.0;
203: }
204: int ix = (count * 4) + xborder + 5;
205: int dheight = (int) (height - iheight);
206: g.setColor(Color.blue);
207: g.drawLine(ix - 1, (int) height, ix - 1, dheight);
208: g.drawLine(ix, (int) height, ix, dheight);
209: g.setColor(Color.black);
210: // draw a red line for 90% point
211: if (num[0].longValue() == ninety.longValue()) {
212: g.setColor(Color.red);
213: g.drawLine(ix, (int) height, ix, 55);
214: g.drawLine(ix, 35, ix, 0);
215: g.drawString("90%", ix - 30, 20);
216: g.drawString(String.valueOf(num[0].longValue()),
217: ix + 8, 20);
218: }
219: // draw an orange line for 50% point
220: if (num[0].longValue() == fifty.longValue()) {
221: g.setColor(Color.orange);
222: g.drawLine(ix, (int) height, ix, 30);
223: g.drawString("50%", ix - 30, 50);
224: g.drawString(String.valueOf(num[0].longValue()),
225: ix + 8, 50);
226: }
227: }
228: }
229: }
230: }
|