diff --git a/spotBugsExcludeFilter.xml b/spotBugsExcludeFilter.xml index 31e4a5f86..c6b2fff2e 100644 --- a/spotBugsExcludeFilter.xml +++ b/spotBugsExcludeFilter.xml @@ -9,11 +9,11 @@ xsi:schemaLocation="https://github.com/spotbugs/filter/3.0.0 https://raw.githubu - + - + diff --git a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java index 3ee61dea2..59d6b04c4 100644 --- a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java +++ b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java @@ -404,11 +404,6 @@ private Result processResponse(final Response re // Call being executed - - if (handler != null) { - handler.configConnection(response); - } - logger.logDebug(String.format(Locale.ROOT, "Response code %d, %s", response.code(), response.message())); diff --git a/src/main/java/com/microsoft/graph/http/IStatefulResponseHandler.java b/src/main/java/com/microsoft/graph/http/IStatefulResponseHandler.java index b4343def5..893691a86 100644 --- a/src/main/java/com/microsoft/graph/http/IStatefulResponseHandler.java +++ b/src/main/java/com/microsoft/graph/http/IStatefulResponseHandler.java @@ -29,8 +29,6 @@ import javax.annotation.Nullable; import javax.annotation.Nonnull; -import okhttp3.Response; - /** * The handler interface for requests having stateful response from server. * The handler will custom the HTTP connection if needed and generate request @@ -40,13 +38,6 @@ * @param the deserialize type for serializer */ public interface IStatefulResponseHandler { - /** - * Configure the response - * - * @param response the HTTP response - */ - void configConnection(@Nonnull final Response response); - /** * Generate result after receiving response * @@ -54,12 +45,13 @@ public interface IStatefulResponseHandler { * @param response the HTTP connection * @param serializer the serializer for parsing response * @param logger the logger + * @param the native http client response type * @return the result generated by this handler * @throws Exception an exception occurs if the request was unable to complete for any reason */ @Nullable - ResultType generateResult(@Nonnull final IHttpRequest request, - @Nonnull final Response response, + ResultType generateResult(@Nonnull final IHttpRequest request, + @Nonnull final ResponseType response, @Nonnull final ISerializer serializer, @Nonnull final ILogger logger) throws Exception; } diff --git a/src/main/java/com/microsoft/graph/concurrency/IProgressCallback.java b/src/main/java/com/microsoft/graph/tasks/IProgressCallback.java similarity index 97% rename from src/main/java/com/microsoft/graph/concurrency/IProgressCallback.java rename to src/main/java/com/microsoft/graph/tasks/IProgressCallback.java index 66a295e5c..0a27f5d4f 100644 --- a/src/main/java/com/microsoft/graph/concurrency/IProgressCallback.java +++ b/src/main/java/com/microsoft/graph/tasks/IProgressCallback.java @@ -20,7 +20,7 @@ // THE SOFTWARE. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; /** * A callback that describes how to deal with success, failure, and progress diff --git a/src/main/java/com/microsoft/graph/concurrency/IUploadSession.java b/src/main/java/com/microsoft/graph/tasks/IUploadSession.java similarity index 98% rename from src/main/java/com/microsoft/graph/concurrency/IUploadSession.java rename to src/main/java/com/microsoft/graph/tasks/IUploadSession.java index 49e36e220..717bdde43 100644 --- a/src/main/java/com/microsoft/graph/concurrency/IUploadSession.java +++ b/src/main/java/com/microsoft/graph/tasks/IUploadSession.java @@ -20,7 +20,7 @@ // THE SOFTWARE. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; import java.util.List; diff --git a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadRequest.java b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadRequest.java similarity index 79% rename from src/main/java/com/microsoft/graph/concurrency/ChunkedUploadRequest.java rename to src/main/java/com/microsoft/graph/tasks/LargeFileUploadRequest.java index 7b9d6a10a..286c10d1a 100644 --- a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadRequest.java +++ b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadRequest.java @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; import java.util.List; import java.util.Locale; @@ -21,7 +21,7 @@ * The chunk upload request. * @param The upload item type. */ -public class ChunkedUploadRequest { +class LargeFileUploadRequest { /** * Content Range header name. @@ -41,7 +41,7 @@ public class ChunkedUploadRequest { /** * The base request. */ - private final BaseRequest> baseRequest; + private final BaseRequest> baseRequest; /** * Construct the ChunkedUploadRequest @@ -55,7 +55,7 @@ public class ChunkedUploadRequest { * @param totalLength The total length of the input stream. */ @SuppressWarnings("unchecked") - protected ChunkedUploadRequest(@Nonnull final String requestUrl, + protected LargeFileUploadRequest(@Nonnull final String requestUrl, @Nonnull final IBaseClient client, @Nullable final List options, @Nonnull final byte[] chunk, @@ -67,7 +67,7 @@ protected ChunkedUploadRequest(@Nonnull final String requestUrl, Objects.requireNonNull(chunk, "parameter chunk cannot be null"); this.data = new byte[chunkSize]; System.arraycopy(chunk, 0, this.data, 0, chunkSize); - this.baseRequest = new BaseRequest>(requestUrl, client, options, (Class>)(new ChunkedUploadResult<>((UploadType)null)).getClass()) { + this.baseRequest = new BaseRequest>(requestUrl, client, options, (Class>)(new LargeFileUploadResponse<>((UploadType)null)).getClass()) { }; this.baseRequest.setHttpMethod(HttpMethod.PUT); this.baseRequest.addHeader(CONTENT_RANGE_HEADER_NAME, @@ -86,16 +86,16 @@ protected ChunkedUploadRequest(@Nonnull final String requestUrl, */ @SuppressWarnings("unchecked") @Nonnull - public ChunkedUploadResult upload( - @Nonnull final ChunkedUploadResponseHandler responseHandler) { + public LargeFileUploadResponse upload( + @Nonnull final LargeFileUploadResponseHandler responseHandler) { Objects.requireNonNull(responseHandler, "parameter responseHandler cannot be null"); - ChunkedUploadResult result = null; + LargeFileUploadResponse result = null; try { result = this.baseRequest .getClient() .getHttpProvider() - .send(baseRequest, (Class>)(Class) ChunkedUploadResult.class, this.data, responseHandler); + .send(baseRequest, (Class>)(Class) LargeFileUploadResponse.class, this.data, responseHandler); } catch (final ClientException e) { throw new ClientException("Request failed with error, retry if necessary.", e); } @@ -103,7 +103,7 @@ public ChunkedUploadResult upload( if (result != null && result.chunkCompleted()) { return result; } else - return new ChunkedUploadResult( + return new LargeFileUploadResponse( new ClientException("Upload session failed.", result == null ? null : result.getError())); } } diff --git a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResult.java b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponse.java similarity index 65% rename from src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResult.java rename to src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponse.java index 7894bb55c..bbf2a53a4 100644 --- a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResult.java +++ b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponse.java @@ -2,7 +2,7 @@ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; import com.microsoft.graph.core.ClientException; import com.microsoft.graph.http.GraphServiceException; @@ -16,7 +16,11 @@ /** * Wrapper class for different upload response from server. */ -public class ChunkedUploadResult { +class LargeFileUploadResponse { + /** + * The location header from the response if provided + */ + private final String location; /** * The uploaded item response. */ @@ -33,44 +37,59 @@ public class ChunkedUploadResult { private final ClientException error; /** - * Construct result with item created. + * Constructs response with the location header. + * + * @param location The location returned by the response + */ + protected LargeFileUploadResponse(@Nullable final String location) { + this.location = location; + this.error = null; + this.session = null; + this.uploadedItem = null; + } + + /** + * Construct response with item created. * * @param uploaded The created item. */ - protected ChunkedUploadResult(@Nullable final UploadType uploaded) { + protected LargeFileUploadResponse(@Nullable final UploadType uploaded) { this.uploadedItem = uploaded; this.session = null; this.error = null; + this.location = null; } /** - * Construct result with next session. + * Construct response with next session. * * @param session The next session. */ - protected ChunkedUploadResult(@Nullable final IUploadSession session) { + protected LargeFileUploadResponse(@Nullable final IUploadSession session) { this.session = session; this.uploadedItem = null; this.error = null; + this.location = null; } /** - * Construct result with error. + * Construct response with error. * * @param error The error occurred during uploading. */ - protected ChunkedUploadResult(@Nullable final ClientException error) { + protected LargeFileUploadResponse(@Nullable final ClientException error) { this.error = error; this.uploadedItem = null; this.session = null; + this.location = null; } /** - * Construct result with server exception. + * Construct response with server exception. * * @param exception The exception received from server. */ - protected ChunkedUploadResult(@Nonnull final GraphServiceException exception) { + protected LargeFileUploadResponse(@Nonnull final GraphServiceException exception) { this(new ClientException(Objects .requireNonNull(exception, "parameter exception cannot be null") .getMessage(/* verbose */ true), @@ -78,9 +97,9 @@ protected ChunkedUploadResult(@Nonnull final GraphServiceException exception) { } /** - * Checks the chunk upload is completed. + * Checks the large upload range is completed. * - * @return true if current chunk upload is completed. + * @return true if current large upload range is completed. */ public boolean chunkCompleted() { return this.uploadedItem != null || this.session != null; @@ -92,7 +111,7 @@ public boolean chunkCompleted() { * @return true if the response is an item. */ public boolean uploadCompleted() { - return this.uploadedItem != null; + return this.uploadedItem != null || this.location != null; } /** @@ -133,4 +152,12 @@ public IUploadSession getSession() { public ClientException getError() { return this.error; } + /** + * Get the location. + * @return The location. + */ + @Nullable + public String getLocation () { + return this.location; + } } diff --git a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponseHandler.java similarity index 70% rename from src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java rename to src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponseHandler.java index c571407a0..9d28bbdb4 100644 --- a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java +++ b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResponseHandler.java @@ -20,7 +20,7 @@ // THE SOFTWARE. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; import com.google.common.io.ByteStreams; @@ -51,8 +51,8 @@ * * @param the expected uploaded item */ -public class ChunkedUploadResponseHandler - implements IStatefulResponseHandler, UploadType> { +class LargeFileUploadResponseHandler + implements IStatefulResponseHandler, UploadType> { /** * The expected deserialized upload type */ @@ -65,69 +65,54 @@ public class ChunkedUploadResponseHandler * @param uploadType the expected upload item type * @param uploadSessionType the type of the upload session */ - protected ChunkedUploadResponseHandler(@Nonnull final Class uploadType, @Nonnull final Class uploadSessionType) { + protected LargeFileUploadResponseHandler(@Nonnull final Class uploadType, @Nonnull final Class uploadSessionType) { this.deserializeTypeClass = Objects.requireNonNull(uploadType, "parameter uploadType cannot be null"); this.uploadSessionClass = Objects.requireNonNull(uploadSessionType, "parameter uploadSessionType cannot be null"); } - /** - * Do nothing before getting the response - * - * @param response The response - */ - @Override - public void configConnection(@Nonnull final Response response) { - return; - } - - /** - * Generate the chunked upload response result - * - * @param request the HTTP request - * @param response the HTTP response - * @param serializer the serializer - * @param logger the system logger - * @return the chunked upload result, which could be either an uploaded item or error - * @throws Exception an exception occurs if the request was unable to complete for any reason - */ @Override @Nullable - public ChunkedUploadResult generateResult( + public LargeFileUploadResponse generateResult( @Nonnull final IHttpRequest request, - @Nonnull final Response response, + @Nonnull final ResponseType response, @Nonnull final ISerializer serializer, @Nonnull final ILogger logger) throws Exception { Objects.requireNonNull(request, "parameter request cannot be null"); Objects.requireNonNull(response, "parameter response cannot be null"); Objects.requireNonNull(serializer, "parameter serializer cannot be null"); Objects.requireNonNull(logger, "parameter logger cannot be null"); - if (response.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) { + if(!(response instanceof Response)) { + throw new ClientException("unsupported response type", null); + } + final Response nativeResponse = (Response)response; + + if (nativeResponse.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) { logger.logDebug("Receiving error during upload, see detail on result error"); - return new ChunkedUploadResult<>( + return new LargeFileUploadResponse<>( GraphServiceException.createFromResponse(request, null, serializer, - response, logger)); - } else if (response.code() >= HTTP_OK - && response.code() < HttpResponseCode.HTTP_MULTIPLE_CHOICES) { - try(final ResponseBody body = response.body()) { - final String location = response.headers().get("Location"); + nativeResponse, logger)); + } else if (nativeResponse.code() >= HTTP_OK + && nativeResponse.code() < HttpResponseCode.HTTP_MULTIPLE_CHOICES) { + try(final ResponseBody body = nativeResponse.body()) { + final String location = nativeResponse.headers().get("Location"); final MediaType contentType = body.contentType(); final String subType = contentType == null ? null : contentType.subtype(); if (subType != null && subType.contains("json")) { return parseJsonUploadResult(body, serializer, logger); } else if (location != null) { logger.logDebug("Upload session is completed (Outlook), uploaded item returned."); - return new ChunkedUploadResult<>(this.deserializeTypeClass.getDeclaredConstructor().newInstance()); + return new LargeFileUploadResponse<>(location); } else { logger.logDebug("Upload session returned an unexpected response"); } } } - return new ChunkedUploadResult<>(new ClientException("Received an unexpected response from the service, response code: " + response.code(), null)); + return new LargeFileUploadResponse<>(new ClientException("Received an unexpected response from the service, response code: " + nativeResponse.code(), null)); } @Nonnull - private ChunkedUploadResult parseJsonUploadResult(@Nonnull final ResponseBody responseBody, @Nonnull final ISerializer serializer, @Nonnull final ILogger logger) throws IOException { + private LargeFileUploadResponse parseJsonUploadResult(@Nonnull final ResponseBody responseBody, @Nonnull final ISerializer serializer, @Nonnull final ILogger logger) throws IOException { try (final InputStream in = responseBody.byteStream()) { final byte[] responseBytes = ByteStreams.toByteArray(in); final IUploadSession session = serializer.deserializeObject(new ByteArrayInputStream(responseBytes), uploadSessionClass); @@ -135,10 +120,10 @@ private ChunkedUploadResult parseJsonUploadResult(@Nonnull final Res if (session == null || session.getNextExpectedRanges() == null) { logger.logDebug("Upload session is completed (ODSP), uploaded item returned."); final UploadType uploadedItem = serializer.deserializeObject(new ByteArrayInputStream(responseBytes), this.deserializeTypeClass); - return new ChunkedUploadResult<>(uploadedItem); + return new LargeFileUploadResponse<>(uploadedItem); } else { logger.logDebug("Chunk bytes has been accepted by the server."); - return new ChunkedUploadResult<>(session); + return new LargeFileUploadResponse<>(session); } } } diff --git a/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResult.java b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResult.java new file mode 100644 index 000000000..b1459adbc --- /dev/null +++ b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadResult.java @@ -0,0 +1,22 @@ +package com.microsoft.graph.tasks; + +import javax.annotation.Nullable; + +/** + * Respresents the result of a large file upload task. + * + * @param type of the deserialized response. + */ +public class LargeFileUploadResult { + /** + * Location response header value if provided. + */ + @Nullable + public String location; + + /** + * Deserialized response body if the response has content. + */ + @Nullable + public ResultType responseBody; +} diff --git a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadProvider.java b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadTask.java similarity index 75% rename from src/main/java/com/microsoft/graph/concurrency/ChunkedUploadProvider.java rename to src/main/java/com/microsoft/graph/tasks/LargeFileUploadTask.java index ce0c61989..897cc6d0f 100644 --- a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadProvider.java +++ b/src/main/java/com/microsoft/graph/tasks/LargeFileUploadTask.java @@ -20,7 +20,7 @@ // THE SOFTWARE. // ------------------------------------------------------------------------------ -package com.microsoft.graph.concurrency; +package com.microsoft.graph.tasks; import com.microsoft.graph.core.ClientException; import com.microsoft.graph.core.IBaseClient; @@ -42,7 +42,7 @@ * * @param the upload item type */ -public class ChunkedUploadProvider { +public class LargeFileUploadTask { /** * The default chunk size for upload. Currently set to 5 MiB. @@ -83,7 +83,7 @@ public class ChunkedUploadProvider { /** * The upload response handler */ - private final ChunkedUploadResponseHandler responseHandler; + private final LargeFileUploadResponseHandler responseHandler; /** * The counter for how many bytes have been read from input stream @@ -99,7 +99,7 @@ public class ChunkedUploadProvider { * @param streamSize the stream size * @param uploadTypeClass the upload type class */ - public ChunkedUploadProvider(@Nonnull final IUploadSession uploadSession, + public LargeFileUploadTask(@Nonnull final IUploadSession uploadSession, @Nonnull final IBaseClient client, @Nonnull final InputStream inputStream, final long streamSize, @@ -115,7 +115,7 @@ public ChunkedUploadProvider(@Nonnull final IUploadSession uploadSession, this.inputStream = Objects.requireNonNull(inputStream, "Input stream is null."); this.streamSize = streamSize; this.uploadUrl = uploadSession.getUploadUrl(); - this.responseHandler = new ChunkedUploadResponseHandler(uploadTypeClass, uploadSession.getClass()); + this.responseHandler = new LargeFileUploadResponseHandler(uploadTypeClass, uploadSession.getClass()); } /** @@ -128,7 +128,7 @@ public ChunkedUploadProvider(@Nonnull final IUploadSession uploadSession, * @throws IOException the IO exception that occurred during upload */ @Nonnull - public java.util.concurrent.CompletableFuture uploadAsync(@Nullable final int chunkSize, @Nullable final List