Upload files to AWS S3 in JMeter using Groovy

I use my personal AWS S3 to store all my personal and confidential documents. There are three primary reasons for choosing AWS S3: affordable, speed and reliable. If you are working on the AWS cloud, the usage of S3 is inevitable. S3 plays a critical role in storing objects in hot and cold storage. Sometimes you need to upload a payload or file objects to S3 programmatically via your performance test script. This blog article will help you to upload files to AWS S3 in JMeter using Groovy.

What is S3?

Amazon Simple Storage Service (Amazon S3) is an object storage service that offers industry-leading scalability, data availability, security, and performance.

S3 comes with various storage classes: S3 Standard, S3 Intelligent Tiering, S3 Glacier Instant Retrieval and more.

Prerequisites

The following are the prerequisites for upload files to AWS S3:

  • AWS Account
  • AWS IAM User with S3 access policy,
  • basic knowledge of AWS services,
  • basic knowledge of JMeter
  • basic knowledge of AWS SDK (Java)
  • file(s) to upload

AWS IAM User

Login into your AWS account and open IAM service. Click Users under Access Management.

Click Add users button to set the user details and credential type as shown below.

Enter jmeter-s3-access in User name and check Access key - Programmatic access.

This user will have access to the AWS service programmatically, not from the user console. Click Next: Permissions

In the Set permissions section, click Attach existing policies directly and filter the policies of S3 by typing s3.

For the demonstration purpose, let us go with AmazonS3FullAccess. Check AmazonS3FullAccess and then click Next: Tags. But for the production server, follow the zero trust framework.

Adding tags is optional, but it is recommended to have relevant key-pair values.

Click Review and then click on Create user.

Copy the Access key ID and Secret access key to a secured location. Alternatively, you can download the .csv file.

JMeter Test Plan

By default, JMeter doesn’t have the feature to upload the artifacts to AWS S3. To extend the functionality, we must leverage the JSR223 Sampler.

Here is the complete playlist of JMeter series which will help you to become a hero within 1 week.

JSR223 Sampler

It is not possible to write upload to S3 code block natively. We must leverage the latest version of AWS SDK for Java. To add the AWS SDK as a dependency to the JSR223 Sampler, the easiest way is to leverage Grape. Personally, I have not tried the JMeter Maven plugin. I found Grape is simple to get start.

What is Grape?

The Groovy Adaptable Packaging Engine or Groovy Advanced Packaging Engine, Grape, is a JAR dependency manager which is built-in with Groovy.

Using the @Grab annotations in JSR223, you can add maven repository dependencies to the classpath.

@Grab(group='software.amazon.awssdk', module='s3', version='2.17.172', scope='test')

The above annotation will download the AWS SDK S3 dependencies. To download multiple dependencies, use @Grapes annotations.

@Grapes(
    @Grab(group='software.amazon.awssdk', module='s3', version='2.17.172', scope='test'),
    @Grab(group='software.amazon.awssdk', module='sts', version='2.17.172', scope='test')

)

To change the source, use @GrabResolver annotation.

@GrabResolver(name='restlet', root='http://maven.restlet.org/')

Hello Time using Grape in JSR223

Let us understand how Grape works in JSR223 Sampler in JMeter by writing a simple snippet with @Grab annotations. To explain it with a simple Hello world, pardon Hello time example, let us use the below code snippet. Copy and paste the below code into your JSR223 Sampler in JMeter.

@Grapes(
    @Grab(group='joda-time', module='joda-time', version='2.10.14')
)
import org.joda.time.LocalDateTime

LocalDateTime currentDateTime = new LocalDateTime()
log.info "Local Time is " + currentDateTime

Let us slice each line. @Grapes is the optional inception annotation which has @Grab annotation. One or more @Grab annotations can be placed anywhere in the JSR223. In this example, @Grab annotation will manage the joda-time dependency and prints the current date and time in the Log Viewer. By default, it will download the dependencies from mvnrepository.com.

Click on the Run button in JMeter to see the log message in the Log Viewer. Once you click on Run button, the above script will download the dependencies from the source and keep the JARs in /Users/<user>/.groovy/grapes in Mac, C:\Users\<user>\.groovy\grapes in Windows OS.

The dependencies download will happen only for the first execution. Eventual execution in JMeter will be fast.

AWS S3 SDK

There are multiple methods available to upload the artifacts to S3, e.g. via AWS CLI, AWS SDK, HTTP requests and more. The official AWS SDK opens the door to building applications for AWS via its API.

By leveraging the AWS SDK for Java, it is easy to interact with the AWS services. The latest version of the AWS SDK for Java is v2. v2 is a major rewrite of v1. The v2 version is packed with a nonblocking I/O architecture using Netty where it achieves high concurrency with fewer threads. Also, it supports HTTP/2 and automatic pagination.

Upload files to S3 in Groovy

We are going to leverage Grape in Groovy in the JSR223 Sampler by adding AWS SDK for S3. Head to https://mvnrepository.com/artifact/software.amazon.awssdk/s3 and select the latest version. At this time of writing, the latest version is 2.17.172.

Click Grape and copy the annotation as shown below.

Upload files to AWS S3 in JMeter using Groovy - Grape
Upload files to AWS S3 in JMeter using Groovy – Grape

Then, copy and paste the below snippet into the JSR223 Sampler.

@Grapes(
    @Grab(group='software.amazon.awssdk', module='s3', version='2.17.172', scope='test')
)
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider
import software.amazon.awssdk.regions.Region
import software.amazon.awssdk.services.s3.S3Client
import software.amazon.awssdk.services.s3.model.*

import java.io.File
import java.nio.file.Paths

// Configurations
String accessKey = vars.get("AWS_ACCESS_KEY")
String secretKey = vars.get("AWS_SECRET_KEY")
String bucketName = vars.get("AWS_BUCKET_NAME")
String strFilename = "C:\\temp\\result.json"

try {
	// Set region
	Region region = Region.US_EAST_1
	
	// Create credentials
	AwsBasicCredentials awsCreds = AwsBasicCredentials.create(
	      accessKey,
	      secretKey)
	
	// Build S3 Client
	S3Client s3 = S3Client.builder()
	      .region(region)
	      .credentialsProvider(StaticCredentialsProvider.create(awsCreds))
	      .build()
	
	// Create file object
	File s3Obj = new File(strFilename)
	
	// Create PUT request
	PutObjectRequest request = PutObjectRequest.builder()
	      .bucket(bucketName)
	      .key(s3Obj.getName())
	      .build()
	
	// Upload file
	s3.putObject(request, Paths.get(strFilename))
}

// Catching exception and displaying it in the Sample result
catch (S3Exception e){
	SampleResult.setSuccessful(false)
	SampleResult.setResponseMessage(e.getMessage())
	SampleResult.setResponseCode(null)
}

Let us slice the above code.

  1. After adding the import statements, the first block represents the configurations such as AWS Access, Secret Key, and Bucket name.
  2. The next configuration is the AWS region.
  3. AwsBasicCredentials block creates the credentials to access the bucket
  4. S3Client block creates a S3 client using the AwsBasicCredentials credentials.
  5. s3Obj is the file to be uploaded.
  6. PutObjectRequest will build the request.
  7. s3.putObject will upload the file by leveraging the request.

If any exceptions occurs, JMeter will display the exceptions in the sampler with the exception details for troubleshooting.

The variables AWS_ACCESS_KEY, AWS_BUCKET_NAME, and AWS_SECRET_KEY is available in the Test Plan.

Here is the repository to download the sample JMeter test plan for your reference.

Never ever store the AWS credentials in the test plan. Pass the credentials via command line, environment variables, or programmatically generate them.

Congratulations! Now you know how to upload artifacts to S3 programmatically in JMeter using JSR223 Sampler.

Conclusion

Uploading artifacts to S3 is just the beginning. By leveraging the AWS SDK for Java, it is possible to interact with AWS services from JMeter using Groovy. E.g, after JMeter test execution, you can upload the results in a zip file using the above snippet. If you have any other usecases for JMeter, please let me know in the comments.

About the Author

5 thoughts on “Upload files to AWS S3 in JMeter using Groovy”

  1. Leverage Aws SDK and Aws Secrets Manager to pull credentials in runtime. So that one doesn’t hardcode or save values in scripts or even in git.

    Reply
  2. I have written a code in Java to upload PUT request in S3 Bucket.

    How can I use java code in Jmeter. I have tried the following and I am facing import failure in

    in file: inline evaluation of: “ import java.io.File;

    Reply
  3. I am trying to upload the json file into s3 bucket But i am getting the below error

    2022-11-01 09:46:10,150 INFO o.a.j.e.StandardJMeterEngine: Running the test!
    2022-11-01 09:46:10,150 INFO o.a.j.s.SampleEvent: List of sample_variables: []
    2022-11-01 09:46:10,169 INFO o.a.j.t.TestPlan: added C:\apache-jmeter-5.4.1\apache-jmeter-5.4.1\lib to classpath
    2022-11-01 09:46:10,169 INFO o.a.j.g.u.JMeterMenuBar: setRunning(true, *local*)
    2022-11-01 09:46:10,261 INFO o.a.j.e.StandardJMeterEngine: Starting ThreadGroup: 1 : Thread Group
    2022-11-01 09:46:10,261 INFO o.a.j.e.StandardJMeterEngine: Starting 1 threads for group Thread Group.
    2022-11-01 09:46:10,261 INFO o.a.j.e.StandardJMeterEngine: Thread will continue on error
    2022-11-01 09:46:10,261 INFO o.a.j.t.ThreadGroup: Starting thread group… number=1 threads=1 ramp-up=1 delayedStart=false
    2022-11-01 09:46:10,261 INFO o.a.j.t.ThreadGroup: Started thread group number 1
    2022-11-01 09:46:10,261 INFO o.a.j.e.StandardJMeterEngine: All thread groups have been started
    2022-11-01 09:46:10,261 INFO o.a.j.t.JMeterThread: Thread started: Thread Group 1-1
    2022-11-01 09:46:10,277 ERROR o.a.j.p.j.s.JSR223Sampler: Problem in JSR223 script JSR223 Sampler, message: javax.script.ScriptException: In file: inline evaluation of: “package com.orr.producer; import java.io.BufferedReader; import java.io.IOExcep . . . ” Encountered “[” at line 29, column 44.
    in inline evaluation of: “package com.orr.producer; import java.io.BufferedReader; import java.io.IOExcep . . . ” at line number 29
    javax.script.ScriptException: In file: inline evaluation of: “package com.orr.producer; import java.io.BufferedReader; import java.io.IOExcep . . . ” Encountered “[” at line 29, column 44.
    in inline evaluation of: “package com.orr.producer; import java.io.BufferedReader; import java.io.IOExcep . . . ” at line number 29
    at bsh.engine.BshScriptEngine.evalSource(BshScriptEngine.java:82) ~[bsh-2.0b6.jar:2.0b6 2016-02-05 05:16:19]
    at bsh.engine.BshScriptEngine.eval(BshScriptEngine.java:46) ~[bsh-2.0b6.jar:2.0b6 2016-02-05 05:16:19]
    at javax.script.AbstractScriptEngine.eval(AbstractScriptEngine.java:233) ~[java.scripting:?]
    at org.apache.jmeter.util.JSR223TestElement.processFileOrScript(JSR223TestElement.java:219) ~[ApacheJMeter_core.jar:5.4.3]
    at org.apache.jmeter.protocol.java.sampler.JSR223Sampler.sample(JSR223Sampler.java:72) [ApacheJMeter_java.jar:5.4.3]
    at org.apache.jmeter.threads.JMeterThread.doSampling(JMeterThread.java:638) [ApacheJMeter_core.jar:?]
    at org.apache.jmeter.threads.JMeterThread.executeSamplePackage(JMeterThread.java:558) [ApacheJMeter_core.jar:?]
    at org.apache.jmeter.threads.JMeterThread.processSampler(JMeterThread.java:489) [ApacheJMeter_core.jar:?]
    at org.apache.jmeter.threads.JMeterThread.run(JMeterThread.java:256) [ApacheJMeter_core.jar:?]
    at java.lang.Thread.run(Thread.java:834) [?:?]
    2022-11-01 09:46:10,277 INFO o.a.j.t.JMeterThread: Thread is done: Thread Group 1-1
    2022-11-01 09:46:10,277 INFO o.a.j.t.JMeterThread: Thread finished: Thread Group 1-1
    2022-11-01 09:46:10,277 INFO o.a.j.e.StandardJMeterEngine: Notifying test listeners of end of test
    2022-11-01 09:46:10,277 INFO o.a.j.g.u.JMeterMenuBar: setRunning(false, *local*)

    and this is the what i am using

    import java.io.IOException;
    import java.io.InputStream;
    import java.util.Properties;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.Scanner;
    import com.amazonaws.auth.AWSSessionCredentials;
    import com.amazonaws.auth.AWSStaticCredentialsProvider;
    import com.amazonaws.auth.BasicAWSCredentials;
    import com.amazonaws.services.s3.AmazonS3;
    import com.amazonaws.services.s3.AmazonS3ClientBuilder;
    import com.amazonaws.services.s3.model.AmazonS3Exception;
    import com.amazonaws.services.s3.model.GetObjectRequest;
    import com.amazonaws.services.s3.model.PutObjectRequest;
    import com.amazonaws.services.s3.model.PutObjectResult;
    import com.amazonaws.services.s3.model.S3Object;
    import com.amazonaws.services.s3.model.S3ObjectInputStream;
    import com.amazonaws.regions.Regions;
    import com.amazonaws.regions.Region;
    import com.amazonaws.services.s3.model.ObjectMetadata;
    import com.amazonaws.services.s3.transfer.Download;
    import com.amazonaws.services.s3.transfer.TransferManager;
    import com.amazonaws.services.s3.transfer.TransferManagerBuilder;
    import com.amazonaws.services.s3.transfer.Upload;

    String accessKey = “AKIAXWVJVMKEHXBWUYVD”;
    String secretKey = “x+goxAYsErfk5eoIgkcclEGhN1kIlr1Nnm6hOtZF”;
    String bucketName = “staging-bu-s3-orr-raw-data-us-east-1”; //specify bucketname
    String region = “us-east-1”;

    BasicAWSCredentials sessionCredentials = new BasicAWSCredentials(accessKey, secretKey);

    AmazonS3 s3 = AmazonS3ClientBuilder.standard()
    .withRegion(region)
    .withCredentials(new AWSStaticCredentialsProvider(sessionCredentials))
    .build();

    TransferManager xfer_mgr = TransferManagerBuilder.standard()
    .withS3Client(s3)
    .withDisableParallelDownloads(false)
    .build();

    File f = new File(“C:\\Users\\BEC\\data.json”); //specify path to your image
    String objectName = “data.json”; //provide a name for the image how you want your image to be shown i
    Upload xfer = xfer_mgr.upload(bucketName, objectName, f);
    xfer.waitForCompletion();
    xfer_mgr.shutdownNow();

    Reply
  4. Hi, I am using AWS Distributed load testing using Jmeter script. Cuurently in the S3 bucket only the logs and error files are displayed. But the CSV Result files from Jmeter(Save response to file) is not displayed. Is there a way I can see/ store the Jmeter result CSV file in S3 bucket?

    Reply

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Hamster - Launch JMeter Recent Test Plans SwiftlyDownload for free
+
Share via
Copy link