THRIFT-1037 Proposed changes to support Silverlight, Windows Phone and AsyncCTP v3
Patch: Damian Mehers & Jens Geyer
git-svn-id: https://svn.apache.org/repos/asf/thrift/trunk@1211880 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/lib/csharp/src/Collections/THashSet.cs b/lib/csharp/src/Collections/THashSet.cs
index 0ddea54..e2fc8b5 100644
--- a/lib/csharp/src/Collections/THashSet.cs
+++ b/lib/csharp/src/Collections/THashSet.cs
@@ -23,11 +23,13 @@
namespace Thrift.Collections
{
+#if !SILVERLIGHT
[Serializable]
+#endif
public class THashSet<T> : ICollection<T>
{
-#if NET_2_0
- TDictSet<T> set = new TDictSet<T>();
+#if NET_2_0 || SILVERLIGHT
+ TDictSet<T> set = new TDictSet<T>();
#else
HashSet<T> set = new HashSet<T>();
#endif
@@ -76,8 +78,8 @@
return set.Remove(item);
}
-#if NET_2_0
- private class TDictSet<V> : ICollection<V>
+#if NET_2_0 || SILVERLIGHT
+ private class TDictSet<V> : ICollection<V>
{
Dictionary<V, TDictSet<V>> dict = new Dictionary<V, TDictSet<V>>();
diff --git a/lib/csharp/src/Properties/AssemblyInfo.WP7.cs b/lib/csharp/src/Properties/AssemblyInfo.WP7.cs
new file mode 100644
index 0000000..697d7aa
--- /dev/null
+++ b/lib/csharp/src/Properties/AssemblyInfo.WP7.cs
@@ -0,0 +1,56 @@
+/**
+ * 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.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("Thrift.WP7")]
+[assembly: AssemblyDescription("C# bindings for the Apache Thrift RPC system")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("The Apache Software Foundation")]
+[assembly: AssemblyProduct("Thrift")]
+[assembly: AssemblyCopyright("The Apache Software Foundation")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+//@TODO where to put License information?
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("a343f89c-57dd-4fa8-a9c6-35391cd5f655")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("0.9.0.0")]
+[assembly: AssemblyFileVersion("0.9.0.0")]
diff --git a/lib/csharp/src/Protocol/TBinaryProtocol.cs b/lib/csharp/src/Protocol/TBinaryProtocol.cs
index 4b3980e..e6b69d6 100644
--- a/lib/csharp/src/Protocol/TBinaryProtocol.cs
+++ b/lib/csharp/src/Protocol/TBinaryProtocol.cs
@@ -201,7 +201,12 @@
public override void WriteDouble(double d)
{
+#if !SILVERLIGHT
WriteI64(BitConverter.DoubleToInt64Bits(d));
+#else
+ var bytes = BitConverter.GetBytes(d);
+ WriteI64(BitConverter.ToInt64(bytes, 0));
+#endif
}
public override void WriteBinary(byte[] b)
@@ -348,7 +353,13 @@
public override double ReadDouble()
{
+#if !SILVERLIGHT
return BitConverter.Int64BitsToDouble(ReadI64());
+#else
+ var value = ReadI64();
+ var bytes = BitConverter.GetBytes(value);
+ return BitConverter.ToDouble(bytes, 0);
+#endif
}
public void SetReadLength(int readLength)
@@ -382,7 +393,7 @@
CheckReadLength(size);
byte[] buf = new byte[size];
trans.ReadAll(buf, 0, size);
- return Encoding.UTF8.GetString(buf);
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
}
private int ReadAll(byte[] buf, int off, int len)
diff --git a/lib/csharp/src/Protocol/TJSONProtocol.cs b/lib/csharp/src/Protocol/TJSONProtocol.cs
index a35fcd3..ed6970e 100644
--- a/lib/csharp/src/Protocol/TJSONProtocol.cs
+++ b/lib/csharp/src/Protocol/TJSONProtocol.cs
@@ -841,7 +841,7 @@
if (reader.Peek() == QUOTE[0])
{
byte[] arr = ReadJSONString(true);
- double dub = Double.Parse(utf8Encoding.GetString(arr), CultureInfo.InvariantCulture);
+ double dub = Double.Parse(utf8Encoding.GetString(arr,0,arr.Length), CultureInfo.InvariantCulture);
if (!context.EscapeNumbers() && !Double.IsNaN(dub) &&
!Double.IsInfinity(dub))
@@ -938,7 +938,8 @@
"Message contained bad version.");
}
- message.Name = utf8Encoding.GetString(ReadJSONString(false));
+ var buf = ReadJSONString(false);
+ message.Name = utf8Encoding.GetString(buf,0,buf.Length);
message.Type = (TMessageType)ReadJSONInteger();
message.SeqID = (int)ReadJSONInteger();
return message;
@@ -1059,7 +1060,8 @@
public override String ReadString()
{
- return utf8Encoding.GetString(ReadJSONString(false));
+ var buf = ReadJSONString(false);
+ return utf8Encoding.GetString(buf,0,buf.Length);
}
public override byte[] ReadBinary()
diff --git a/lib/csharp/src/Protocol/TProtocol.cs b/lib/csharp/src/Protocol/TProtocol.cs
index 4f723dd..b6884c9 100644
--- a/lib/csharp/src/Protocol/TProtocol.cs
+++ b/lib/csharp/src/Protocol/TProtocol.cs
@@ -84,8 +84,9 @@
public abstract long ReadI64();
public abstract double ReadDouble();
public virtual string ReadString() {
- return Encoding.UTF8.GetString(ReadBinary());
- }
+ var buf = ReadBinary();
+ return Encoding.UTF8.GetString(buf, 0, buf.Length);
+ }
public abstract byte[] ReadBinary();
}
}
diff --git a/lib/csharp/src/Thrift.WP7.csproj b/lib/csharp/src/Thrift.WP7.csproj
new file mode 100644
index 0000000..be38bc8
--- /dev/null
+++ b/lib/csharp/src/Thrift.WP7.csproj
@@ -0,0 +1,110 @@
+<?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
+ 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.
+-->
+<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProductVersion>10.0.20506</ProductVersion>
+ <SchemaVersion>2.0</SchemaVersion>
+ <ProjectGuid>{DF58BDB0-2457-4A52-9981-65A0E8B50833}</ProjectGuid>
+ <ProjectTypeGuids>{C089C8C0-30E0-4E22-80C0-CE093F111A43};{fae04ec0-301f-11d3-bf4b-00c04f79efbc}</ProjectTypeGuids>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Thrift.WP7</RootNamespace>
+ <AssemblyName>Thrift.WP7</AssemblyName>
+ <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
+ <SilverlightVersion>$(TargetFrameworkVersion)</SilverlightVersion>
+ <TargetFrameworkProfile>WindowsPhone</TargetFrameworkProfile>
+ <TargetFrameworkIdentifier>Silverlight</TargetFrameworkIdentifier>
+ <SilverlightApplication>false</SilverlightApplication>
+ <ValidateXaml>true</ValidateXaml>
+ <ThrowErrorsInValidation>true</ThrowErrorsInValidation>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>Bin\Debug</OutputPath>
+ <DefineConstants>DEBUG;TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>Bin\Release</OutputPath>
+ <DefineConstants>TRACE;SILVERLIGHT;WINDOWS_PHONE</DefineConstants>
+ <NoStdLib>true</NoStdLib>
+ <NoConfig>true</NoConfig>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="Microsoft.Phone" />
+ <Reference Include="Microsoft.Phone.Interop" />
+ <Reference Include="System.Runtime.Serialization" />
+ <Reference Include="System.Servicemodel.Web" />
+ <Reference Include="System.Windows" />
+ <Reference Include="system" />
+ <Reference Include="System.Core" />
+ <Reference Include="System.Xml" />
+ <Reference Include="System.Net" />
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="Collections\THashSet.cs" />
+ <Compile Include="Properties\AssemblyInfo.WP7.cs" />
+ <Compile Include="Protocol\TBase.cs" />
+ <Compile Include="Protocol\TBase64Utils.cs" />
+ <Compile Include="Protocol\TBinaryProtocol.cs" />
+ <Compile Include="Protocol\TField.cs" />
+ <Compile Include="Protocol\TJSONProtocol.cs" />
+ <Compile Include="Protocol\TList.cs" />
+ <Compile Include="Protocol\TMap.cs" />
+ <Compile Include="Protocol\TMessage.cs" />
+ <Compile Include="Protocol\TMessageType.cs" />
+ <Compile Include="Protocol\TProtocol.cs" />
+ <Compile Include="Protocol\TProtocolException.cs" />
+ <Compile Include="Protocol\TProtocolFactory.cs" />
+ <Compile Include="Protocol\TProtocolUtil.cs" />
+ <Compile Include="Protocol\TSet.cs" />
+ <Compile Include="Protocol\TStruct.cs" />
+ <Compile Include="Protocol\TType.cs" />
+ <Compile Include="TApplicationException.cs" />
+ <Compile Include="TProcessor.cs" />
+ <Compile Include="Transport\TFramedTransport.cs" />
+ <Compile Include="Transport\THttpClient.cs" />
+ <Compile Include="Transport\TStreamTransport.cs" />
+ <Compile Include="Transport\TTransport.cs" />
+ <Compile Include="Transport\TTransportException.cs" />
+ <Compile Include="Transport\TTransportFactory.cs" />
+ </ItemGroup>
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.$(TargetFrameworkProfile).Overrides.targets" />
+ <Import Project="$(MSBuildExtensionsPath)\Microsoft\Silverlight for Phone\$(TargetFrameworkVersion)\Microsoft.Silverlight.CSharp.targets" />
+ <ProjectExtensions />
+ <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
+ Other similar extension points exist, see Microsoft.Common.targets.
+ <Target Name="BeforeBuild">
+ </Target>
+ <Target Name="AfterBuild">
+ </Target>
+ -->
+</Project>
\ No newline at end of file
diff --git a/lib/csharp/src/Transport/THttpClient.cs b/lib/csharp/src/Transport/THttpClient.cs
index bf6ca4d..768b64e 100644
--- a/lib/csharp/src/Transport/THttpClient.cs
+++ b/lib/csharp/src/Transport/THttpClient.cs
@@ -23,6 +23,7 @@
using System.Collections.Generic;
using System.IO;
using System.Net;
+using System.Threading;
namespace Thrift.Transport
{
@@ -119,6 +120,7 @@
outputStream.Write(buf, off, len);
}
+#if !SILVERLIGHT
public override void Flush()
{
try
@@ -153,11 +155,12 @@
throw new TTransportException(TTransportException.ExceptionType.Unknown, "Couldn't connect to server: " + wx);
}
}
-
- private HttpWebRequest CreateRequest()
+#endif
+ private HttpWebRequest CreateRequest()
{
HttpWebRequest connection = (HttpWebRequest)WebRequest.Create(uri);
+#if !SILVERLIGHT
if (connectTimeout > 0)
{
connection.Timeout = connectTimeout;
@@ -166,23 +169,190 @@
{
connection.ReadWriteTimeout = readTimeout;
}
-
+#endif
// Make the request
connection.ContentType = "application/x-thrift";
connection.Accept = "application/x-thrift";
connection.UserAgent = "C#/THttpClient";
connection.Method = "POST";
+#if !SILVERLIGHT
connection.ProtocolVersion = HttpVersion.Version10;
+#endif
- //add custom headers here
+ //add custom headers here
foreach (KeyValuePair<string, string> item in customHeaders)
{
+#if !SILVERLIGHT
connection.Headers.Add(item.Key, item.Value);
+#else
+ connection.Headers[item.Key] = item.Value;
+#endif
}
connection.Proxy = null;
- return connection;
+ return connection;
}
- }
+
+#if SILVERLIGHT
+ public override IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ // Extract request and reset buffer
+ var data = outputStream.ToArray();
+
+ //requestBuffer_ = new MemoryStream();
+
+ try
+ {
+ // Create connection object
+ var flushAsyncResult = new FlushAsyncResult(callback, state);
+ flushAsyncResult.Connection = CreateRequest();
+
+ flushAsyncResult.Data = data;
+
+
+ flushAsyncResult.Connection.BeginGetRequestStream(GetRequestStreamCallback, flushAsyncResult);
+ return flushAsyncResult;
+
+ }
+ catch (IOException iox)
+ {
+ throw new TTransportException(iox.ToString());
+ }
+ }
+
+ public override void EndFlush(IAsyncResult asyncResult)
+ {
+ try
+ {
+ var flushAsyncResult = (FlushAsyncResult) asyncResult;
+
+ if (!flushAsyncResult.IsCompleted)
+ {
+ var waitHandle = flushAsyncResult.AsyncWaitHandle;
+ waitHandle.WaitOne(); // blocking INFINITEly
+ waitHandle.Close();
+ }
+
+ if (flushAsyncResult.AsyncException != null)
+ {
+ throw flushAsyncResult.AsyncException;
+ }
+ } finally
+ {
+ outputStream = new MemoryStream();
+ }
+
+ }
+
+
+ private void GetRequestStreamCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ var reqStream = flushAsyncResult.Connection.EndGetRequestStream(asynchronousResult);
+ reqStream.Write(flushAsyncResult.Data, 0, flushAsyncResult.Data.Length);
+ reqStream.Flush();
+ reqStream.Close();
+
+ // Start the asynchronous operation to get the response
+ flushAsyncResult.Connection.BeginGetResponse(GetResponseCallback, flushAsyncResult);
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+ }
+
+ private void GetResponseCallback(IAsyncResult asynchronousResult)
+ {
+ var flushAsyncResult = (FlushAsyncResult)asynchronousResult.AsyncState;
+ try
+ {
+ inputStream = flushAsyncResult.Connection.EndGetResponse(asynchronousResult).GetResponseStream();
+ }
+ catch (Exception exception)
+ {
+ flushAsyncResult.AsyncException = new TTransportException(exception.ToString());
+ }
+ flushAsyncResult.UpdateStatusToComplete();
+ flushAsyncResult.NotifyCallbackWhenAvailable();
+ }
+
+ // Based on http://msmvps.com/blogs/luisabreu/archive/2009/06/15/multithreading-implementing-the-iasyncresult-interface.aspx
+ class FlushAsyncResult : IAsyncResult
+ {
+ private volatile Boolean _isCompleted;
+ private ManualResetEvent _evt;
+ private readonly AsyncCallback _cbMethod;
+ private readonly Object _state;
+
+ public FlushAsyncResult(AsyncCallback cbMethod, Object state)
+ {
+ _cbMethod = cbMethod;
+ _state = state;
+ }
+
+ internal byte[] Data { get; set; }
+ internal HttpWebRequest Connection { get; set; }
+ internal TTransportException AsyncException { get; set; }
+
+ public object AsyncState
+ {
+ get { return _state; }
+ }
+ public WaitHandle AsyncWaitHandle
+ {
+ get { return GetEvtHandle(); }
+ }
+ public bool CompletedSynchronously
+ {
+ get { return false; }
+ }
+ public bool IsCompleted
+ {
+ get { return _isCompleted; }
+ }
+ private readonly Object _locker = new Object();
+ private ManualResetEvent GetEvtHandle()
+ {
+ lock (_locker)
+ {
+ if (_evt == null)
+ {
+ _evt = new ManualResetEvent(false);
+ }
+ if (_isCompleted)
+ {
+ _evt.Set();
+ }
+ }
+ return _evt;
+ }
+ internal void UpdateStatusToComplete()
+ {
+ _isCompleted = true; //1. set _iscompleted to true
+ lock (_locker)
+ {
+ if (_evt != null)
+ {
+ _evt.Set(); //2. set the event, when it exists
+ }
+ }
+ }
+
+ internal void NotifyCallbackWhenAvailable()
+ {
+ if (_cbMethod != null)
+ {
+ _cbMethod(this);
+ }
+ }
+ }
+
+#endif
+ }
}
diff --git a/lib/csharp/src/Transport/TTransport.cs b/lib/csharp/src/Transport/TTransport.cs
index cecde87..520ba46 100644
--- a/lib/csharp/src/Transport/TTransport.cs
+++ b/lib/csharp/src/Transport/TTransport.cs
@@ -71,5 +71,14 @@
public virtual void Flush()
{
}
- }
+
+ public virtual IAsyncResult BeginFlush(AsyncCallback callback, object state)
+ {
+ return null;
+ }
+
+ public virtual void EndFlush(IAsyncResult asyncResult)
+ {
+ }
+ }
}