Repository: reef Updated Branches: refs/heads/master 407041dcd -> 76adb40cf
[REEF-1169] Implement REST requests using .NET HttpClient in O.A.R.Client This addressed the issue by * Implementing IRestRequest, IRestResponse and IRestClient based on .NET * httpclient JIRA: [REEF-1169](https://issues.apache.org/jira/browse/REEF-1169) Pull Request: Closes #805 Project: http://git-wip-us.apache.org/repos/asf/reef/repo Commit: http://git-wip-us.apache.org/repos/asf/reef/commit/76adb40c Tree: http://git-wip-us.apache.org/repos/asf/reef/tree/76adb40c Diff: http://git-wip-us.apache.org/repos/asf/reef/diff/76adb40c Branch: refs/heads/master Commit: 76adb40cf422e0d46021516ec3a9318ddf13dc3a Parents: 407041d Author: Anupam <[email protected]> Authored: Thu Jan 7 16:27:23 2016 -0800 Committer: Andrew Chung <[email protected]> Committed: Sun Jan 31 00:58:45 2016 -0800 ---------------------------------------------------------------------- lang/cs/.nuget/packages.config | 2 +- .../Org.Apache.REEF.Client.Tests.csproj | 12 +- .../RestClientTests.cs | 188 +++++++++++++++++++ .../YarnClientTests.cs | 35 ++-- .../packages.config | 3 +- .../Org.Apache.REEF.Client.csproj | 25 ++- .../YARN/RESTClient/HttpClient.cs | 59 ++++++ .../YARN/RESTClient/HttpClientRetryHandler.cs | 68 +++++++ .../YARN/RESTClient/IDeserializer.cs | 33 ++++ .../YARN/RESTClient/IHttpClient.cs | 37 ++++ .../YARN/RESTClient/IRequestFactory.cs | 43 +++++ .../YARN/RESTClient/IRestClient.cs | 43 +++++ .../YARN/RESTClient/IRestClientFactory.cs | 52 ----- .../YARN/RESTClient/IRestRequestExecutor.cs | 20 +- .../YARN/RESTClient/ISerializer.cs | 33 ++++ .../YARN/RESTClient/Method.cs | 26 +++ .../YARN/RESTClient/RequestFactory.cs | 76 ++++++++ .../YARN/RESTClient/RestClient.cs | 141 ++++++++++++++ .../YARN/RESTClient/RestJsonDeserializer.cs | 31 +-- .../YARN/RESTClient/RestJsonSerializer.cs | 28 ++- .../YARN/RESTClient/RestRequest.cs | 49 +++++ .../YARN/RESTClient/RestRequestExecutor.cs | 92 ++++++--- .../YARN/RESTClient/RestResponse.cs | 50 +++++ .../YARN/RESTClient/YarnClient.cs | 57 +++--- .../YARN/RESTClient/YarnRestAPIException.cs | 3 + lang/cs/Org.Apache.REEF.Client/packages.config | 4 +- .../AsyncUtils/VoidResult.cs | 31 +++ .../Org.Apache.Reef.Utilities.csproj | 1 + lang/cs/Org.Apache.REEF.sln | Bin 34256 -> 34618 bytes 29 files changed, 1057 insertions(+), 185 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/.nuget/packages.config ---------------------------------------------------------------------- diff --git a/lang/cs/.nuget/packages.config b/lang/cs/.nuget/packages.config index a4f6043..ca0774b 100644 --- a/lang/cs/.nuget/packages.config +++ b/lang/cs/.nuget/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client.Tests/Org.Apache.REEF.Client.Tests.csproj ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client.Tests/Org.Apache.REEF.Client.Tests.csproj b/lang/cs/Org.Apache.REEF.Client.Tests/Org.Apache.REEF.Client.Tests.csproj index 7260f07..724fe05 100644 --- a/lang/cs/Org.Apache.REEF.Client.Tests/Org.Apache.REEF.Client.Tests.csproj +++ b/lang/cs/Org.Apache.REEF.Client.Tests/Org.Apache.REEF.Client.Tests.csproj @@ -41,11 +41,8 @@ under the License. <HintPath>$(PackagesDir)\NSubstitute.1.8.2.0\lib\net45\NSubstitute.dll</HintPath> <Private>True</Private> </Reference> - <Reference Include="RestSharp, Version=100.0.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL"> - <HintPath>$(PackagesDir)\RestSharpSigned.105.2.3\lib\net45\RestSharp.dll</HintPath> - <Private>True</Private> - </Reference> <Reference Include="System" /> + <Reference Include="System.Net.Http" /> <Reference Include="System.ServiceProcess" /> <Reference Include="xunit.abstractions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=8d05b1bb7a6fdb6c, processorArchitecture=MSIL"> <HintPath>$(PackagesDir)\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll</HintPath> @@ -68,6 +65,7 @@ under the License. <Compile Include="JobResourceUploaderTests.cs" /> <Compile Include="LegacyJobResourceUploaderTests.cs" /> <Compile Include="MultipleRMUrlProviderTests.cs" /> + <Compile Include="RestClientTests.cs" /> <Compile Include="WindowsHadoopEmulatorYarnClientTests.cs" /> <Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="YarnClientTests.cs" /> @@ -87,6 +85,10 @@ under the License. <Project>{97DBB573-3994-417A-9F69-FFA25F00D2A6}</Project> <Name>Org.Apache.REEF.Tang</Name> </ProjectReference> + <ProjectReference Include="$(SolutionDir)\Org.Apache.REEF.Utilities\Org.Apache.REEF.Utilities.csproj"> + <Project>{79E7F89A-1DFB-45E1-8D43-D71A954AEB98}</Project> + <Name>Org.Apache.REEF.Utilities</Name> + </ProjectReference> </ItemGroup> <ItemGroup> <None Include="packages.config" /> @@ -109,4 +111,4 @@ under the License. <Target Name="AfterBuild"> </Target> --> -</Project> +</Project> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client.Tests/RestClientTests.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client.Tests/RestClientTests.cs b/lang/cs/Org.Apache.REEF.Client.Tests/RestClientTests.cs new file mode 100644 index 0000000..a970cf9 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client.Tests/RestClientTests.cs @@ -0,0 +1,188 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using NSubstitute; +using Org.Apache.REEF.Client.YARN.RestClient; +using Org.Apache.REEF.Tang.Implementations.Tang; +using Org.Apache.REEF.Tang.Util; + +namespace Org.Apache.REEF.Client.Tests +{ + [TestClass] + public class RestClientTests + { + private const string AnyResource = "anyResource"; + private const string AnyRootElement = "anyRootElement"; + private const int AnyIntField = 42; + private const string AnyStringField = "AnyStringFieldValue"; + private const string MediaType = @"application/json"; + + private static readonly string ExpectedReturnJson = + "{anyRootElement:{\"AnyStringField\":\"" + AnyStringField + "\",\"AnyIntField\":" + AnyIntField + "}}"; + + private static readonly string AnyPostContent = + "{\"AnyStringField\":\"" + AnyStringField + "\"}"; + + private static readonly Uri AnyRequestUri = new Uri("http://any/request/uri"); + private static readonly Encoding Encoding = Encoding.UTF8; + + [TestMethod] + public async Task RestClientGetRequestReturnsResponse() + { + var tc = new TestContext(); + + var client = tc.GetRestClient(); + + var anyRequest = new RestRequest + { + Method = Method.GET, + Resource = AnyResource, + RootElement = AnyRootElement + }; + + var successfulResponseMessage = CreateSuccessfulResponseMessage(); + tc.HttpClient.GetAsync(AnyRequestUri + anyRequest.Resource, CancellationToken.None) + .Returns(Task.FromResult(successfulResponseMessage)); + + var response = + await client.ExecuteRequestAsync<AnyResponse>(anyRequest, AnyRequestUri, CancellationToken.None); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(AnyStringField, response.Data.AnyStringField); + Assert.AreEqual(AnyIntField, response.Data.AnyIntField); + Assert.IsNull(response.Exception); + var unused = tc.HttpClient.Received(1).GetAsync(AnyRequestUri + anyRequest.Resource, CancellationToken.None); + } + + [TestMethod] + public async Task RestClientPostRequestReturnsResponse() + { + var tc = new TestContext(); + + var client = tc.GetRestClient(); + + var anyRequest = new RestRequest + { + Method = Method.POST, + Resource = AnyResource, + RootElement = AnyRootElement, + Content = new StringContent(AnyPostContent, Encoding, MediaType) + }; + + var successfulResponseMessage = CreateSuccessfulResponseMessage(); + tc.HttpClient.PostAsync(AnyRequestUri + anyRequest.Resource, + Arg.Is<StringContent>( + stringContent => + stringContent.Headers.ContentType.MediaType == MediaType && + stringContent.ReadAsStringAsync().Result == AnyPostContent), + CancellationToken.None) + .Returns(Task.FromResult(successfulResponseMessage)); + + var response = + await client.ExecuteRequestAsync<AnyResponse>(anyRequest, AnyRequestUri, CancellationToken.None); + + Assert.AreEqual(HttpStatusCode.OK, response.StatusCode); + Assert.AreEqual(AnyStringField, response.Data.AnyStringField); + Assert.AreEqual(AnyIntField, response.Data.AnyIntField); + Assert.IsNull(response.Exception); + var unused = tc.HttpClient.Received(1).PostAsync(AnyRequestUri + anyRequest.Resource, + Arg.Is<StringContent>( + stringContent => + stringContent.Headers.ContentType.MediaType == MediaType && + stringContent.ReadAsStringAsync().Result == AnyPostContent), + CancellationToken.None); + } + + [TestMethod] + public async Task RestClientRequestReturnsFailureResponse() + { + var tc = new TestContext(); + + var client = tc.GetRestClient(); + + var anyRequest = new RestRequest + { + Method = Method.GET, + Resource = AnyResource, + RootElement = AnyRootElement + }; + + var successfulResponseMessage = CreateFailedResponseMessage(); + tc.HttpClient.GetAsync(AnyRequestUri + anyRequest.Resource, CancellationToken.None) + .Returns(Task.FromResult(successfulResponseMessage)); + + var response = + await client.ExecuteRequestAsync<AnyResponse>(anyRequest, AnyRequestUri, CancellationToken.None); + + Assert.AreEqual(HttpStatusCode.InternalServerError, response.StatusCode); + Assert.IsNull(response.Data); + Assert.IsNotNull(response.Exception); + Assert.IsInstanceOfType(response.Exception, typeof(HttpRequestException)); + var unused = tc.HttpClient.Received(1).GetAsync(AnyRequestUri + anyRequest.Resource, CancellationToken.None); + } + + private HttpResponseMessage CreateSuccessfulResponseMessage() + { + return new HttpResponseMessage(HttpStatusCode.OK) + { + Content = new StringContent(ExpectedReturnJson, Encoding, MediaType) + }; + } + + private HttpResponseMessage CreateFailedResponseMessage() + { + return new HttpResponseMessage(HttpStatusCode.InternalServerError) + { + Content = new StringContent("AnyFailedContent", Encoding, MediaType) + }; + } + + private class TestContext + { + public readonly IHttpClient HttpClient = Substitute.For<IHttpClient>(); + + public readonly IDeserializer Deserializer = Substitute.For<IDeserializer>(); + + public IRestClient GetRestClient() + { + var injector = TangFactory.GetTang().NewInjector(); + injector.BindVolatileInstance(GenericType<IHttpClient>.Class, HttpClient); + ////injector.BindVolatileInstance(GenericType<IDeserializer>.Class, Deserializer); + return injector.GetInstance<IRestClient>(); + } + } + + private class AnyResponse + { + public string AnyStringField { get; set; } + + public int AnyIntField { get; set; } + } + + private class AnyRequest + { + public string AnyStringField { get; set; } + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client.Tests/YarnClientTests.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client.Tests/YarnClientTests.cs b/lang/cs/Org.Apache.REEF.Client.Tests/YarnClientTests.cs index fe07cc0..3858b12 100644 --- a/lang/cs/Org.Apache.REEF.Client.Tests/YarnClientTests.cs +++ b/lang/cs/Org.Apache.REEF.Client.Tests/YarnClientTests.cs @@ -27,7 +27,7 @@ using Org.Apache.REEF.Client.YARN.RestClient; using Org.Apache.REEF.Client.YARN.RestClient.DataModel; using Org.Apache.REEF.Tang.Implementations.Tang; using Org.Apache.REEF.Tang.Util; -using RestSharp; +using Org.Apache.REEF.Utilities.AsyncUtils; using Xunit; namespace Org.Apache.REEF.Client.Tests @@ -50,7 +50,7 @@ namespace Org.Apache.REEF.Client.Tests HadoopVersionBuiltOn = "AnyVersionBuildOn", }; restReqExecutor.ExecuteAsync<ClusterInfo>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/info" && req.RootElement == "clusterInfo" && req.Method == Method.GET), @@ -82,7 +82,7 @@ namespace Org.Apache.REEF.Client.Tests AppsCompleted = 301 }; restReqExecutor.ExecuteAsync<ClusterMetrics>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/metrics" && req.RootElement == "clusterMetrics" && req.Method == Method.GET), @@ -116,7 +116,7 @@ namespace Org.Apache.REEF.Client.Tests RunningContainers = 0 }; restReqExecutor.ExecuteAsync<Application>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/apps/" + applicationId && req.RootElement == "app" @@ -152,7 +152,7 @@ namespace Org.Apache.REEF.Client.Tests RunningContainers = 0 }; restReqExecutor.ExecuteAsync<Application>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/apps/" + applicationId && req.RootElement == "app" @@ -181,7 +181,7 @@ namespace Org.Apache.REEF.Client.Tests ApplicationId = applicationId }; restReqExecutor.ExecuteAsync<NewApplication>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/apps/new-application" && req.Method == Method.POST), @@ -298,30 +298,23 @@ namespace Org.Apache.REEF.Client.Tests RunningContainers = 0 }; - var response = Substitute.For<IRestResponse>(); - response.Headers.Returns(new List<Parameter> + var response = new RestResponse<VoidResult> { - new Parameter - { - Name = "Location", - Value = "http://somelocation" - } - }); - response.StatusCode.Returns(HttpStatusCode.Accepted); + StatusCode = HttpStatusCode.Accepted + }; restReqExecutor.ExecuteAsync( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/apps" && req.Method == Method.POST - && req.JsonSerializer is RestJsonSerializer - && req.Parameters.First().Name == "application/json" + && req.Content.Headers.ContentType.MediaType == "application/json" && IsExpectedJson(req, expectedJson)), anyUri.First(), CancellationToken.None).Returns(Task.FromResult(response)); restReqExecutor.ExecuteAsync<Application>( - Arg.Is<IRestRequest>( + Arg.Is<RestRequest>( req => req.Resource == "ws/v1/cluster/apps/" + applicationId && req.RootElement == "app" @@ -336,9 +329,9 @@ namespace Org.Apache.REEF.Client.Tests var unused = urlProvider.Received(2).GetUrlAsync(); } - private static bool IsExpectedJson(IRestRequest req, string expectedJson) + private static bool IsExpectedJson(RestRequest req, string expectedJson) { - return (string)req.Parameters.First().Value == expectedJson; + return req.Content.ReadAsStringAsync().Result == expectedJson; } private class TestContext http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client.Tests/packages.config ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client.Tests/packages.config b/lang/cs/Org.Apache.REEF.Client.Tests/packages.config index 779566d..4a71ef7 100644 --- a/lang/cs/Org.Apache.REEF.Client.Tests/packages.config +++ b/lang/cs/Org.Apache.REEF.Client.Tests/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -19,7 +19,6 @@ under the License. --> <packages> <package id="NSubstitute" version="1.8.2.0" targetFramework="net45" /> - <package id="RestSharpSigned" version="105.2.3" targetFramework="net45" /> <package id="StyleCop.MSBuild" version="4.7.49.1" targetFramework="net45" developmentDependency="true" /> <package id="xunit" version="2.1.0" targetFramework="net45" /> <package id="xunit.abstractions" version="2.0.0" targetFramework="net45" /> http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/Org.Apache.REEF.Client.csproj ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/Org.Apache.REEF.Client.csproj b/lang/cs/Org.Apache.REEF.Client/Org.Apache.REEF.Client.csproj index df01b65..91a2114 100644 --- a/lang/cs/Org.Apache.REEF.Client/Org.Apache.REEF.Client.csproj +++ b/lang/cs/Org.Apache.REEF.Client/Org.Apache.REEF.Client.csproj @@ -38,12 +38,13 @@ under the License. </PropertyGroup> <ItemGroup> <Reference Include="Microsoft.CSharp" /> + <Reference Include="Microsoft.Practices.TransientFaultHandling.Core, Version=5.1.1209.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> + <HintPath>$(PackagesDir)\TransientFaultHandling.Core.5.1.1209.1\lib\NET4\Microsoft.Practices.TransientFaultHandling.Core.dll</HintPath> + <Private>True</Private> + </Reference> <Reference Include="Newtonsoft.Json"> <HintPath>$(PackagesDir)\Newtonsoft.Json.$(NewtonsoftJsonVersion)\lib\net45\Newtonsoft.Json.dll</HintPath> </Reference> - <Reference Include="RestSharp, Version=100.0.0.0, Culture=neutral, PublicKeyToken=598062e77f915f75, processorArchitecture=MSIL"> - <HintPath>$(PackagesDir)\RestSharpSigned.105.2.3\lib\net45\RestSharp.dll</HintPath> - </Reference> <Reference Include="Microsoft.Hadoop.Avro"> <HintPath>$(PackagesDir)\Microsoft.Hadoop.Avro.$(AvroVersion)\lib\net45\Microsoft.Hadoop.Avro.dll</HintPath> </Reference> @@ -52,6 +53,7 @@ under the License. <Reference Include="System.IO.Compression" /> <Reference Include="System.IO.Compression.FileSystem" /> <Reference Include="System.Net.Http" /> + <Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Xml" /> <Reference Include="System.Xml.Linq" /> <Reference Include="System.Runtime.Serialization" /> @@ -95,6 +97,20 @@ under the License. <Compile Include="YARN\IJobSubmissionDirectoryProvider.cs" /> <Compile Include="YARN\Parameters\DriverMaxMemoryAllicationPoolSizeMB.cs" /> <Compile Include="YARN\Parameters\DriverMaxPermSizeMB.cs" /> + <Compile Include="YARN\RestClient\HttpClient.cs" /> + <Compile Include="YARN\RestClient\IDeserializer.cs" /> + <Compile Include="YARN\RestClient\IHttpClient.cs" /> + <Compile Include="YARN\RestClient\IRequestFactory.cs" /> + <Compile Include="YARN\RestClient\IRestClient.cs" /> + <Compile Include="YARN\RestClient\ISerializer.cs" /> + <Compile Include="YARN\RestClient\Method.cs" /> + <Compile Include="YARN\RestClient\RequestFactory.cs" /> + <Compile Include="YARN\RestClient\RestClient.cs" /> + <Compile Include="YARN\RestClient\RestJsonDeserializer.cs" /> + <Compile Include="YARN\RestClient\RestJsonSerializer.cs" /> + <Compile Include="YARN\RestClient\RestRequest.cs" /> + <Compile Include="YARN\RestClient\RestResponse.cs" /> + <Compile Include="YARN\RestClient\HttpClientRetryHandler.cs" /> <Compile Include="YARN\WindowsYarnJobCommandProvider.cs" /> <Compile Include="YARN\JobResource.cs" /> <Compile Include="YARN\JobSubmissionDirectoryProvider.cs" /> @@ -121,11 +137,8 @@ under the License. <Compile Include="YARN\RestClient\IUrlProvider.cs" /> <Compile Include="YARN\RestClient\FileSystemJobResourceUploader.cs" /> <Compile Include="YARN\RestClient\MultipleRMUrlProvider.cs" /> - <Compile Include="YARN\RestClient\RestJsonDeserializer.cs" /> - <Compile Include="YARN\RestClient\RestJsonSerializer.cs" /> <Compile Include="YARN\YarnJobSubmissionResult.cs" /> <Compile Include="YARN\YARNREEFClient.cs" /> - <Compile Include="YARN\RestClient\IRestClientFactory.cs" /> <Compile Include="YARN\RestClient\RestRequestExecutor.cs" /> <Compile Include="YARN\RestClient\IYarnRMClient.cs" /> <Compile Include="YARN\RestClient\DataModel\Application.cs" /> http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClient.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClient.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClient.cs new file mode 100644 index 0000000..6d636d2 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClient.cs @@ -0,0 +1,59 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Pass through HTTP client which calls into <see cref="System.Net.Http.HttpClient"/> + /// </summary> + internal class HttpClient : IHttpClient + { + private readonly System.Net.Http.HttpClient _httpClient; + + [Inject] + private HttpClient() + { + _httpClient = new System.Net.Http.HttpClient( + new HttpClientRetryHandler(new WebRequestHandler()), + disposeHandler: false); + } + + public async Task<HttpResponseMessage> GetAsync(string requestResource, CancellationToken cancellationToken) + { + return await _httpClient.GetAsync(requestResource, cancellationToken); + } + + public async Task<HttpResponseMessage> PostAsync(string requestResource, + StringContent content, + CancellationToken cancellationToken) + { + return await _httpClient.PostAsync(requestResource, content, cancellationToken); + } + + public async Task<HttpResponseMessage> PutAsync(string requestResource, + StringContent content, + CancellationToken cancellationToken) + { + return await _httpClient.PutAsync(requestResource, content, cancellationToken); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClientRetryHandler.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClientRetryHandler.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClientRetryHandler.cs new file mode 100644 index 0000000..ccc9052 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/HttpClientRetryHandler.cs @@ -0,0 +1,68 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.Practices.TransientFaultHandling; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// DelegatingHandler for retrying requests with HTTP client + /// </summary> + internal class HttpClientRetryHandler : DelegatingHandler + { + private const int RetryCount = 3; + private static readonly TimeSpan MinBackoffTimeSpan = TimeSpan.FromMilliseconds(100); + private static readonly TimeSpan MaxBackoffTimeSpan = TimeSpan.FromSeconds(5); + private static readonly TimeSpan DeltaBackoffTimeSpan = TimeSpan.FromMilliseconds(500); + + private readonly RetryPolicy<AllErrorsTransientStrategy> _retryPolicy; + + public HttpClientRetryHandler(HttpMessageHandler innerHandler) + : base(innerHandler) + { + this._retryPolicy = new RetryPolicy<AllErrorsTransientStrategy>( + new ExponentialBackoff( + "YarnRESTRetryHandler", + RetryCount, + MinBackoffTimeSpan, + MaxBackoffTimeSpan, + DeltaBackoffTimeSpan, + firstFastRetry: true)); + } + + protected override async Task<HttpResponseMessage> SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken) + { + return await this._retryPolicy.ExecuteAsync( + async () => await base.SendAsync(request, cancellationToken), + cancellationToken); + } + } + + internal class AllErrorsTransientStrategy : ITransientErrorDetectionStrategy + { + public bool IsTransient(Exception ex) + { + return true; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IDeserializer.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IDeserializer.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IDeserializer.cs new file mode 100644 index 0000000..869a157 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IDeserializer.cs @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Deserialize from string representation to corresponding data model object + /// </summary> + [DefaultImplementation(typeof(RestJsonDeserializer))] + internal interface IDeserializer + { + /// <summary> + /// Deserialize content string to <typeparam name="T"></typeparam> + /// </summary> + T Deserialize<T>(string contentString, string rootElement); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IHttpClient.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IHttpClient.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IHttpClient.cs new file mode 100644 index 0000000..366dd29 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IHttpClient.cs @@ -0,0 +1,37 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Wrapper interface on <see cref="System.Net.Http.HttpClient"/> for ease of unit-testing + /// </summary> + [DefaultImplementation(typeof(HttpClient))] + internal interface IHttpClient + { + Task<HttpResponseMessage> GetAsync(string requestResource, CancellationToken cancellationToken); + + Task<HttpResponseMessage> PostAsync(string requestResource, StringContent content, CancellationToken cancellationToken); + + Task<HttpResponseMessage> PutAsync(string requestResource, StringContent content, CancellationToken cancellationToken); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRequestFactory.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRequestFactory.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRequestFactory.cs new file mode 100644 index 0000000..ecef9f5 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRequestFactory.cs @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Factory for generating REST requests + /// </summary> + [DefaultImplementation(typeof(RequestFactory))] + internal interface IRequestFactory + { + /// <summary> + /// Generate REST request + /// </summary> + RestRequest CreateRestRequest(string resourcePath, Method method); + + /// <summary> + /// Generate REST request + /// </summary> + RestRequest CreateRestRequest(string resourcePath, Method method, string rootElement); + + /// <summary> + /// Generate REST request + /// </summary> + RestRequest CreateRestRequest(string resourcePath, Method method, string rootElement, object body); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClient.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClient.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClient.cs new file mode 100644 index 0000000..4ac1654 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClient.cs @@ -0,0 +1,43 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Threading; +using System.Threading.Tasks; +using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Utilities.AsyncUtils; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Interface for the client that executes the RestRequests and handles + /// errors and retries + /// </summary> + [DefaultImplementation(typeof(RestClient))] + internal interface IRestClient + { + /// <summary> + /// Execute request where the response is expected to be type T + /// </summary> + Task<RestResponse<T>> ExecuteRequestAsync<T>(RestRequest request, Uri requestBaseUri, CancellationToken cancellationToken); + + /// <summary> + /// Execute request where no response object is expected + /// </summary> + Task<RestResponse<VoidResult>> ExecuteRequestAsync(RestRequest request, Uri requestBaseUri, CancellationToken cancellationToken); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClientFactory.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClientFactory.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClientFactory.cs deleted file mode 100644 index 77e813d..0000000 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestClientFactory.cs +++ /dev/null @@ -1,52 +0,0 @@ -// Licensed to the Apache Software Foundation (ASF) under one -// or more contributor license agreements. See the NOTICE file -// distributed with this work for additional information -// regarding copyright ownership. The ASF licenses this file -// to you under the Apache License, Version 2.0 (the -// "License"); you may not use this file except in compliance -// with the License. You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, -// software distributed under the License is distributed on an -// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -// KIND, either express or implied. See the License for the -// specific language governing permissions and limitations -// under the License. - -using System; -using Org.Apache.REEF.Client.YARN.RestClient; -using Org.Apache.REEF.Tang.Annotations; -using RestSharp; - -namespace Org.Apache.REEF.Client.Yarn.RestClient -{ - [DefaultImplementation(typeof(RestClientFactory))] - internal interface IRestClientFactory - { - IRestClient CreateRestClient(Uri baseUri); - } - - internal class RestClientFactory : IRestClientFactory - { - [Inject] - private RestClientFactory() - { - } - - public IRestClient CreateRestClient(Uri baseUri) - { - // TODO: We are creating a new client per request - // as one client can contact only one baseUri. - // This is not very bad but it might still be worth - // it to cache clients per baseUri in the future. - var restClient = new RestSharp.RestClient(baseUri) - { - FollowRedirects = true - }; - restClient.AddHandler("application/json", new RestJsonDeserializer()); - return restClient; - } - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestRequestExecutor.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestRequestExecutor.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestRequestExecutor.cs index 7bb492a..ca4e2bc 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestRequestExecutor.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/IRestRequestExecutor.cs @@ -18,21 +18,31 @@ using System; using System.Threading; using System.Threading.Tasks; +using Org.Apache.REEF.Client.YARN.RestClient; using Org.Apache.REEF.Tang.Annotations; -using RestSharp; +using Org.Apache.REEF.Utilities.AsyncUtils; namespace Org.Apache.REEF.Client.Yarn.RestClient { + /// <summary> + /// Executes a REST request + /// </summary> [DefaultImplementation(typeof(RestRequestExecutor))] internal interface IRestRequestExecutor { + /// <summary> + /// Executes a REST request where a response is expected + /// </summary> Task<T> ExecuteAsync<T>( - IRestRequest request, + RestRequest request, Uri uri, - CancellationToken cancellationToken) where T : new(); + CancellationToken cancellationToken); - Task<IRestResponse> ExecuteAsync( - IRestRequest request, + /// <summary> + /// Executes a REST request where a response is NOT expected + /// </summary> + Task<RestResponse<VoidResult>> ExecuteAsync( + RestRequest request, Uri uri, CancellationToken cancellationToken); } http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/ISerializer.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/ISerializer.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/ISerializer.cs new file mode 100644 index 0000000..abea5c6 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/ISerializer.cs @@ -0,0 +1,33 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Serializes a data model object to string + /// </summary> + [DefaultImplementation(typeof(RestJsonSerializer))] + internal interface ISerializer + { + /// <summary> + /// Serializes <param name="obj"></param> to <see cref="string"/> + /// </summary> + string Serialize(object obj); + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/Method.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/Method.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/Method.cs new file mode 100644 index 0000000..2b78e71 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/Method.cs @@ -0,0 +1,26 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + internal enum Method + { + INVALID, + GET, + POST, + PUT + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RequestFactory.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RequestFactory.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RequestFactory.cs new file mode 100644 index 0000000..b499c55 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RequestFactory.cs @@ -0,0 +1,76 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.Net.Http; +using System.Text; +using Org.Apache.REEF.Tang.Annotations; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Factory to generate REST requests + /// </summary> + internal class RequestFactory : IRequestFactory + { + private readonly ISerializer _serializer; + private readonly string _baseResourceString; + + [Inject] + private RequestFactory(ISerializer serializer) + { + _serializer = serializer; + _baseResourceString = @"ws/v1/"; + } + + /// <summary> + /// Generate REST request + /// </summary> + public RestRequest CreateRestRequest(string resourcePath, Method method) + { + return CreateRestRequest(resourcePath, method, null); + } + + /// <summary> + /// Generate REST request + /// </summary> + public RestRequest CreateRestRequest(string resourcePath, Method method, string rootElement) + { + return CreateRestRequest(resourcePath, method, rootElement, null); + } + + /// <summary> + /// Generate REST request + /// </summary> + public RestRequest CreateRestRequest(string resourcePath, Method method, string rootElement, object body) + { + var request = new RestRequest + { + Resource = _baseResourceString + resourcePath, + RootElement = rootElement, + Method = method, + }; + + if (body != null) + { + string content = _serializer.Serialize(body); + request.Content = new StringContent(content, Encoding.UTF8, @"application/json"); + } + + return request; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestClient.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestClient.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestClient.cs new file mode 100644 index 0000000..d034c73 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestClient.cs @@ -0,0 +1,141 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Utilities.AsyncUtils; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Implementation of RestClient which uses HTTPClient to + /// make REST requests and handles errors + /// </summary> + internal class RestClient : IRestClient + { + private readonly IDeserializer _deserializer; + private readonly IHttpClient _httpClient; + + [Inject] + private RestClient(IDeserializer deserializer, IHttpClient httpClient) + { + _httpClient = httpClient; + _deserializer = deserializer; + } + + /// <summary> + /// Execute request where no response object is expected + /// </summary> + public async Task<RestResponse<VoidResult>> ExecuteRequestAsync( + RestRequest request, + Uri requestBaseUri, + CancellationToken cancellationToken) + { + var httpResponseMessage = await GetHttpResponseAsync(request, requestBaseUri, cancellationToken); + Exception exception = null; + if (!httpResponseMessage.IsSuccessStatusCode) + { + exception = + new HttpRequestException(string.Format("HTTP call failed with status [{0}] and content [{1}]", + httpResponseMessage.StatusCode, + await GetContentStringFromHttpResponseMessage(httpResponseMessage))); + } + + return new RestResponse<VoidResult> + { + Data = new VoidResult(), + Exception = exception, + StatusCode = httpResponseMessage.StatusCode + }; + } + + /// <summary> + /// Execute request where the response is expected to be type T + /// </summary> + public async Task<RestResponse<T>> ExecuteRequestAsync<T>( + RestRequest request, + Uri requestBaseUri, + CancellationToken cancellationToken) + { + var httpResponseMessage = await GetHttpResponseAsync(request, requestBaseUri, cancellationToken); + string contentString = await GetContentStringFromHttpResponseMessage(httpResponseMessage); + Exception exception = null; + T returnObj = default(T); + + if (!httpResponseMessage.IsSuccessStatusCode) + { + exception = + new HttpRequestException(string.Format("HTTP call failed with status [{0}] and content [{1}]", + httpResponseMessage.StatusCode, + contentString)); + } + else + { + returnObj = _deserializer.Deserialize<T>(contentString, + request.RootElement); + } + return new RestResponse<T> + { + Content = contentString, + Data = returnObj, + StatusCode = httpResponseMessage.StatusCode, + Exception = exception + }; + } + + private async Task<HttpResponseMessage> GetHttpResponseAsync( + RestRequest request, + Uri requestBaseUri, + CancellationToken cancellationToken) + { + HttpResponseMessage httpResponseMessage; + var requestResource = requestBaseUri + request.Resource; + switch (request.Method) + { + case Method.GET: + httpResponseMessage = await _httpClient.GetAsync( + requestResource, + cancellationToken); + break; + case Method.PUT: + httpResponseMessage = await _httpClient.PutAsync( + requestResource, + request.Content, + cancellationToken); + break; + case Method.POST: + httpResponseMessage = await _httpClient.PostAsync( + requestResource, + request.Content, + cancellationToken); + break; + default: + throw new InvalidOperationException(string.Format("Unknown method type {0}", request.Method)); + } + + return httpResponseMessage; + } + + private static async Task<string> GetContentStringFromHttpResponseMessage(HttpResponseMessage response) + { + return response.Content == null ? string.Empty : await response.Content.ReadAsStringAsync(); + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonDeserializer.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonDeserializer.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonDeserializer.cs index aa6fd7b..c58f3b4 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonDeserializer.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonDeserializer.cs @@ -18,18 +18,21 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; -using RestSharp; -using RestSharp.Deserializers; +using Org.Apache.REEF.Tang.Annotations; namespace Org.Apache.REEF.Client.YARN.RestClient { + /// <summary> + /// Simple implementation of JSON deserializer by using Newtonsoft JSON lib + /// </summary> internal sealed class RestJsonDeserializer : IDeserializer { - public string RootElement { get; set; } - public string Namespace { get; set; } - public string DateFormat { get; set; } + [Inject] + private RestJsonDeserializer() + { + } - public T Deserialize<T>(IRestResponse response) + public T Deserialize<T>(string contentString, string rootElement) { /* If root element is not empty, then we want to * skip the top level token and parse only one level deeper @@ -49,17 +52,17 @@ namespace Org.Apache.REEF.Client.YARN.RestClient * } * * This logic helps us avoid such classes. - */ - if (!string.IsNullOrEmpty(RootElement)) + */ + if (string.IsNullOrEmpty(rootElement)) { - var jobject = JObject.Parse(response.Content); - var jtoken = jobject[RootElement]; - var jsonSerializer = new JsonSerializer(); - jsonSerializer.Converters.Add(new StringEnumConverter()); - return jtoken.ToObject<T>(jsonSerializer); + return JsonConvert.DeserializeObject<T>(contentString, new StringEnumConverter()); } - return JsonConvert.DeserializeObject<T>(response.Content, new StringEnumConverter()); + var jobject = JObject.Parse(contentString); + var jtoken = jobject[rootElement]; + var jsonSerializer = new JsonSerializer(); + jsonSerializer.Converters.Add(new StringEnumConverter()); + return jtoken.ToObject<T>(jsonSerializer); } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonSerializer.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonSerializer.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonSerializer.cs index abd88b9..14eb5b8 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonSerializer.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestJsonSerializer.cs @@ -17,34 +17,30 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using RestSharp.Serializers; +using Newtonsoft.Json.Serialization; +using Org.Apache.REEF.Tang.Annotations; namespace Org.Apache.REEF.Client.YARN.RestClient { /// <summary> - /// RestSharp by default uses SimpleJsonSerializer which - /// does not understand property renaming. Here we create a - /// simple ISerializer implementation that uses Newtonsoft.Json - /// for performing serialization + /// Simple implementation of JSON serializer by using Newtonsoft JSON lib /// </summary> internal sealed class RestJsonSerializer : ISerializer { - public RestJsonSerializer() + private readonly JsonSerializerSettings _jsonSerializerSettings = new JsonSerializerSettings { - ContentType = "application/json"; - } - - public string RootElement { get; set; } - - public string Namespace { get; set; } + ContractResolver = new CamelCasePropertyNamesContractResolver(), + Converters = new JsonConverter[] { new StringEnumConverter() } + }; - public string DateFormat { get; set; } - - public string ContentType { get; set; } + [Inject] + private RestJsonSerializer() + { + } public string Serialize(object obj) { - return JsonConvert.SerializeObject(obj, new StringEnumConverter()); + return JsonConvert.SerializeObject(obj, _jsonSerializerSettings); } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequest.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequest.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequest.cs new file mode 100644 index 0000000..a70191c --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequest.cs @@ -0,0 +1,49 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.Net.Http; +using System.Text; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Encapsulated data related to a REST request + /// </summary> + internal class RestRequest + { + /// <summary> + /// Path to the resource being accessed + /// This path is relative to the base path. + /// </summary> + public string Resource { get; set; } + + /// <summary> + /// Root element (if any) in the response JSON + /// </summary> + public string RootElement { get; set; } + + /// <summary> + /// HTTP method to be invoked for the request + /// </summary> + public Method Method { get; set; } + + /// <summary> + /// The serialized string content for the request + /// </summary> + public StringContent Content { get; internal set; } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequestExecutor.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequestExecutor.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequestExecutor.cs index 75187a5..538c705 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequestExecutor.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestRequestExecutor.cs @@ -5,9 +5,9 @@ // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at -// +// // http://www.apache.org/licenses/LICENSE-2.0 -// +// // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -20,73 +20,103 @@ using System.Net; using System.Threading; using System.Threading.Tasks; using Newtonsoft.Json; +using Org.Apache.REEF.Client.YARN.RestClient; using Org.Apache.REEF.Client.YARN.RestClient.DataModel; using Org.Apache.REEF.Tang.Annotations; +using Org.Apache.REEF.Utilities.AsyncUtils; using Org.Apache.REEF.Utilities.Logging; -using RestSharp; namespace Org.Apache.REEF.Client.Yarn.RestClient { + /// <summary> + /// Executes a REST request + /// </summary> internal class RestRequestExecutor : IRestRequestExecutor { private static readonly Logger Log = Logger.GetLogger(typeof(RestRequestExecutor)); - private readonly IRestClientFactory _clientFactory; + private readonly IRestClient _client; [Inject] private RestRequestExecutor( - IRestClientFactory clientFactory) + IRestClient client) { - _clientFactory = clientFactory; + _client = client; } public async Task<T> ExecuteAsync<T>( - IRestRequest request, + RestRequest request, Uri requestUri, - CancellationToken cancellationToken) where T : new() + CancellationToken cancellationToken) { - var client = _clientFactory.CreateRestClient(requestUri); - - var response = - await - client.ExecuteTaskAsync<T>(request, cancellationToken); - - if (response.ErrorException != null) + RestResponse<T> response; + try { - throw new YarnRestAPIException("Executing REST API failed", response.ErrorException); + response = await _client.ExecuteRequestAsync<T>(request, requestUri, cancellationToken); + } + catch (Exception exception) + { + throw new YarnRestAPIException("Unhandled exception in executing REST request.", exception); } + HandleResponse(response.Exception, response.StatusCode, response.Content); + + return response.Data; + } + + public async Task<RestResponse<VoidResult>> ExecuteAsync(RestRequest request, + Uri requestUri, + CancellationToken cancellationToken) + { + RestResponse<VoidResult> response; try { - // HTTP status code greater than 300 is unexpected here. - // See if the server sent a error response and throw suitable - // exception to user. - if (response.StatusCode >= HttpStatusCode.Ambiguous) - { - Log.Log(Level.Error, "RESTRequest failed. StatusCode: {0}; Response: {1}", response.StatusCode, response.Content); - var errorResponse = JsonConvert.DeserializeObject<Error>(response.Content); - throw new YarnRestAPIException { Error = errorResponse }; - } + response = await _client.ExecuteRequestAsync(request, requestUri, cancellationToken); } catch (Exception exception) { - throw new YarnRestAPIException("Unhandled exception in deserializing error response.", exception); + throw new YarnRestAPIException("Unhandled exception in executing REST request.", exception); } - return response.Data; + HandleResponse(response.Exception, response.StatusCode, response.Content); + return response; } - public async Task<IRestResponse> ExecuteAsync(IRestRequest request, Uri uri, CancellationToken cancellationToken) + private static void HandleResponse(Exception responseException, HttpStatusCode httpStatusCode, string content) { - var client = _clientFactory.CreateRestClient(uri); + if (responseException != null) + { + throw new YarnRestAPIException("Executing REST API failed", responseException) + { + StatusCode = httpStatusCode + }; + } + // HTTP status code greater than 300 is unexpected here. + // See if the server sent a error response and throw suitable + // exception to user. + if (httpStatusCode < HttpStatusCode.Ambiguous) + { + return; + } + + Log.Log(Level.Error, + "RESTRequest failed. StatusCode: {0}; Response: {1}", + httpStatusCode, + content); + Error errorResponse; try { - return await client.ExecuteTaskAsync(request, cancellationToken); + errorResponse = JsonConvert.DeserializeObject<Error>(content); } catch (Exception exception) { - throw new YarnRestAPIException("Unhandled exception in executing REST request.", exception); + throw new YarnRestAPIException("Unhandled exception in deserializing error response.", exception) + { + StatusCode = httpStatusCode + }; } + + throw new YarnRestAPIException { Error = errorResponse, StatusCode = httpStatusCode }; } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestResponse.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestResponse.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestResponse.cs new file mode 100644 index 0000000..87872c6 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/RestResponse.cs @@ -0,0 +1,50 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System; +using System.Net; + +namespace Org.Apache.REEF.Client.YARN.RestClient +{ + /// <summary> + /// Represents the response of a REST request + /// </summary> + /// <typeparam name="T"></typeparam> + internal class RestResponse<T> + { + /// <summary> + /// Deserialized response for REST request + /// </summary> + public T Data { get; set; } + + /// <summary> + /// StatusCode of the REST response + /// </summary> + public HttpStatusCode StatusCode { get; set; } + + /// <summary> + /// Exception details if a communication/protocol + /// error happened while processing the request + /// </summary> + public Exception Exception { get; set; } + + /// <summary> + /// String content (serialized) of the response + /// </summary> + public string Content { get; set; } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnClient.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnClient.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnClient.cs index 62f0c14..ec47d8b 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnClient.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnClient.cs @@ -26,7 +26,6 @@ using Org.Apache.REEF.Client.YARN.RestClient.DataModel; using Org.Apache.REEF.Tang.Annotations; using Org.Apache.REEF.Utilities.AsyncUtils; using Org.Apache.REEF.Utilities.Logging; -using RestSharp; namespace Org.Apache.REEF.Client.Yarn.RestClient { @@ -39,15 +38,18 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient internal sealed partial class YarnClient : IYarnRMClient { private static readonly Logger Logger = Logger.GetLogger(typeof(YarnClient)); - private readonly string _baseResourceString; private readonly IUrlProvider _yarnRmUrlProviderUri; private readonly IRestRequestExecutor _restRequestExecutor; + private readonly IRequestFactory _requestFactory; [Inject] - internal YarnClient(IUrlProvider yarnRmUrlProviderUri, IRestRequestExecutor restRequestExecutor) + private YarnClient( + IUrlProvider yarnRmUrlProviderUri, + IRestRequestExecutor restRequestExecutor, + IRequestFactory requestFactory) { + _requestFactory = requestFactory; _yarnRmUrlProviderUri = yarnRmUrlProviderUri; - _baseResourceString = @"ws/v1/"; _restRequestExecutor = restRequestExecutor; } @@ -55,7 +57,10 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient { await new RemoveSynchronizationContextAwaiter(); - IRestRequest request = CreateRestRequest(ClusterInfo.Resource, Method.GET, ClusterInfo.RootElement); + var request = _requestFactory.CreateRestRequest( + ClusterInfo.Resource, + Method.GET, + ClusterInfo.RootElement); return await GenerateUrlAndExecuteRequestAsync<ClusterInfo>(request, cancellationToken); @@ -65,7 +70,9 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient { await new RemoveSynchronizationContextAwaiter(); - var request = CreateRestRequest(ClusterMetrics.Resource, Method.GET, ClusterMetrics.RootElement); + var request = _requestFactory.CreateRestRequest(ClusterMetrics.Resource, + Method.GET, + ClusterMetrics.RootElement); return await @@ -76,7 +83,9 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient { await new RemoveSynchronizationContextAwaiter(); - var request = CreateRestRequest(Application.Resource + appId, Method.GET, Application.RootElement); + var request = _requestFactory.CreateRestRequest(Application.Resource + appId, + Method.GET, + Application.RootElement); return await GenerateUrlAndExecuteRequestAsync<Application>(request, cancellationToken); @@ -86,7 +95,7 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient { await new RemoveSynchronizationContextAwaiter(); - var request = CreateRestRequest(NewApplication.Resource, Method.POST); + var request = _requestFactory.CreateRestRequest(NewApplication.Resource, Method.POST); return await @@ -99,38 +108,26 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient { await new RemoveSynchronizationContextAwaiter(); - var request = CreateRestRequest(SubmitApplication.Resource, Method.POST); + var request = _requestFactory.CreateRestRequest( + SubmitApplication.Resource, + Method.POST, + rootElement: null, + body: submitApplication); - request.AddBody(submitApplication); var submitResponse = await GenerateUrlAndExecuteRequestAsync(request, cancellationToken); if (submitResponse.StatusCode != HttpStatusCode.Accepted) { throw new YarnRestAPIException( string.Format("Application submission failed with HTTP STATUS {0}", - submitResponse.StatusCode)); + submitResponse.StatusCode)); } return await GetApplicationAsync(submitApplication.ApplicationId, cancellationToken); } - private RestRequest CreateRestRequest(string resourcePath, Method method, string rootElement = null) - { - var request = new RestRequest - { - Resource = _baseResourceString + resourcePath, - RootElement = rootElement, - Method = method, - RequestFormat = DataFormat.Json, - JsonSerializer = new RestJsonSerializer() - }; - - return request; - } - - private async Task<T> GenerateUrlAndExecuteRequestAsync<T>(IRestRequest request, + private async Task<T> GenerateUrlAndExecuteRequestAsync<T>(RestRequest request, CancellationToken cancellationToken) - where T : new() { IEnumerable<Uri> yarnRmUris = await _yarnRmUrlProviderUri.GetUrlAsync(); var exceptions = new List<Exception>(); @@ -145,7 +142,7 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient catch (Exception e) { exceptions.Add(e); - Logger.Log(Level.Verbose, + Logger.Log(Level.Verbose, string.Format(CultureInfo.CurrentCulture, "Possibly transient error in rest call {0}", e.Message)); } } @@ -153,7 +150,7 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient throw new AggregateException("Failed Rest Request", exceptions); } - private async Task<IRestResponse> GenerateUrlAndExecuteRequestAsync(IRestRequest request, + private async Task<RestResponse<VoidResult>> GenerateUrlAndExecuteRequestAsync(RestRequest request, CancellationToken cancellationToken) { IEnumerable<Uri> yarnRmUris = await _yarnRmUrlProviderUri.GetUrlAsync(); @@ -169,7 +166,7 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient catch (Exception e) { exceptions.Add(e); - Logger.Log(Level.Verbose, + Logger.Log(Level.Verbose, string.Format(CultureInfo.CurrentCulture, "Possibly transient error in rest call {0}", e.Message)); } } http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnRestAPIException.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnRestAPIException.cs b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnRestAPIException.cs index 7ee9889..b25a30c 100644 --- a/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnRestAPIException.cs +++ b/lang/cs/Org.Apache.REEF.Client/YARN/RESTClient/YarnRestAPIException.cs @@ -16,6 +16,7 @@ // under the License. using System; +using System.Net; using Org.Apache.REEF.Client.YARN.RestClient.DataModel; namespace Org.Apache.REEF.Client.Yarn.RestClient @@ -38,5 +39,7 @@ namespace Org.Apache.REEF.Client.Yarn.RestClient } public Error Error { get; internal set; } + + public HttpStatusCode StatusCode { get; internal set; } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Client/packages.config ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Client/packages.config b/lang/cs/Org.Apache.REEF.Client/packages.config index 827a433..63005d5 100644 --- a/lang/cs/Org.Apache.REEF.Client/packages.config +++ b/lang/cs/Org.Apache.REEF.Client/packages.config @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="utf-8"?> +<?xml version="1.0" encoding="utf-8"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file @@ -19,6 +19,6 @@ under the License. --> <packages> <package id="Newtonsoft.Json" version="6.0.8" targetFramework="net45" /> - <package id="RestSharpSigned" version="105.2.3" targetFramework="net45" /> <package id="StyleCop.MSBuild" version="4.7.49.1" targetFramework="net45" developmentDependency="true" /> + <package id="TransientFaultHandling.Core" version="5.1.1209.1" targetFramework="net45" /> </packages> \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Utilities/AsyncUtils/VoidResult.cs ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Utilities/AsyncUtils/VoidResult.cs b/lang/cs/Org.Apache.REEF.Utilities/AsyncUtils/VoidResult.cs new file mode 100644 index 0000000..a9404a4 --- /dev/null +++ b/lang/cs/Org.Apache.REEF.Utilities/AsyncUtils/VoidResult.cs @@ -0,0 +1,31 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +using System.Threading.Tasks; + +namespace Org.Apache.REEF.Utilities.AsyncUtils +{ + /// <summary> + /// A type used to indicate that no meaningful value is returned. + /// We often use this type with <see cref="Task{TResult}"/> to indicate that an asynchronous + /// operation returns no meaningful value. This eliminates the need to simultaneously + /// support <see cref="Task"/> APIs and <see cref="Task{TResult}"/> APIs + /// </summary> + public struct VoidResult + { + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.Utilities/Org.Apache.Reef.Utilities.csproj ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.Utilities/Org.Apache.Reef.Utilities.csproj b/lang/cs/Org.Apache.REEF.Utilities/Org.Apache.Reef.Utilities.csproj index 5065af6..4576cd6 100644 --- a/lang/cs/Org.Apache.REEF.Utilities/Org.Apache.Reef.Utilities.csproj +++ b/lang/cs/Org.Apache.REEF.Utilities/Org.Apache.Reef.Utilities.csproj @@ -38,6 +38,7 @@ under the License. </ItemGroup> <ItemGroup> <Compile Include="AsyncUtils\RemoveSynchronizationContextAwaiter.cs" /> + <Compile Include="AsyncUtils\VoidResult.cs" /> <Compile Include="Attributes\ClientSideAttribute.cs" /> <Compile Include="Attributes\DriverSideAttribute.cs" /> <Compile Include="Attributes\EvaluatorSideAttribute.cs" /> http://git-wip-us.apache.org/repos/asf/reef/blob/76adb40c/lang/cs/Org.Apache.REEF.sln ---------------------------------------------------------------------- diff --git a/lang/cs/Org.Apache.REEF.sln b/lang/cs/Org.Apache.REEF.sln index 3b13a5e..41932c1 100644 Binary files a/lang/cs/Org.Apache.REEF.sln and b/lang/cs/Org.Apache.REEF.sln differ
