In the following stub request, I use withBodyFile which loads large files (>300MB) from specific locations on local disk using streams:
public void mockGetUrlContent(String url) {
stubFor(get(urlEqualTo(url))
.willReturn(ok()
.withBodyFile(FilenameUtils.getName(url))));
}
Once the stub is being called, wiremock tries to complete a served event and in order to log the response it uses the from method below:
public static LoggedResponse from(Response response) {
return new LoggedResponse(
response.getStatus(),
response.getHeaders() == null || response.getHeaders().all().isEmpty()
? null
: response.getHeaders(),
response.getBody(),
response.getFault());
}
When reached to wiremock's Response getBody method it converts the stream in to byte[] and in that points it blows on OutOfMemoryError (Java heap space).:
public byte[] getBody() {
try (InputStream stream = bodyStreamSource == null ? null : getBodyStream()) {
return stream == null ? null : ByteStreams.toByteArray(stream);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Any ideas about what am I doing wrong?
Your help is highly appreciated!
WireMock 2.34.0 has a solution to this.
To ensure that logged response bodies are limited to a particular size you need to start WireMock with a new configuration parameter specifying the max size in bytes
When running standalone add the CLI parameter:
--logged-response-body-size-limit=1000000
When running in Java add the following to the configuration builder:
wireMockConfig().maxLoggedResponseSize(1000000)
Related
I am calling a File Server's REST API using POST method and sending it file content to be uploaded. The REST API should ideally save the file and send a response which contains fileName.
My code is something like this.
public String uploadFile() {
UploadResponse response = restTemplate.postForObject(
FILE_SERVER_URL/upload,
new HttpEntity<>(fileContent, headers),
UploadResponse.class);
return response.getFileName();
}
In the above code, the compiler complains that UploadResponse response could be null, and I should handle that.
I plan to handle it with the below code.
public String uploadFile() {
UploadResponse response = restTemplate.postForObject(
FILE_SERVER_URL/upload,
new HttpEntity<>(fileContent, headers),
UploadResponse.class);
if(response != null) {
return response.getFileServiceId();
}
else {
throw new RuntimeException("File upload failed");
}
}
However, I am not sure if it is the right way to handle this. I don't feel this is a Runtime Exception. Please guide me as how should I handle the case that response could be null.
You can use Optional to avoid null checks in your code.
You can read this really insightful Q/A here - https://softwareengineering.stackexchange.com/questions/309134/why-is-using-an-optional-preferential-to-null-checking-the-variable
This question already has answers here:
Compiler error "archive for required library could not be read" - Spring Tool Suite
(24 answers)
Closed 4 years ago.
I need your help as I am new to this field. I want to use Azure storage blob service to upload images, list and download, but I am facing some problems.
I have imported a project from this repository, and as soon as I import I am getting errors:
Description Resource Path Location Type
Archive for required library: 'C:/Users/NUTRIP-DEVLP1/.m2/repository/org/apache/commons/commons-lang3/3.4/commons-lang3-3.4.jar' in project 'blobAzureApp' cannot be read or is not a valid ZIP file blobAzureApp Build path Build Path Problem
Description Resource Path Location Type
The project cannot be built until build path errors are resolved blobAzureApp Unknown Java Problem
Should I run this as a normal Java application or a Maven project? If Maven, how do I run it?
I suggest you using official java sdk in your maven project.
<dependency>
<groupId>com.microsoft.azure</groupId>
<artifactId>azure-storage-blob</artifactId>
<version>10.1.0</version>
</dependency>
sample upload code:
static void uploadFile(BlockBlobURL blob, File sourceFile) throws IOException {
FileChannel fileChannel = FileChannel.open(sourceFile.toPath());
// Uploading a file to the blobURL using the high-level methods available in TransferManager class
// Alternatively call the Upload/StageBlock low-level methods from BlockBlobURL type
TransferManager.uploadFileToBlockBlob(fileChannel, blob, 8*1024*1024, null)
.subscribe(response-> {
System.out.println("Completed upload request.");
System.out.println(response.response().statusCode());
});
}
sample list code:
static void listBlobs(ContainerURL containerURL) {
// Each ContainerURL.listBlobsFlatSegment call return up to maxResults (maxResults=10 passed into ListBlobOptions below).
// To list all Blobs, we are creating a helper static method called listAllBlobs,
// and calling it after the initial listBlobsFlatSegment call
ListBlobsOptions options = new ListBlobsOptions(null, null, 10);
containerURL.listBlobsFlatSegment(null, options)
.flatMap(containersListBlobFlatSegmentResponse ->
listAllBlobs(containerURL, containersListBlobFlatSegmentResponse))
.subscribe(response-> {
System.out.println("Completed list blobs request.");
System.out.println(response.statusCode());
});
}
private static Single <ContainersListBlobFlatSegmentResponse> listAllBlobs(ContainerURL url, ContainersListBlobFlatSegmentResponse response) {
// Process the blobs returned in this result segment (if the segment is empty, blobs() will be null.
if (response.body().blobs() != null) {
for (Blob b : response.body().blobs().blob()) {
String output = "Blob name: " + b.name();
if (b.snapshot() != null) {
output += ", Snapshot: " + b.snapshot();
}
System.out.println(output);
}
}
else {
System.out.println("There are no more blobs to list off.");
}
// If there is not another segment, return this response as the final response.
if (response.body().nextMarker() == null) {
return Single.just(response);
} else {
/*
IMPORTANT: ListBlobsFlatSegment returns the start of the next segment; you MUST use this to get the next
segment (after processing the current result segment
*/
String nextMarker = response.body().nextMarker();
/*
The presence of the marker indicates that there are more blobs to list, so we make another call to
listBlobsFlatSegment and pass the result through this helper function.
*/
return url.listBlobsFlatSegment(nextMarker, new ListBlobsOptions(null, null,1))
.flatMap(containersListBlobFlatSegmentResponse ->
listAllBlobs(url, containersListBlobFlatSegmentResponse));
}
}
sample download code:
static void getBlob(BlockBlobURL blobURL, File sourceFile) {
try {
// Get the blob using the low-level download method in BlockBlobURL type
// com.microsoft.rest.v2.util.FlowableUtil is a static class that contains helpers to work with Flowable
blobURL.download(new BlobRange(0, Long.MAX_VALUE), null, false)
.flatMapCompletable(response -> {
AsynchronousFileChannel channel = AsynchronousFileChannel.open(Paths
.get(sourceFile.getPath()), StandardOpenOption.CREATE, StandardOpenOption.WRITE);
return FlowableUtil.writeFile(response.body(), channel);
}).doOnComplete(()-> System.out.println("The blob was downloaded to " + sourceFile.getAbsolutePath()))
// To call it synchronously add .blockingAwait()
.subscribe();
} catch (Exception ex){
System.out.println(ex.toString());
}
}
More details, please refer to this doc.Hope it helps you.
I am developing an API REST using Spring WebFlux, but I have problems when uploading files. They are stored but I don't get the expected return value.
This is what I do:
Receive a Flux<Part>
Cast Part to FilePart.
Save parts with transferTo() (this return a Mono<Void>)
Map the Mono<Void> to Mono<String>, using file name.
Return Flux<String> to client.
I expect file name to be returned, but client gets an empty string.
Controller code
#PostMapping(value = "/muscles/{id}/image")
public Flux<String> updateImage(#PathVariable("id") String id, #RequestBody Flux<Part> file) {
log.info("REST request to update image to Muscle");
return storageService.saveFiles(file);
}
StorageService
public Flux<String> saveFiles(Flux<Part> parts) {
log.info("StorageService.saveFiles({})", parts);
return
parts
.filter(p -> p instanceof FilePart)
.cast(FilePart.class)
.flatMap(file -> saveFile(file));
}
private Mono<String> saveFile(FilePart filePart) {
log.info("StorageService.saveFile({})", filePart);
String filename = DigestUtils.sha256Hex(filePart.filename() + new Date());
Path target = rootLocation.resolve(filename);
try {
Files.deleteIfExists(target);
File file = Files.createFile(target).toFile();
return filePart.transferTo(file)
.map(r -> filename);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
FilePart.transferTo() returns Mono<Void>, which signals when the operation is done - this means the reactive Publisher will only publish an onComplete/onError signal and will never publish a value before that.
This means that the map operation was never executed, because it's only given elements published by the source.
You can return the name of the file and still chain reactive operators, like this:
return part.transferTo(file).thenReturn(part.filename());
It is forbidden to use the block operator within a reactive pipeline and it even throws an exception at runtime as of Reactor 3.2.
Using subscribe as an alternative is not good either, because subscribe will decouple the transferring process from your request processing, making those happen in different execution sequences. This means that your server could be done processing the request and close the HTTP connection while the other part is still trying to read the file part to copy it on disk. This is likely to fail in subtle ways at runtime.
FilePart.transferTo() returns Mono<Void> that is a constant empty. Then, map after that was never executed. I solved it by doing this:
private Mono<String> saveFile(FilePart filePart) {
log.info("StorageService.saveFile({})", filePart);
String filename = DigestUtils.sha256Hex(filePart.filename() + new Date());
Path target = rootLocation.resolve(filename);
try {
Files.deleteIfExists(target);
File file = Files.createFile(target).toFile();
return filePart
.transferTo(file)
.doOnSuccess(data -> log.info("do something..."))
.thenReturn(filename);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
It's not clear must I close JAX RS Client/Response instances or not. And if I must, always or not?
According to documentation about the Client class:
Calling this method effectively invalidates all resource targets
produced by the client instance.
The WebTarget class does not have any invalidate()/close() method, but the Response class does.
According to documentation:
Close the underlying message entity input stream (if available and
open) as well as releases any other resources associated with the
response (e.g. buffered message entity data).
... The close() method
should be invoked on all instances that contain an un-consumed entity
input stream to ensure the resources associated with the instance are
properly cleaned-up and prevent potential memory leaks. This is
typical for client-side scenarios where application layer code
processes only the response headers and ignores the response entity.
The last paragraph is not clear to me. What does "un-consumed entity input stream" mean? If I get an InputSteam or a String from response, should I close the response explicitly?
We can get a response result without getting access to Response instance:
Client client = ...;
WebTarget webTarget = ...;
Invocation.Builder builder = webTarget.request(MediaType.APPLICATION_JSON_TYPE);
Invocation invocation = builder.buildGet();
InputStream reso = invocation.invoke(InputStream.class);
I'm working with RESTeasy implementation, and I expected that response will be closed inside of resteasy implementation, but I could not find it. Could anyone tell me why?
I know that the Response class will implement Closeable interface
But even know, the Response is used, without closing it.
According to the documentation close() is idempotent.
This operation is idempotent, i.e. it can be invoked multiple times with the same effect which also means that calling the close() method on an already closed message instance is legal and has no further effect.
So you can safely close the InputStream yourself and should.
That being said I style wise would not do invocation.invoke(InputStream.class) as the invoker(Class) is made for doing entity transformation. Instead if you want InputStream you should probably just call invocation.invoke() and deal with the Response object directly as you may want some header info before reading the stream.
The reason you want headers when dealing with a response InputStream is typical because you either don't care about the body or the body requires special processing and size considerations which is what the documentation is alluding to (e.g. HEAD request to ping server).
See also link
A message instance returned from this method will be cached for subsequent retrievals via getEntity(). Unless the supplied entity type is an input stream, this method automatically closes the an unconsumed original response entity data stream if open. In case the entity data has been buffered, the buffer will be reset prior consuming the buffered data to enable subsequent invocations of readEntity(...) methods on this response.
So if you choose anything other than InputStream you will not have to close the Response (but regardless its safe to do it anyways as its idempotent).
In short: do call close() or use closeable with try-with-resources-statements.
If you use the JAX-RS Client reference, calling close() on the client closes open sockets.
Calling close on Response releases the connection but not any open socket
It is not necessary required to call close() since Resteasy will release the connection under the covers. But it should be done if result is an InputStream or if you're dealing with Response results.
Resources/Reference:
According to the Resteasy documentation you should call close() on Response references.
In section 47.3 at the end it states that
Resteasy will release the connection under the covers. The only
counterexample is the case in which the response is an instance of
InputStream, which must be closed explicitly.
On the other hand, if the result of an invocation is an instance of
Response, then Response.close() method must be used to released the
connection.
You should probably execute this in a try/finally block. Again,
releasing a connection only makes it available for another use. It
does not normally close the socket.
Note that if ApacheHttpClient4Engine has created its own instance of
HttpClient, it is not necessary to wait for finalize() to close open
sockets. The ClientHttpEngine interface has a close() method for this
purpose.
Finally, if your javax.ws.rs.client.Client class has created the
engine automatically for you, you should call Client.close() and
this will clean up any socket connections.
Looking into the resteasy-client source code, Invocation#invoke(Class<T>) is simply calling Invocation#invoke() and calling Invocation#extractResult(GenericType<T> responseType, Response response, Annotation[] annotations) to extract the result from the Response:
#Override
public <T> T invoke(Class<T> responseType)
{
Response response = invoke();
if (Response.class.equals(responseType)) return (T)response;
return extractResult(new GenericType<T>(responseType), response, null);
}
Invocation#extractResult(GenericType<T> responseType, Response response, Annotation[] annotations) closes the Response in the finally block:
/**
* Extracts result from response throwing an appropriate exception if not a successful response.
*
* #param responseType
* #param response
* #param annotations
* #param <T>
* #return
*/
public static <T> T extractResult(GenericType<T> responseType, Response response, Annotation[] annotations)
{
int status = response.getStatus();
if (status >= 200 && status < 300)
{
try
{
if (response.getMediaType() == null)
{
return null;
}
else
{
T rtn = response.readEntity(responseType, annotations);
if (InputStream.class.isInstance(rtn)
|| Reader.class.isInstance(rtn))
{
if (response instanceof ClientResponse)
{
ClientResponse clientResponse = (ClientResponse)response;
clientResponse.noReleaseConnection();
}
}
return rtn;
}
}
catch (WebApplicationException wae)
{
try
{
response.close();
}
catch (Exception e)
{
}
throw wae;
}
catch (Throwable throwable)
{
try
{
response.close();
}
catch (Exception e)
{
}
throw new ResponseProcessingException(response, throwable);
}
finally
{
if (response.getMediaType() == null) response.close();
}
}
try
{
// Buffer the entity for any exception thrown as the response may have any entity the user wants
// We don't want to leave the connection open though.
String s = String.class.cast(response.getHeaders().getFirst("resteasy.buffer.exception.entity"));
if (s == null || Boolean.parseBoolean(s))
{
response.bufferEntity();
}
else
{
// close connection
if (response instanceof ClientResponse)
{
try
{
ClientResponse.class.cast(response).releaseConnection();
}
catch (IOException e)
{
// Ignore
}
}
}
if (status >= 300 && status < 400) throw new RedirectionException(response);
return handleErrorStatus(response);
}
finally
{
// close if no content
if (response.getMediaType() == null) response.close();
}
}
I'm trying to create a dynamic Rest client, where I can set the HTTP Method(GET-POST-PUT-DELETE), Query Params and body(Json, plain, XML), this is basically what I need, for the request I think i know how I can do it, but my concern is for reading the answer, since I know what I should get ( format) but I dont know how to read it properly, so far I return an object, below the code (only for POST, but the idea is the same):
Response responseRest = null;
Client client = null;
try {
client = new ResteasyClientBuilder().establishConnectionTimeout(TIME_OUT, TimeUnit.MILLISECONDS).socketTimeout(TIME_OUT, TimeUnit.MILLISECONDS).build();
WebTarget target = client.target(request.getUrlTarget());
MediaType type = assignResponseType(request.getTypeResponse());
switch (request.getProtocol()) {
case POST: {
if (request.getParamQuery() != null) {
for (VarRequestDTO varRequest : request.getParamQuery()) {
target = target.queryParam(varRequest.getName(), varRequest.getValue());
}
}
responseRest = target.request().post(Entity.entity(new ResponseWrapper(), type));
break;
}
default:
//HTTP METHOD No supported
}
Object result = responseRest.readEntity(Object.class);
}
catch (Exception e) {
response.setError(Boolean.TRUE);
response.setMessage(e.getMessage());
e.printStackTrace();
} finally {
if (responseRest != null) {
responseRest.close();
}
if (client != null) {
client.close();
}
}
What I basically I need is to return the object in the format needed, and where is called it's supposed to do a cast to the correct format, I just need it to be dynamic and used for any service.
Thanks
Every request that a ReST client makes to a ReST service, it passes an "Accept" header.
This is to indicate to the service the MIME-type of the resource the client is willing to accept.
In the above case, what are the acceptable formats (json/ plain text/ etc.) for you?
Depending on the "accept" format you choose, and the "Content-type" header that you receive, you can write a deserializer to accept that data and process.
Also, instead of returning an Object which is too generic, consider returning a readable Stream to the caller.