1 /* 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package org.apache.jmeter.samplers; 20 21 import java.io.Serializable; 22 import java.io.UnsupportedEncodingException; 23 import java.net.HttpURLConnection; 24 import java.net.URL; 25 import java.util.ArrayList; 26 import java.util.HashSet; 27 import java.util.List; 28 import java.util.Set; 29 import java.lang.reflect.Method; 30 import java.lang.reflect.InvocationTargetException; 31 32 import org.apache.avalon.framework.configuration.Configuration; 33 import org.apache.jmeter.assertions.AssertionResult; 34 import org.apache.jmeter.util.JMeterUtils; 35 import org.apache.jorphan.logging.LoggingManager; 36 import org.apache.jorphan.util.JOrphanUtils; 37 import org.apache.log.Logger; 38 39 // For unit tests, @see TestSampleResult 40 41 /** 42 * This is a nice packaging for the various information returned from taking a 43 * sample of an entry. 44 * 45 */ 46 public class SampleResult implements Serializable { 47 48 private static final long serialVersionUID = 233L; 49 50 // Needs to be accessible from Test code 51 static final Logger log = LoggingManager.getLoggerForClass(); 52 53 /** 54 * The default encoding to be used if not overridden. 55 * The value is ISO-8859-1. 56 */ 57 public static final String DEFAULT_HTTP_ENCODING = "ISO-8859-1"; // $NON-NLS-1$ 58 59 // Bug 33196 - encoding ISO-8859-1 is only suitable for Western countries 60 // However the suggested System.getProperty("file.encoding") is Cp1252 on 61 // Windows 62 // So use a new property with the original value as default 63 // needs to be accessible from test code 64 /** 65 * The default encoding to be used to decode the responseData byte array. 66 * The value is defined by the property "sampleresult.default.encoding" 67 * with a default of DEFAULT_HTTP_ENCODING if that is not defined. 68 */ 69 static final String DEFAULT_ENCODING 70 = JMeterUtils.getPropDefault("sampleresult.default.encoding", // $NON-NLS-1$ 71 DEFAULT_HTTP_ENCODING); 72 73 /** 74 * Data type value indicating that the response data is text. 75 * 76 * @see #getDataType 77 * @see #setDataType(java.lang.String) 78 */ 79 public final static String TEXT = "text"; // $NON-NLS-1$ 80 81 /** 82 * Data type value indicating that the response data is binary. 83 * 84 * @see #getDataType 85 * @see #setDataType(java.lang.String) 86 */ 87 public final static String BINARY = "bin"; // $NON-NLS-1$ 88 89 /* empty arrays which can be returned instead of null */ 90 private static final byte[] EMPTY_BA = new byte[0]; 91 92 private static final SampleResult[] EMPTY_SR = new SampleResult[0]; 93 94 private static final AssertionResult[] EMPTY_AR = new AssertionResult[0]; 95 96 private SampleSaveConfiguration saveConfig; 97 98 private SampleResult parent = null; 99 100 /** 101 * @param propertiesToSave 102 * The propertiesToSave to set. 103 */ 104 public void setSaveConfig(SampleSaveConfiguration propertiesToSave) { 105 this.saveConfig = propertiesToSave; 106 } 107 108 public SampleSaveConfiguration getSaveConfig() { 109 return saveConfig; 110 } 111 112 private byte[] responseData = EMPTY_BA; 113 114 private String responseCode = "";// Never return null 115 116 private String label = "";// Never return null 117 118 private String resultFileName = ""; // Filename used by ResultSaver 119 120 private String samplerData; // The data used by the sampler 121 122 private String threadName = ""; // Never return null 123 124 private String responseMessage = ""; 125 126 private String responseHeaders = ""; // Never return null 127 128 private String contentType = ""; // e.g. text/html; charset=utf-8 129 130 private String requestHeaders = ""; 131 132 // TODO timeStamp == 0 means either not yet initialised or no stamp available (e.g. when loading a results file) 133 private long timeStamp = 0;// the time stamp - can be start or end 134 135 private long startTime = 0; 136 137 private long endTime = 0; 138 139 private long idleTime = 0;// Allow for non-sample time 140 141 private long pauseTime = 0;// Start of pause (if any) 142 143 private List assertionResults; 144 145 private List subResults; 146 147 private String dataType=""; // Don't return null if not set 148 149 private boolean success; 150 151 //@GuardedBy("this"") 152 private final Set files = new HashSet(); // files that this sample has been saved in 153 154 private String dataEncoding;// (is this really the character set?) e.g. 155 // ISO-8895-1, UTF-8 156 157 private static Method initNanoTimeMethod() { 158 try { 159 return System.class.getMethod("nanoTime", null); 160 } catch (NoSuchMethodException e) { 161 return null; 162 } 163 } 164 165 private static boolean haveNanoTime() { 166 return nanoTimeMethod != null; 167 } 168 169 private static long nanoTime() { 170 Long result = null; 171 try { 172 result = (Long) nanoTimeMethod.invoke(null, null); 173 } catch (IllegalAccessException e) { 174 throw new RuntimeException(e); 175 } catch (InvocationTargetException e) { 176 throw new RuntimeException(e); 177 } 178 return result.longValue(); 179 } 180 181 private static final Method nanoTimeMethod = initNanoTimeMethod(); 182 183 // a reference time from the nanosecond clock 184 private static final long referenceTimeNsClock = haveNanoTime() ? sampleNsClockInMs() : Long.MIN_VALUE; 185 186 // a reference time from the millisecond clock 187 private static final long referenceTimeMsClock = System.currentTimeMillis(); 188 189 private long time = 0; // elapsed time 190 191 private long latency = 0; // time to first response 192 193 private boolean stopThread = false; // Should thread terminate? 194 195 private boolean stopTest = false; // Should test terminate? 196 197 private boolean stopTestNow = false; // Should test terminate abruptly? 198 199 private boolean isMonitor = false; 200 201 private int sampleCount = 1; 202 203 private int bytes = 0; // Allows override of sample size in case sampler does not want to store all the data 204 205 private volatile int groupThreads = 0; // Active threads in this thread group 206 207 private volatile int allThreads = 0; // Active threads in all thread groups 208 209 // TODO do contentType and/or dataEncoding belong in HTTPSampleResult instead? 210 211 private final static String TOTAL_TIME = "totalTime"; // $NON-NLS-1$ 212 213 private static final boolean startTimeStamp 214 = JMeterUtils.getPropDefault("sampleresult.timestamp.start", false); // $NON-NLS-1$ 215 216 static { 217 if (startTimeStamp) { 218 log.info("Note: Sample TimeStamps are START times"); 219 } else { 220 log.info("Note: Sample TimeStamps are END times"); 221 } 222 log.info("sampleresult.default.encoding is set to " + DEFAULT_ENCODING); 223 } 224 225 public SampleResult() { 226 time = 0; 227 } 228 229 /** 230 * Construct a 'parent' result for an already-existing result, essentially 231 * cloning it 232 * 233 * @param res 234 * existing sample result 235 */ 236 public SampleResult(SampleResult res) { 237 //TODO - why not just copy all the fields? Do we need the calculations that some of the set() methods perform? 238 //TODO - why are the following not copied: 239 // assertionResults, bytes, idleTime, latency, parent,pauseTime,resultFileName,sampleCount,samplerData,saveConfig 240 // stopTest, stopThread, subResults,threadName 241 setStartTime(res.getStartTime()); 242 setEndTime(res.getStartTime()); 243 // was setElapsed(0) which is the same as setStartTime=setEndTime=now 244 245 setSampleLabel(res.getSampleLabel()); 246 setRequestHeaders(res.getRequestHeaders()); 247 setResponseData(res.getResponseData()); 248 setResponseCode(res.getResponseCode()); 249 setSuccessful(res.isSuccessful()); 250 setResponseMessage(res.getResponseMessage()); 251 setDataType(res.getDataType()); 252 setResponseHeaders(res.getResponseHeaders()); 253 setContentType(res.getContentType()); 254 setDataEncoding(res.getDataEncodingNoDefault()); 255 setURL(res.getURL()); 256 257 setGroupThreads(res.getGroupThreads()); 258 setAllThreads(res.getAllThreads()); 259 260 addSubResult(res); // this will add res.getTime() to getTime(). 261 } 262 263 public boolean isStampedAtStart() { 264 return startTimeStamp; 265 } 266 267 /** 268 * Create a sample with a specific elapsed time but don't allow the times to 269 * be changed later 270 * 271 * (only used by HTTPSampleResult) 272 * 273 * @param elapsed 274 * time 275 * @param atend 276 * create the sample finishing now, else starting now 277 */ 278 protected SampleResult(long elapsed, boolean atend) { 279 long now = currentTimeInMs(); 280 if (atend) { 281 setTimes(now - elapsed, now); 282 } else { 283 setTimes(now, now + elapsed); 284 } 285 } 286 287 /** 288 * Create a sample with specific start and end times for test purposes, but 289 * don't allow the times to be changed later 290 * 291 * (used by StatVisualizerModel.Test) 292 * 293 * @param start 294 * start time 295 * @param end 296 * end time 297 */ 298 public static SampleResult createTestSample(long start, long end) { 299 SampleResult res = new SampleResult(); 300 res.setStartTime(start); 301 res.setEndTime(end); 302 return res; 303 } 304 305 /** 306 * Create a sample with a specific elapsed time for test purposes, but don't 307 * allow the times to be changed later 308 * 309 * @param elapsed - 310 * desired elapsed time 311 */ 312 public static SampleResult createTestSample(long elapsed) { 313 long now = currentTimeInMs(); 314 return createTestSample(now, now + elapsed); 315 } 316 317 /** 318 * Allow users to create a sample with specific timestamp and elapsed times 319 * for cloning purposes, but don't allow the times to be changed later 320 * 321 * Currently used by OldSaveService, CSVSaveService and StatisticalSampleResult 322 * 323 * @param stamp - 324 * this may be a start time or an end time 325 * @param elapsed 326 */ 327 public SampleResult(long stamp, long elapsed) { 328 stampAndTime(stamp, elapsed); 329 } 330 331 private static long sampleNsClockInMs() { 332 return nanoTime() / 1000000; 333 } 334 335 // Helper method to get 1 ms resolution timing. 336 public static long currentTimeInMs() { 337 if (haveNanoTime()) { 338 long elapsedInMs = sampleNsClockInMs() - referenceTimeNsClock; 339 return referenceTimeMsClock + elapsedInMs; 340 } else { 341 return System.currentTimeMillis(); 342 } 343 } 344 345 // Helper method to maintain timestamp relationships 346 private void stampAndTime(long stamp, long elapsed) { 347 if (startTimeStamp) { 348 startTime = stamp; 349 endTime = stamp + elapsed; 350 } else { 351 startTime = stamp - elapsed; 352 endTime = stamp; 353 } 354 timeStamp = stamp; 355 time = elapsed; 356 } 357 358 /* 359 * For use by SaveService only. 360 * 361 * @param stamp - 362 * this may be a start time or an end time 363 * @param elapsed 364 */ 365 public void setStampAndTime(long stamp, long elapsed) { 366 if (startTime != 0 || endTime != 0){ 367 throw new RuntimeException("Calling setStampAndTime() after start/end times have been set"); 368 } 369 stampAndTime(stamp, elapsed); 370 } 371 372 /** 373 * Method to set the elapsed time for a sample. Retained for backward 374 * compatibility with 3rd party add-ons. 375 * It is assumed that the method is only called at the end of a sample 376 * and that timeStamps are end-times 377 * 378 * Also used by SampleResultConverter when creating results from files. 379 * 380 * Must not be used in conjunction with sampleStart()/End() 381 * 382 * @deprecated use sampleStart() and sampleEnd() instead 383 * @param elapsed 384 * time in milliseconds 385 */ 386 public void setTime(long elapsed) { 387 if (startTime != 0 || endTime != 0){ 388 throw new RuntimeException("Calling setTime() after start/end times have been set"); 389 } 390 long now = currentTimeInMs(); 391 setTimes(now - elapsed, now); 392 } 393 394 /** 395 * Set the "marked" flag to show that the result has been written to the file. 396 * 397 * @param filename 398 * @return true if the result was previously marked 399 */ 400 public synchronized boolean markFile(String filename) { 401 return !files.add(filename); 402 } 403 404 public String getResponseCode() { 405 return responseCode; 406 } 407 408 private static final String OK = Integer.toString(HttpURLConnection.HTTP_OK); 409 410 /** 411 * Set response code to OK, i.e. "200" 412 * 413 */ 414 public void setResponseCodeOK(){ 415 responseCode=OK; 416 } 417 418 public void setResponseCode(String code) { 419 responseCode = code; 420 } 421 422 public boolean isResponseCodeOK(){ 423 return responseCode.equals(OK); 424 } 425 public String getResponseMessage() { 426 return responseMessage; 427 } 428 429 public void setResponseMessage(String msg) { 430 responseMessage = msg; 431 } 432 433 public void setResponseMessageOK() { 434 responseMessage = "OK"; // $NON-NLS-1$ 435 } 436 437 public String getThreadName() { 438 return threadName; 439 } 440 441 public void setThreadName(String threadName) { 442 this.threadName = threadName; 443 } 444 445 /** 446 * Get the sample timestamp, which may be either the start time or the end time. 447 * 448 * @see #getStartTime() 449 * @see #getEndTime() 450 * 451 * @return timeStamp in milliseconds 452 */ 453 public long getTimeStamp() { 454 return timeStamp; 455 } 456 457 public String getSampleLabel() { 458 return label; 459 } 460 461 /** 462 * Get the sample label for use in summary reports etc. 463 * 464 * @param includeGroup whether to include the thread group name 465 * @return the label 466 */ 467 public String getSampleLabel(boolean includeGroup) { 468 if (includeGroup) { 469 StringBuffer sb = new StringBuffer(threadName.substring(0,threadName.lastIndexOf(" "))); //$NON-NLS-1$ 470 return sb.append(":").append(label).toString(); //$NON-NLS-1$ 471 } 472 return label; 473 } 474 475 public void setSampleLabel(String label) { 476 this.label = label; 477 } 478 479 public void addAssertionResult(AssertionResult assertResult) { 480 if (assertionResults == null) { 481 assertionResults = new ArrayList(); 482 } 483 assertionResults.add(assertResult); 484 } 485 486 /** 487 * Gets the assertion results associated with this sample. 488 * 489 * @return an array containing the assertion results for this sample. 490 * Returns empty array if there are no assertion results. 491 */ 492 public AssertionResult[] getAssertionResults() { 493 if (assertionResults == null) { 494 return EMPTY_AR; 495 } 496 return (AssertionResult[]) assertionResults.toArray(new AssertionResult[0]); 497 } 498 499 public void addSubResult(SampleResult subResult) { 500 String tn = getThreadName(); 501 if (tn.length()==0) { 502 tn=Thread.currentThread().getName();//TODO do this more efficiently 503 this.setThreadName(tn); 504 } 505 subResult.setThreadName(tn); 506 if (subResults == null) { 507 subResults = new ArrayList(); 508 } 509 subResults.add(subResult); 510 // Extend the time to the end of the added sample 511 setEndTime(Math.max(getEndTime(), subResult.getEndTime())); 512 // Include the byte count for the added sample 513 setBytes(getBytes() + subResult.getBytes()); 514 subResult.setParent(this); 515 } 516 517 /** 518 * Add a subresult read from a results file. 519 * 520 * As for addSubResult(), except that the fields don't need to be accumulated 521 * 522 * @param subResult 523 */ 524 public void storeSubResult(SampleResult subResult) { 525 if (subResults == null) { 526 subResults = new ArrayList(); 527 } 528 subResults.add(subResult); 529 subResult.setParent(this); 530 } 531 532 /** 533 * Gets the subresults associated with this sample. 534 * 535 * @return an array containing the subresults for this sample. Returns an 536 * empty array if there are no subresults. 537 */ 538 public SampleResult[] getSubResults() { 539 if (subResults == null) { 540 return EMPTY_SR; 541 } 542 return (SampleResult[]) subResults.toArray(new SampleResult[0]); 543 } 544 545 public void configure(Configuration info) { 546 time = info.getAttributeAsLong(TOTAL_TIME, 0L); 547 } 548 549 /** 550 * Sets the responseData attribute of the SampleResult object. 551 * 552 * If the parameter is null, then the responseData is set to an empty byte array. 553 * This ensures that getResponseData() can never be null. 554 * 555 * @param response 556 * the new responseData value 557 */ 558 public void setResponseData(byte[] response) { 559 responseData = response == null ? EMPTY_BA : response; 560 } 561 562 /** 563 * Sets the responseData attribute of the SampleResult object. 564 * Should only be called after setting the dataEncoding (if necessary) 565 * 566 * @param response 567 * the new responseData value (String) 568 * 569 * @deprecated - only intended for use from BeanShell code 570 */ 571 public void setResponseData(String response) { 572 try { 573 responseData = response.getBytes(getDataEncodingWithDefault()); 574 } catch (UnsupportedEncodingException e) { 575 log.warn("Could not convert string, using default encoding. "+e.getLocalizedMessage()); 576 responseData = response.getBytes(); 577 } 578 } 579 580 /** 581 * Gets the responseData attribute of the SampleResult object. 582 * <p> 583 * Note that some samplers may not store all the data, in which case 584 * getResponseData().length will be incorrect. 585 * 586 * Instead, always use {@link #getBytes()} to obtain the sample result byte count. 587 * </p> 588 * @return the responseData value (cannot be null) 589 */ 590 public byte[] getResponseData() { 591 return responseData; 592 } 593 594 /** 595 * Gets the responseData of the SampleResult object as a String 596 * 597 * @return the responseData value as a String, converted according to the encoding 598 */ 599 public String getResponseDataAsString() { 600 try { 601 return new String(responseData,getDataEncodingWithDefault()); 602 } catch (UnsupportedEncodingException e) { 603 log.warn("Using platform default as "+getDataEncodingWithDefault()+" caused "+e); 604 return new String(responseData); 605 } 606 } 607 608 public void setSamplerData(String s) { 609 samplerData = s; 610 } 611 612 public String getSamplerData() { 613 return samplerData; 614 } 615 616 /** 617 * Get the time it took this sample to occur. 618 * 619 * @return elapsed time in milliseonds 620 * 621 */ 622 public long getTime() { 623 return time; 624 } 625 626 public boolean isSuccessful() { 627 return success; 628 } 629 630 public void setDataType(String dataType) { 631 this.dataType = dataType; 632 } 633 634 public String getDataType() { 635 return dataType; 636 } 637 /** 638 * Extract and save the DataEncoding and DataType from the parameter provided. 639 * Does not save the full content Type. 640 * @see #setContentType(String) which should be used to save the full content-type string 641 * 642 * @param ct - content type (may be null) 643 */ 644 public void setEncodingAndType(String ct){ 645 if (ct != null) { 646 // Extract charset and store as DataEncoding 647 // N.B. The meta tag: 648 // <META http-equiv="content-type" content="text/html; charset=foobar"> 649 // is now processed by HTTPSampleResult#getDataEncodingWithDefault 650 final String CS_PFX = "charset="; // $NON-NLS-1$ 651 int cset = ct.toLowerCase(java.util.Locale.ENGLISH).indexOf(CS_PFX); 652 if (cset >= 0) { 653 // TODO - assumes charset is not followed by anything else 654 String charSet = ct.substring(cset + CS_PFX.length()); 655 // Check for quoted string 656 if (charSet.startsWith("\"")){ // $NON-NLS-1$ 657 setDataEncoding(charSet.substring(1, charSet.length()-1)); // remove quotes 658 } else { 659 setDataEncoding(charSet); 660 } 661 } 662 if (isBinaryType(ct)) { 663 setDataType(BINARY); 664 } else { 665 setDataType(TEXT); 666 } 667 } 668 } 669 670 // List of types that are known to be binary 671 private static final String[] BINARY_TYPES = { 672 "image/", //$NON-NLS-1$ 673 "audio/", //$NON-NLS-1$ 674 "video/", //$NON-NLS-1$ 675 }; 676 677 /* 678 * Determine if content-type is known to be binary, i.e. not displayable as text. 679 * 680 * @param ct content type 681 * @return true if content-type is of type binary. 682 */ 683 private static boolean isBinaryType(String ct){ 684 for (int i = 0; i < BINARY_TYPES.length; i++){ 685 if (ct.startsWith(BINARY_TYPES[i])){ 686 return true; 687 } 688 } 689 return false; 690 } 691 692 /** 693 * Sets the successful attribute of the SampleResult object. 694 * 695 * @param success 696 * the new successful value 697 */ 698 public void setSuccessful(boolean success) { 699 this.success = success; 700 } 701 702 /** 703 * Returns the display name. 704 * 705 * @return display name of this sample result 706 */ 707 public String toString() { 708 return getSampleLabel(); 709 } 710 711 /** 712 * Returns the dataEncoding or the default if no dataEncoding was provided 713 * 714 * @deprecated use getDataEncodingWithDefault() or getDataEncodingNoDefault() as needed. 715 */ 716 public String getDataEncoding() { 717 if (dataEncoding != null) { 718 return dataEncoding; 719 } 720 return DEFAULT_ENCODING; 721 } 722 723 /** 724 * Returns the dataEncoding or the default if no dataEncoding was provided 725 * @return the value of the dataEncoding or DEFAULT_ENCODING 726 */ 727 public String getDataEncodingWithDefault() { 728 if (dataEncoding != null && dataEncoding.length() > 0) { 729 return dataEncoding; 730 } 731 return DEFAULT_ENCODING; 732 } 733 734 /** 735 * Returns the dataEncoding. May be null or the empty String. 736 * @return the value of the dataEncoding 737 */ 738 public String getDataEncodingNoDefault() { 739 return dataEncoding; 740 } 741 742 /** 743 * Sets the dataEncoding. 744 * 745 * @param dataEncoding 746 * the dataEncoding to set, e.g. ISO-8895-1, UTF-8 747 */ 748 public void setDataEncoding(String dataEncoding) { 749 this.dataEncoding = dataEncoding; 750 } 751 752 /** 753 * @return whether to stop the test 754 */ 755 public boolean isStopTest() { 756 return stopTest; 757 } 758 759 /** 760 * @return whether to stop the test now 761 */ 762 public boolean isStopTestNow() { 763 return stopTestNow; 764 } 765 766 /** 767 * @return whether to stop this thread 768 */ 769 public boolean isStopThread() { 770 return stopThread; 771 } 772 773 public void setStopTest(boolean b) { 774 stopTest = b; 775 } 776 777 public void setStopTestNow(boolean b) { 778 stopTestNow = b; 779 } 780 781 public void setStopThread(boolean b) { 782 stopThread = b; 783 } 784 785 /** 786 * @return the request headers 787 */ 788 public String getRequestHeaders() { 789 return requestHeaders; 790 } 791 792 /** 793 * @return the response headers 794 */ 795 public String getResponseHeaders() { 796 return responseHeaders; 797 } 798 799 /** 800 * @param string - 801 * request headers 802 */ 803 public void setRequestHeaders(String string) { 804 requestHeaders = string; 805 } 806 807 /** 808 * @param string - 809 * response headers 810 */ 811 public void setResponseHeaders(String string) { 812 responseHeaders = string; 813 } 814 815 /** 816 * @return the full content type - e.g. text/html [;charset=utf-8 ] 817 */ 818 public String getContentType() { 819 return contentType; 820 } 821 822 /** 823 * Get the media type from the Content Type 824 * @return the media type - e.g. text/html (without charset, if any) 825 */ 826 public String getMediaType() { 827 return JOrphanUtils.trim(contentType," ;").toLowerCase(java.util.Locale.ENGLISH); 828 } 829 830 /** 831 * Stores the content-type string, e.g. "text/xml; charset=utf-8" 832 * @see #setEncodingAndType(String) which can be used to extract the charset. 833 * 834 * @param string 835 */ 836 public void setContentType(String string) { 837 contentType = string; 838 } 839 840 /** 841 * @return idleTime 842 */ 843 public long getIdleTime() { 844 return idleTime; 845 } 846 847 /** 848 * @return the end time 849 */ 850 public long getEndTime() { 851 return endTime; 852 } 853 854 /** 855 * @return the start time 856 */ 857 public long getStartTime() { 858 return startTime; 859 } 860 861 /* 862 * Helper methods N.B. setStartTime must be called before setEndTime 863 * 864 * setStartTime is used by HTTPSampleResult to clone the parent sampler and 865 * allow the original start time to be kept 866 */ 867 protected final void setStartTime(long start) { 868 startTime = start; 869 if (startTimeStamp) { 870 timeStamp = startTime; 871 } 872 } 873 874 protected void setEndTime(long end) { 875 endTime = end; 876 if (!startTimeStamp) { 877 timeStamp = endTime; 878 } 879 if (startTime == 0) { 880 log.error("setEndTime must be called after setStartTime", new Throwable("Invalid call sequence")); 881 // TODO should this throw an error? 882 } else { 883 time = endTime - startTime - idleTime; 884 } 885 } 886 887 private void setTimes(long start, long end) { 888 setStartTime(start); 889 setEndTime(end); 890 } 891 892 /** 893 * Record the start time of a sample 894 * 895 */ 896 public void sampleStart() { 897 if (startTime == 0) { 898 setStartTime(currentTimeInMs()); 899 } else { 900 log.error("sampleStart called twice", new Throwable("Invalid call sequence")); 901 } 902 } 903 904 /** 905 * Record the end time of a sample and calculate the elapsed time 906 * 907 */ 908 public void sampleEnd() { 909 if (endTime == 0) { 910 setEndTime(currentTimeInMs()); 911 } else { 912 log.error("sampleEnd called twice", new Throwable("Invalid call sequence")); 913 } 914 } 915 916 /** 917 * Pause a sample 918 * 919 */ 920 public void samplePause() { 921 if (pauseTime != 0) { 922 log.error("samplePause called twice", new Throwable("Invalid call sequence")); 923 } 924 pauseTime = currentTimeInMs(); 925 } 926 927 /** 928 * Resume a sample 929 * 930 */ 931 public void sampleResume() { 932 if (pauseTime == 0) { 933 log.error("sampleResume without samplePause", new Throwable("Invalid call sequence")); 934 } 935 idleTime += currentTimeInMs() - pauseTime; 936 pauseTime = 0; 937 } 938 939 /** 940 * When a Sampler is working as a monitor 941 * 942 * @param monitor 943 */ 944 public void setMonitor(boolean monitor) { 945 isMonitor = monitor; 946 } 947 948 /** 949 * If the sampler is a monitor, method will return true. 950 * 951 * @return true if the sampler is a monitor 952 */ 953 public boolean isMonitor() { 954 return isMonitor; 955 } 956 957 /** 958 * For the JMS sampler, it can perform multiple samples for greater degree 959 * of accuracy. 960 * 961 * @param count 962 */ 963 public void setSampleCount(int count) { 964 sampleCount = count; 965 } 966 967 /** 968 * return the sample count. by default, the value is 1. 969 * 970 * @return the sample count 971 */ 972 public int getSampleCount() { 973 return sampleCount; 974 } 975 976 /** 977 * Returns the count of errors. 978 * 979 * @return 0 - or 1 if the sample failed 980 */ 981 public int getErrorCount(){ 982 return success ? 0 : 1; 983 } 984 985 public void setErrorCount(int i){// for reading from CSV files 986 // ignored currently 987 } 988 /* 989 * TODO: error counting needs to be sorted out after 2.3 final. 990 * At present the Statistical Sampler tracks errors separately 991 * It would make sense to move the error count here, but this would 992 * mean lots of changes. 993 * It's also tricky maintaining the count - it can't just be incremented/decremented 994 * when the success flag is set as this may be done multiple times. 995 * The work-round for now is to do the work in the StatisticalSampleResult, 996 * which overrides this method. 997 * Note that some JMS samplers also create samples with > 1 sample count 998 * Also the Transaction Controller probably needs to be changed to do 999 * proper sample and error accounting. 1000 * The purpose of this work-round is to allow at least minimal support for 1001 * errors in remote statistical batch mode. 1002 * 1003 */ 1004 /** 1005 * In the event the sampler does want to pass back the actual contents, we 1006 * still want to calculate the throughput. The bytes is the bytes of the 1007 * response data. 1008 * 1009 * @param length 1010 */ 1011 public void setBytes(int length) { 1012 bytes = length; 1013 } 1014 1015 /** 1016 * return the bytes returned by the response. 1017 * 1018 * @return byte count 1019 */ 1020 public int getBytes() { 1021 return bytes == 0 ? responseData.length : bytes; 1022 } 1023 1024 /** 1025 * @return Returns the latency. 1026 */ 1027 public long getLatency() { 1028 return latency; 1029 } 1030 1031 /** 1032 * Set the time to the first response 1033 * 1034 */ 1035 public void latencyEnd() { 1036 latency = currentTimeInMs() - startTime - idleTime; 1037 } 1038 1039 /** 1040 * This is only intended for use by SampleResultConverter! 1041 * 1042 * @param latency 1043 * The latency to set. 1044 */ 1045 public void setLatency(long latency) { 1046 this.latency = latency; 1047 } 1048 1049 /** 1050 * This is only intended for use by SampleResultConverter! 1051 * 1052 * @param timeStamp 1053 * The timeStamp to set. 1054 */ 1055 public void setTimeStamp(long timeStamp) { 1056 this.timeStamp = timeStamp; 1057 } 1058 1059 private URL location; 1060 1061 public void setURL(URL location) { 1062 this.location = location; 1063 } 1064 1065 public URL getURL() { 1066 return location; 1067 } 1068 1069 /** 1070 * Get a String representation of the URL (if defined). 1071 * 1072 * @return ExternalForm of URL, or empty string if url is null 1073 */ 1074 public String getUrlAsString() { 1075 return location == null ? "" : location.toExternalForm(); 1076 } 1077 1078 /** 1079 * @return Returns the parent. 1080 */ 1081 public SampleResult getParent() { 1082 return parent; 1083 } 1084 1085 /** 1086 * @param parent 1087 * The parent to set. 1088 */ 1089 public void setParent(SampleResult parent) { 1090 this.parent = parent; 1091 } 1092 1093 public String getResultFileName() { 1094 return resultFileName; 1095 } 1096 1097 public void setResultFileName(String resultFileName) { 1098 this.resultFileName = resultFileName; 1099 } 1100 1101 public int getGroupThreads() { 1102 return groupThreads; 1103 } 1104 1105 public void setGroupThreads(int n) { 1106 this.groupThreads = n; 1107 } 1108 1109 public int getAllThreads() { 1110 return allThreads; 1111 } 1112 1113 public void setAllThreads(int n) { 1114 this.allThreads = n; 1115 } 1116 }