diff --git a/src/main/java/com/microsoft/graph/Util.java b/src/main/java/com/microsoft/graph/Util.java deleted file mode 100644 index 9ec86d861..000000000 --- a/src/main/java/com/microsoft/graph/Util.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.microsoft.graph; - -import java.io.Closeable; - -import javax.annotation.Nullable; - -/** - * Class with commonly used utility methods.
- * Note: This class is meant for internal SDK use only and SDK users should not take a - * dependency on it. - */ -public final class Util { - private Util() {} - - /** - * Closes a {@code Closeable} quietly, i.e. ignores any exceptions thrown when {@code close()}. - * @param closeable The {@code Closeable} instance to be quietly closed. - */ - public static void closeQuietly(@Nullable Closeable closeable) { - if (closeable == null) { - return; - } - try { - closeable.close(); - } catch (Exception e) { - // we don't care much - } - } -} diff --git a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java b/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java index dbbe68ccc..ba7efc8ab 100644 --- a/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java +++ b/src/main/java/com/microsoft/graph/concurrency/ChunkedUploadResponseHandler.java @@ -25,10 +25,8 @@ import com.google.common.io.ByteStreams; import com.microsoft.graph.core.ClientException; -import com.microsoft.graph.core.Constants; import com.microsoft.graph.http.GraphServiceException; import com.microsoft.graph.http.HttpResponseCode; -import com.microsoft.graph.http.HttpResponseHeadersHelper; import com.microsoft.graph.http.IHttpRequest; import com.microsoft.graph.http.IStatefulResponseHandler; import com.microsoft.graph.logger.ILogger; @@ -37,12 +35,12 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Map; import javax.annotation.Nonnull; import javax.annotation.Nullable; import okhttp3.Response; +import okhttp3.ResponseBody; import static com.microsoft.graph.http.HttpResponseCode.HTTP_OK; @@ -53,8 +51,6 @@ */ public class ChunkedUploadResponseHandler implements IStatefulResponseHandler, UploadType> { - - private final static HttpResponseHeadersHelper responseHeadersHelper = new HttpResponseHeadersHelper(); /** * The expected deserialized upload type */ @@ -107,25 +103,24 @@ public ChunkedUploadResult generateResult( response, logger)); } else if (response.code() >= HTTP_OK && response.code() < HttpResponseCode.HTTP_MULTIPLE_CHOICES) { - final Map headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response); - final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME); - final String location = headers.get("Location"); - if (contentType != null - && contentType.contains(Constants.JSON_CONTENT_TYPE)) { - return parseJsonUploadResult(response, serializer, logger); - } else if (location != null) { - logger.logDebug("Upload session is completed (Outlook), uploaded item returned."); - return new ChunkedUploadResult<>(this.deserializeTypeClass.getDeclaredConstructor().newInstance()); - } else { - logger.logDebug("Upload session returned an unexpected response"); - } + try(final ResponseBody body = response.body()) { + final String location = response.headers().get("Location"); + if (body.contentType() != null && body.contentType().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()); + } 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)); } @Nonnull - private ChunkedUploadResult parseJsonUploadResult(@Nonnull Response response, @Nonnull ISerializer serializer, @Nonnull ILogger logger) throws IOException { - try (final InputStream in = response.body().byteStream()) { + private ChunkedUploadResult 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); diff --git a/src/main/java/com/microsoft/graph/core/Constants.java b/src/main/java/com/microsoft/graph/core/Constants.java deleted file mode 100644 index 651cb996f..000000000 --- a/src/main/java/com/microsoft/graph/core/Constants.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.microsoft.graph.core; - -/** Multi-purpose constants holder used accross the SDK */ -public final class Constants { - private Constants() { - } - /** - * The content type header - */ - public static final String CONTENT_TYPE_HEADER_NAME = "Content-Type"; - /** - * The encoding type for getBytes - */ - public static final String JSON_ENCODING = "UTF-8"; - /** - * The content type for JSON responses - */ - public static final String JSON_CONTENT_TYPE = "application/json"; - /** - * The content type for TEXT responses - */ - public static final String TEXT_CONTENT_TYPE = "text/plain"; - /** - * The binary content type header's value - */ - public static final String BINARY_CONTENT_TYPE = "application/octet-stream"; -} - diff --git a/src/main/java/com/microsoft/graph/http/BaseRequest.java b/src/main/java/com/microsoft/graph/http/BaseRequest.java index d659e706a..a2b0f953c 100644 --- a/src/main/java/com/microsoft/graph/http/BaseRequest.java +++ b/src/main/java/com/microsoft/graph/http/BaseRequest.java @@ -34,11 +34,9 @@ import com.microsoft.graph.options.FunctionOption; import com.microsoft.graph.options.QueryOption; import com.microsoft.graph.core.ClientException; -import com.microsoft.graph.core.Constants; import com.microsoft.graph.options.HeaderOption; import com.microsoft.graph.options.Option; -import java.lang.reflect.Field; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; diff --git a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java index a108ea951..50ee6eed4 100644 --- a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java +++ b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java @@ -25,7 +25,6 @@ import com.google.common.annotations.VisibleForTesting; import com.microsoft.graph.core.ClientException; -import com.microsoft.graph.core.Constants; import com.microsoft.graph.httpcore.middlewareoption.RedirectOptions; import com.microsoft.graph.httpcore.middlewareoption.RetryOptions; import com.microsoft.graph.logger.ILogger; @@ -54,16 +53,30 @@ import okhttp3.Request; import okhttp3.RequestBody; import okhttp3.Response; +import okhttp3.ResponseBody; import okio.BufferedSink; -import static com.microsoft.graph.Util.closeQuietly; - /** * HTTP provider based off of OkHttp and msgraph-sdk-java-core library */ public class CoreHttpProvider implements IHttpProvider { - private final static HttpResponseHeadersHelper responseHeadersHelper = new HttpResponseHeadersHelper(); - /** + /** + * The content type header + */ + private static final String CONTENT_TYPE_HEADER_NAME = "Content-Type"; + /** + * The encoding type for getBytes + */ + private static final String JSON_ENCODING = "UTF-8"; + /** + * The content type for JSON responses + */ + private static final String JSON_CONTENT_TYPE = "application/json"; + /** + * The binary content type header's value + */ + private static final String BINARY_CONTENT_TYPE = "application/octet-stream"; + /** * The serializer */ private final ISerializer serializer; @@ -236,7 +249,7 @@ public Request getHttpRequest(@Nonnull final IHttpRequest request final List requestHeaders = request.getHeaders(); for(HeaderOption headerOption : requestHeaders) { - if(headerOption.getName().equalsIgnoreCase(Constants.CONTENT_TYPE_HEADER_NAME)) { + if(headerOption.getName().equalsIgnoreCase(CONTENT_TYPE_HEADER_NAME)) { contenttype = headerOption.getValue().toString(); break; } @@ -250,7 +263,7 @@ public Request getHttpRequest(@Nonnull final IHttpRequest request if (request.getHttpMethod() == HttpMethod.POST) { bytesToWrite = new byte[0]; if(contenttype == null) { - contenttype = Constants.BINARY_CONTENT_TYPE; + contenttype = BINARY_CONTENT_TYPE; } } else { @@ -261,15 +274,15 @@ public Request getHttpRequest(@Nonnull final IHttpRequest request bytesToWrite = (byte[]) serializable; // If the user hasn't specified a Content-Type for the request - if (!hasHeader(requestHeaders, Constants.CONTENT_TYPE_HEADER_NAME)) { - corehttpRequestBuilder.addHeader(Constants.CONTENT_TYPE_HEADER_NAME, Constants.BINARY_CONTENT_TYPE); - contenttype = Constants.BINARY_CONTENT_TYPE; + if (!hasHeader(requestHeaders, CONTENT_TYPE_HEADER_NAME)) { + corehttpRequestBuilder.addHeader(CONTENT_TYPE_HEADER_NAME, BINARY_CONTENT_TYPE); + contenttype = BINARY_CONTENT_TYPE; } } else { logger.logDebug("Sending " + serializable.getClass().getName() + " as request body"); final String serializeObject = serializer.serializeObject(serializable); try { - bytesToWrite = serializeObject.getBytes(Constants.JSON_ENCODING); + bytesToWrite = serializeObject.getBytes(JSON_ENCODING); } catch (final UnsupportedEncodingException ex) { final ClientException clientException = new ClientException("Unsupported encoding problem: ", ex); @@ -278,9 +291,9 @@ public Request getHttpRequest(@Nonnull final IHttpRequest request } // If the user hasn't specified a Content-Type for the request - if (!hasHeader(requestHeaders, Constants.CONTENT_TYPE_HEADER_NAME)) { - corehttpRequestBuilder.addHeader(Constants.CONTENT_TYPE_HEADER_NAME, Constants.JSON_CONTENT_TYPE); - contenttype = Constants.JSON_CONTENT_TYPE; + if (!hasHeader(requestHeaders, CONTENT_TYPE_HEADER_NAME)) { + corehttpRequestBuilder.addHeader(CONTENT_TYPE_HEADER_NAME, JSON_CONTENT_TYPE); + contenttype = JSON_CONTENT_TYPE; } } @@ -376,86 +389,88 @@ private Result processResponse(final Response re final Class resultClass, final Body serializable, final IStatefulResponseHandler handler) { - try { - InputStream in = null; - boolean isBinaryStreamInput = false; + try(final ResponseBody body = response.body()) { try { + InputStream in = null; + boolean isBinaryStreamInput = false; + try { - // Call being executed + // Call being executed - if (handler != null) { - handler.configConnection(response); - } + if (handler != null) { + handler.configConnection(response); + } - logger.logDebug(String.format(Locale.ROOT, "Response code %d, %s", - response.code(), - response.message())); + logger.logDebug(String.format(Locale.ROOT, "Response code %d, %s", + response.code(), + response.message())); - if (handler != null) { - logger.logDebug("StatefulResponse is handling the HTTP response."); - return handler.generateResult( - request, response, this.serializer, this.logger); - } + if (handler != null) { + logger.logDebug("StatefulResponse is handling the HTTP response."); + return handler.generateResult( + request, response, this.serializer, this.logger); + } - if (response.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) { - logger.logDebug("Handling error response"); - in = response.body().byteStream(); - handleErrorResponse(request, serializable, response); - } + if (response.code() >= HttpResponseCode.HTTP_CLIENT_ERROR) { + logger.logDebug("Handling error response"); + in = body.byteStream(); + handleErrorResponse(request, serializable, response); + } - if (response.code() == HttpResponseCode.HTTP_NOBODY - || response.code() == HttpResponseCode.HTTP_NOT_MODIFIED) { - logger.logDebug("Handling response with no body"); - return handleEmptyResponse(responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass); - } + final Map> responseHeaders = response.headers().toMultimap(); - if (response.code() == HttpResponseCode.HTTP_ACCEPTED) { - logger.logDebug("Handling accepted response"); - return handleEmptyResponse(responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass); - } + if (response.code() == HttpResponseCode.HTTP_NOBODY + || response.code() == HttpResponseCode.HTTP_NOT_MODIFIED) { + logger.logDebug("Handling response with no body"); + return handleEmptyResponse(responseHeaders, resultClass); + } - in = new BufferedInputStream(response.body().byteStream()); - - final Map headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response); - - if (response.body() == null || response.body().contentLength() == 0) - return null; - - final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME); - if (contentType != null && resultClass != InputStream.class && - contentType.contains(Constants.JSON_CONTENT_TYPE)) { - logger.logDebug("Response json"); - return handleJsonResponse(in, responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass); - } else if (resultClass == InputStream.class) { - logger.logDebug("Response binary"); - isBinaryStreamInput = true; - return (Result) handleBinaryStream(in); - } else if (contentType != null && resultClass != InputStream.class && - contentType.contains(Constants.TEXT_CONTENT_TYPE)) { - return handleRawResponse(in, resultClass); - } else { - return null; - } - } finally { - if (!isBinaryStreamInput) { - try{ - if (in != null) in.close(); - }catch(IOException e) { - logger.logError(e.getMessage(), e); + if (response.code() == HttpResponseCode.HTTP_ACCEPTED) { + logger.logDebug("Handling accepted response"); + return handleEmptyResponse(responseHeaders, resultClass); + } + + in = new BufferedInputStream(body.byteStream()); + + if (body == null || body.contentLength() == 0) + return null; + + if (body.contentType() != null && body.contentType().subtype().contains("json") + && resultClass != InputStream.class) { + logger.logDebug("Response json"); + return handleJsonResponse(in, responseHeaders, resultClass); + } else if (resultClass == InputStream.class) { + logger.logDebug("Response binary"); + isBinaryStreamInput = true; + return (Result) handleBinaryStream(in); + } else if (body.contentType() != null && resultClass != InputStream.class && + body.contentType().type().contains("text") && + body.contentType().subtype().contains("plain")) { + return handleRawResponse(in, resultClass); + } else { + return null; + } + } finally { + if (!isBinaryStreamInput) { + try{ + if (in != null) in.close(); + }catch(IOException e) { + logger.logError(e.getMessage(), e); + } + if (response != null) response.close(); } - if (response != null) response.close(); } + } catch (final GraphServiceException ex) { + final boolean shouldLogVerbosely = logger.getLoggingLevel() == LoggerLevel.DEBUG; + logger.logError("Graph service exception " + ex.getMessage(shouldLogVerbosely), ex); + throw ex; + } catch (final Exception ex) { + final ClientException clientException = new ClientException("Error during http request", + ex); + logger.logError("Error during http request", clientException); + throw clientException; } - } catch (final GraphServiceException ex) { - final boolean shouldLogVerbosely = logger.getLoggingLevel() == LoggerLevel.DEBUG; - logger.logError("Graph service exception " + ex.getMessage(shouldLogVerbosely), ex); - throw ex; - } catch (final Exception ex) { - final ClientException clientException = new ClientException("Error during http request", - ex); - logger.logError("Error during http request", clientException); - throw clientException; } } /** @@ -496,12 +511,7 @@ private Result handleJsonResponse(final InputStream in, Map Result handleRawResponse(final InputStream in, final Class Result handleEmptyResponse(Map> responseHeaders, final Class clazz) throws UnsupportedEncodingException{ //Create an empty object to attach the response headers to - InputStream in = new ByteArrayInputStream("{}".getBytes(Constants.JSON_ENCODING)); - return handleJsonResponse(in, responseHeaders, clazz); + Result result = null; + try(final InputStream in = new ByteArrayInputStream("{}".getBytes(JSON_ENCODING))) { + result = handleJsonResponse(in, responseHeaders, clazz); + } catch (IOException ex) { + //noop we couldnt close the byte array stream we just opened and its ok + } + return result; } private Request convertIHttpRequestToOkHttpRequest(IHttpRequest request) { diff --git a/src/main/java/com/microsoft/graph/http/GraphServiceException.java b/src/main/java/com/microsoft/graph/http/GraphServiceException.java index 8e7ce29cd..eece63096 100644 --- a/src/main/java/com/microsoft/graph/http/GraphServiceException.java +++ b/src/main/java/com/microsoft/graph/http/GraphServiceException.java @@ -1,16 +1,16 @@ // ------------------------------------------------------------------------------ // Copyright (c) 2017 Microsoft Corporation -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sub-license, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -40,21 +40,20 @@ import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.TreeMap; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import okhttp3.Headers; import okhttp3.Response; import static java.nio.charset.StandardCharsets.UTF_8; -import static okhttp3.internal.Util.closeQuietly; /** * An exception from the Graph service */ public class GraphServiceException extends ClientException { - private final static HttpResponseHeadersHelper responseHeadersHelper = new HttpResponseHeadersHelper(); - private static final long serialVersionUID = -7416427229421064119L; /** @@ -184,7 +183,7 @@ public String getResponseMessage() { public String getMessage() { return getMessage(verbose); } - + /** * Gets the HTTP status code * @@ -229,7 +228,7 @@ public String getMethod() { public String getUrl() { return url; } - + /** * Gets the request headers * @return the request headers @@ -370,7 +369,7 @@ public static GraphServiceException createFromResponse(@Nonnull final IHttpR final int responseCode = response.code(); final List responseHeaders = new LinkedList<>(); - final Map headers = responseHeadersHelper.getResponseHeadersAsMapStringString(response); + final Map headers = getResponseHeadersAsMapStringString(response); for (final String key : headers.keySet()) { final String fieldPrefix; if (key == null) { @@ -413,11 +412,8 @@ private static GraphErrorResponse parseErrorResponse(@Nonnull ISerializer serial throws IOException { byte[] responseBytes; - InputStream is = response.body().byteStream(); - try { + try(final InputStream is = response.body().byteStream()) { responseBytes = ByteStreams.toByteArray(is); - } finally { - closeQuietly(is); } GraphErrorResponse error; try { @@ -425,7 +421,7 @@ private static GraphErrorResponse parseErrorResponse(@Nonnull ISerializer serial error = serializer.deserializeObject( new ByteArrayInputStream(responseBytes), GraphErrorResponse.class, - responseHeadersHelper.getResponseHeadersAsMapOfStringList(response) + response.headers().toMultimap() ); } catch (final Exception ex) { error = new GraphErrorResponse(); @@ -437,4 +433,27 @@ private static GraphErrorResponse parseErrorResponse(@Nonnull ISerializer serial } return error; } + + /** + * Gets the response headers from OkHttp Response + * + * @param response the OkHttp response + * @return the set of headers names and value + */ + @Nonnull + protected static Map getResponseHeadersAsMapStringString(@Nonnull final Response response) { + final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + int index = 0; + final Headers responseHeaders = response.headers(); + while (index < responseHeaders.size()) { + final String headerName = responseHeaders.name(index); + final String headerValue = responseHeaders.value(index); + if (headerName == null || headerValue == null) { + break; + } + headers.put(headerName, headerValue); + index++; + } + return headers; + } } diff --git a/src/main/java/com/microsoft/graph/http/HttpResponseHeadersHelper.java b/src/main/java/com/microsoft/graph/http/HttpResponseHeadersHelper.java deleted file mode 100644 index 5fd0f4788..000000000 --- a/src/main/java/com/microsoft/graph/http/HttpResponseHeadersHelper.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.microsoft.graph.http; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; - -import javax.annotation.Nonnull; - -import okhttp3.Headers; -import okhttp3.Response; - -/** Internal utility class to help parsing the response headers */ -public class HttpResponseHeadersHelper { - /** - * Gets the response headers from OkHttp Response - * - * @param response the OkHttp response - * @return the set of headers names and value - */ - @Nonnull - public Map getResponseHeadersAsMapStringString(@Nonnull final Response response) { - final Map headers = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); - int index = 0; - final Headers responseHeaders = response.headers(); - while (index < responseHeaders.size()) { - final String headerName = responseHeaders.name(index); - final String headerValue = responseHeaders.value(index); - if (headerName == null || headerValue == null) { - break; - } - headers.put(headerName, headerValue); - index++; - } - return headers; - } - - /** - * Gets the response headers from OkHttp Response - * - * @param response the OkHttp response - * @return the set of headers names and value - */ - @Nonnull - public Map> getResponseHeadersAsMapOfStringList(@Nonnull final Response response) { - final Map> headerFields = response.headers().toMultimap(); - // Add the response code - final List list = new ArrayList<>(); - headerFields.put("responseCode", list); - return headerFields; - } -} diff --git a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java index de7087829..63e6e4bfb 100644 --- a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java @@ -31,6 +31,7 @@ import com.microsoft.graph.logger.ILogger; +import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Field; @@ -44,8 +45,6 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import static com.microsoft.graph.Util.closeQuietly; - /** * The default serializer implementation for the SDK */ @@ -72,7 +71,6 @@ public DefaultSerializer(@Nonnull final ILogger logger) { this.gson = GsonFactory.getGsonInstance(logger); } - @SuppressWarnings("unchecked") @Override @Nullable public T deserializeObject(@Nonnull final String inputString, @Nonnull final Class clazz, @Nonnull final Map> responseHeaders) { @@ -80,18 +78,17 @@ public T deserializeObject(@Nonnull final String inputString, @Nonnull final return deserializeObject(rawElement, clazz, responseHeaders); } - @SuppressWarnings("unchecked") @Override @Nullable public T deserializeObject(@Nonnull final InputStream inputStream, @Nonnull final Class clazz, @Nonnull final Map> responseHeaders) { - InputStreamReader streamReader = null; - try { - streamReader = new InputStreamReader(inputStream); + T result = null; + try (final InputStreamReader streamReader = new InputStreamReader(inputStream)) { final JsonElement rawElement = gson.fromJson(streamReader, JsonElement.class); - return deserializeObject(rawElement, clazz, responseHeaders); - } finally { - closeQuietly(streamReader); - } + result = deserializeObject(rawElement, clazz, responseHeaders); + } catch (IOException ex) { + //noop we couldn't close the stream reader we just opened and it's ok + } + return result; } @SuppressWarnings("unchecked")