Multipart FileUpload – Handling File Upload in JMeter

A while back, I wrote a blog about the breaking changes between JAX-RS 1.1 and JAX-RS 2.0 with regards to multipart fileupload. In it, I discussed a generic solution that both works for JAX-RS 1.1 and JAX-RS 2.0. In this blog I like to take the next step, and highlight a way of testing a REST-endpoint for multipart fileupload, in JMeter.

Why JMeter, you might wonder? Simply because our project uses automated JMeter tests for testing backend SOAP and REST services. Just sticking to the program here.

The use case

The system in question consists of many separate SOAP services, which process incoming messages from external parties. The application processing these messages is a Java EE application running on Liberty on z/OS. The SOAP services are orchestrated by IBM’s Business Process Management (BPM) tool, and most of these processes are short-lived.

They are so-called Straight-Through-Processing (STP) processes, i.e. highly automated processes with as little human intervention as possible.

One of the flows in the application is the processing of Payment Obligation messages. In these processes, separate SOAP services are called, and these services create many business objects (entities). Most business objects (BO) then get a lifecycle status, allowing business users and maintainers to monitor a certain case within the system.

For example, when an incoming message is received, a business object is created with a lifecycle status of ‘Registered’ as first part of the flow. Further on in the flow other actions take place and more business objects are created with a lifecycle status ‘Activated’, if all goes well. Or they get a lifecycle status ‘Blocked’ if there was an issue or an error (see figure below):

Sample Processes

In this case, the incoming message is registered and a business object (A) is created with a lifecycle status ‘Registered’. Then some other services are called, creating more business objects: Y with lifecycle status ‘Activated’ and X with lifecycle status ‘Blocked’.

In case of ‘Blocked’, something went wrong in the validation and the message is parked.

The users monitoring the system can analyze parked messages and take appropriate action. The reason why the message failed might be because it was an invalid message. The action the user could take is to ‘Reject’ the message, which also means all related business objects that were created, need to be rejected.

The Solution

Since many messages are processed in batches, and a batch of messages may fail, it should be possible to ‘Reject’ multiple messages at once. To make this possible, we devised a csv fileupload in which the user can provide a list of ids for the business objects that failed and need to be rejected (in this case, a list of ids of Payment Obligations).

This file, together with some metadata, is uploaded from a maintenance screen and processed and stored in the backend. A separate application is used to pick up the information and offer it to a separate ‘Rejection’ service.

The setup

The maintenance application consists of several Angular frontend screens used by functional maintainers. These Angular screens consume REST services on the backend side. The screens are tested separately and the REST services are also tested separately using JMeter.

One could doubt if JMeter is the right testing tool for testing REST services, but since JMeter was already heavily used for testing all other SOAP services, we decided not to introduce another testing framework for the REST services.

The frontend: multipart form upload screen

The user enters some metadata and uploads a csv file with ids, to be stored in the back-end database.

frontend

The backend: REST service for multipart form upload

As seen in my previous post, this is the REST endpoint receiving the metadata and multipart form:

@Stateless
@Path(“recovery”)
public class ErrorRecoveryResource {

   @POST
   @Consumes("multipart/form-data")
   @Produces("multipart/form-data")
   @Path("upload")
   @RolesAllowed("maintainer")
   public Response processUpload (@Context HttpServletRequest request) {
      MultipartRequestMap map = new MultipartRequestMap(request);
      UploadParameters uploadParameters = UploadParameters.newBuilder()
            .withListIdentification(map.getStringParameter("listIdentification"))
            .withObjectType(map.getStringParameter("objectType"))
            .withCreationTime(map.getStringParameter("creationTime"))
            .withListType(map.getStringParameter("listType"))
            .withItsmId(map.getStringParameter("itsmId"))
            .withUser(map.getStringParameter("user"))
            .withRecoveryReason(map.getStringParameter("recoveryReason"))
            .withFile(map.getFileParameter("file"))
            .build();

      // process the incoming parts and file

      return Response.ok(“upload processed”).build();
   }

}

The JMeter test

So, how can we test this REST endpoint from JMeter? JMeter supports uploading files using the HTTP Request Sampler. However, this doesn’t work for every possible scenario. In our case we also want to pass metadata along with the file itself. Therefore, in this case we need to take the following steps.

First, we need to add an HTTP Request Sampler, as follows:

HttpRequest

Secondly, we need to add an HTTP Header Manager Config Element under the HTTP Request Sampler, where we define the Accept and Content-type headers as such:

HTTP Header Manager

You could also set the User-Agent header to jmeter/x.x and the Expect header to 100-continue.

It is also always a good idea to protect your endpoints against Cross-Site Request Forgery (CXFR/XSRF) attacks, in which case you also need to pass an X-XSRF-TOKEN header. But that’s a different discussion.

Last but not least, we need to add a bit of magic in handling the csv file. As you may still remember, we did the following in the HTTP Request Sampler for the file where we used the variable ${csvData} as the file content:

Part Name File CsvData

One might wonder, though: where does this csvData come from? In order to do it like this, we need to convert the file content to a String. This can be done by adding a JSR223 PreProcessor under the HTTP Request Sampler:

PreProcessor

And there you have it: you can use the ${csvData} variable directly in the Body data of the HTTP Request Sampler.

Resources

• Blazemeter: Testing REST API File Uploads in JMeter
• Stackoverflow: send Byte Array in Http Request in JMeter
• Mozilla Http Expect Header

 

Leave a Reply

Your email address will not be published. Required fields are marked *