From 864ebd6793a12ff5ef341cd3f280c06e2c7a8c32 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Wed, 16 Sep 2009 14:07:41 +0000 Subject: [PATCH] Implemented upload of usage data. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@4939 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- .../AnalyticsMonitor.cs | 4 +- .../UsageDataCollector/Reference.cs | 150 ++++++++++++++++++ .../UsageDataCollector.csproj | 9 +- .../UsageDataCollector/UsageDataMessage.cs | 39 ++++- .../UsageDataCollector/UsageDataUploader.cs | 44 ++++- .../UsageDataCollector/app.config | 28 ++++ 6 files changed, 259 insertions(+), 15 deletions(-) create mode 100644 src/AddIns/Misc/UsageDataCollector/UsageDataCollector/Reference.cs create mode 100644 src/AddIns/Misc/UsageDataCollector/UsageDataCollector/app.config diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs index bbc340a5fd..4243a8bbfc 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs @@ -19,6 +19,8 @@ namespace ICSharpCode.UsageDataCollector /// public sealed class AnalyticsMonitor : IAnalyticsMonitor { + const string UploadUrl = "http://build.sharpdevelop.net/udc.upload/uploadusagedata.svc"; + public static readonly AnalyticsMonitor Instance = new AnalyticsMonitor(); public static bool EnabledIsUndecided { @@ -99,7 +101,7 @@ namespace ICSharpCode.UsageDataCollector } if (sessionOpened) { UsageDataUploader uploader = new UsageDataUploader(dbFileName); - ThreadPool.QueueUserWorkItem(delegate { uploader.StartUpload(); }); + ThreadPool.QueueUserWorkItem(delegate { uploader.StartUpload(UploadUrl); }); } } diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/Reference.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/Reference.cs new file mode 100644 index 0000000000..7710682cc2 --- /dev/null +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/Reference.cs @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.20728.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace ICSharpCode.UsageDataCollector.Service { + + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] + [System.ServiceModel.ServiceContractAttribute(ConfigurationName="Service.IUDCUploadService")] + internal interface IUDCUploadService { + + // CODEGEN: Generating message contract since the wrapper name (UDCUploadRequest) of message UDCUploadRequest does not match the default value (UploadUsageData) + [System.ServiceModel.OperationContractAttribute(IsOneWay=true, Action="http://tempuri.org/IUDCUploadService/UploadUsageData")] + void UploadUsageData(ICSharpCode.UsageDataCollector.Service.UDCUploadRequest request); + + [System.ServiceModel.OperationContractAttribute(IsOneWay=true, AsyncPattern=true, Action="http://tempuri.org/IUDCUploadService/UploadUsageData")] + System.IAsyncResult BeginUploadUsageData(ICSharpCode.UsageDataCollector.Service.UDCUploadRequest request, System.AsyncCallback callback, object asyncState); + + void EndUploadUsageData(System.IAsyncResult result); + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] + [System.ServiceModel.MessageContractAttribute(WrapperName="UDCUploadRequest", WrapperNamespace="http://tempuri.org/", IsWrapped=true)] + internal partial class UDCUploadRequest { + + [System.ServiceModel.MessageHeaderAttribute(Namespace="http://tempuri.org/")] + public string ApplicationKey; + + [System.ServiceModel.MessageBodyMemberAttribute(Namespace="http://tempuri.org/", Order=0)] + public System.IO.Stream UsageData; + + public UDCUploadRequest() { + } + + public UDCUploadRequest(string ApplicationKey, System.IO.Stream UsageData) { + this.ApplicationKey = ApplicationKey; + this.UsageData = UsageData; + } + } + + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] + internal interface IUDCUploadServiceChannel : ICSharpCode.UsageDataCollector.Service.IUDCUploadService, System.ServiceModel.IClientChannel { + } + + [System.Diagnostics.DebuggerStepThroughAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")] + internal partial class UDCUploadServiceClient : System.ServiceModel.ClientBase, ICSharpCode.UsageDataCollector.Service.IUDCUploadService { + + private BeginOperationDelegate onBeginUploadUsageDataDelegate; + + private EndOperationDelegate onEndUploadUsageDataDelegate; + + private System.Threading.SendOrPostCallback onUploadUsageDataCompletedDelegate; + + public UDCUploadServiceClient() { + } + + public UDCUploadServiceClient(string endpointConfigurationName) : + base(endpointConfigurationName) { + } + + public UDCUploadServiceClient(string endpointConfigurationName, string remoteAddress) : + base(endpointConfigurationName, remoteAddress) { + } + + public UDCUploadServiceClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress) : + base(endpointConfigurationName, remoteAddress) { + } + + public UDCUploadServiceClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress) : + base(binding, remoteAddress) { + } + + public event System.EventHandler UploadUsageDataCompleted; + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + void ICSharpCode.UsageDataCollector.Service.IUDCUploadService.UploadUsageData(ICSharpCode.UsageDataCollector.Service.UDCUploadRequest request) { + base.Channel.UploadUsageData(request); + } + + public void UploadUsageData(string ApplicationKey, System.IO.Stream UsageData) { + ICSharpCode.UsageDataCollector.Service.UDCUploadRequest inValue = new ICSharpCode.UsageDataCollector.Service.UDCUploadRequest(); + inValue.ApplicationKey = ApplicationKey; + inValue.UsageData = UsageData; + ((ICSharpCode.UsageDataCollector.Service.IUDCUploadService)(this)).UploadUsageData(inValue); + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + System.IAsyncResult ICSharpCode.UsageDataCollector.Service.IUDCUploadService.BeginUploadUsageData(ICSharpCode.UsageDataCollector.Service.UDCUploadRequest request, System.AsyncCallback callback, object asyncState) { + return base.Channel.BeginUploadUsageData(request, callback, asyncState); + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public System.IAsyncResult BeginUploadUsageData(string ApplicationKey, System.IO.Stream UsageData, System.AsyncCallback callback, object asyncState) { + ICSharpCode.UsageDataCollector.Service.UDCUploadRequest inValue = new ICSharpCode.UsageDataCollector.Service.UDCUploadRequest(); + inValue.ApplicationKey = ApplicationKey; + inValue.UsageData = UsageData; + return ((ICSharpCode.UsageDataCollector.Service.IUDCUploadService)(this)).BeginUploadUsageData(inValue, callback, asyncState); + } + + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + public void EndUploadUsageData(System.IAsyncResult result) { + base.Channel.EndUploadUsageData(result); + } + + private System.IAsyncResult OnBeginUploadUsageData(object[] inValues, System.AsyncCallback callback, object asyncState) { + string ApplicationKey = ((string)(inValues[0])); + System.IO.Stream UsageData = ((System.IO.Stream)(inValues[1])); + return this.BeginUploadUsageData(ApplicationKey, UsageData, callback, asyncState); + } + + private object[] OnEndUploadUsageData(System.IAsyncResult result) { + this.EndUploadUsageData(result); + return null; + } + + private void OnUploadUsageDataCompleted(object state) { + if ((this.UploadUsageDataCompleted != null)) { + InvokeAsyncCompletedEventArgs e = ((InvokeAsyncCompletedEventArgs)(state)); + this.UploadUsageDataCompleted(this, new System.ComponentModel.AsyncCompletedEventArgs(e.Error, e.Cancelled, e.UserState)); + } + } + + public void UploadUsageDataAsync(string ApplicationKey, System.IO.Stream UsageData) { + this.UploadUsageDataAsync(ApplicationKey, UsageData, null); + } + + public void UploadUsageDataAsync(string ApplicationKey, System.IO.Stream UsageData, object userState) { + if ((this.onBeginUploadUsageDataDelegate == null)) { + this.onBeginUploadUsageDataDelegate = new BeginOperationDelegate(this.OnBeginUploadUsageData); + } + if ((this.onEndUploadUsageDataDelegate == null)) { + this.onEndUploadUsageDataDelegate = new EndOperationDelegate(this.OnEndUploadUsageData); + } + if ((this.onUploadUsageDataCompletedDelegate == null)) { + this.onUploadUsageDataCompletedDelegate = new System.Threading.SendOrPostCallback(this.OnUploadUsageDataCompleted); + } + base.InvokeAsync(this.onBeginUploadUsageDataDelegate, new object[] { + ApplicationKey, + UsageData}, this.onEndUploadUsageDataDelegate, this.onUploadUsageDataCompletedDelegate, userState); + } + } +} diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataCollector.csproj b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataCollector.csproj index 34c148109e..defcc441b3 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataCollector.csproj +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataCollector.csproj @@ -7,7 +7,7 @@ Library ICSharpCode.UsageDataCollector UsageDataCollector - v4.0 + v3.5 ..\..\..\..\..\AddIns\AddIns\Misc\UsageDataCollector\ False False @@ -43,21 +43,26 @@ - ..\..\..\Libraries\SQLite\System.Data.SQLite.dll + ..\..\..\..\Libraries\SQLite\System.Data.SQLite.dll True 3.0 + GlobalAssemblyInfo.cs + + + + \ No newline at end of file diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataMessage.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataMessage.cs index c67520149d..6b738df8e9 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataMessage.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataMessage.cs @@ -9,13 +9,13 @@ using System; using System.Collections.Generic; using System.Runtime.Serialization; -namespace ICSharpCode.UsageDataCollector +namespace ICSharpCode.UsageDataCollector.DataContract { /// /// Represents a message used to send data to the server. /// [DataContract] - sealed class UsageDataMessage + public sealed class UsageDataMessage { /// /// Unique identifier per user. @@ -23,10 +23,17 @@ namespace ICSharpCode.UsageDataCollector /// [DataMember] public Guid UserID; - + + /// + /// Gets the sessions stored in this message. + /// [DataMember] public List Sessions = new List(); + /// + /// Finds the session with the specified session ID. + /// + /// No session with the specified ID was found. public UsageDataSession FindSession(long sessionID) { foreach (UsageDataSession s in Sessions) { @@ -37,8 +44,11 @@ namespace ICSharpCode.UsageDataCollector } } + /// + /// A UsageData session. + /// [DataContract] - sealed class UsageDataSession + public sealed class UsageDataSession { /// /// ID of the session, usually unique per user (unless the user restores a backup of the database). @@ -60,12 +70,21 @@ namespace ICSharpCode.UsageDataCollector [DataMember] public DateTime? EndTime; + /// + /// List of environment properties associated with the session. + /// [DataMember] public List EnvironmentProperties = new List(); + /// + /// List of feature uses associated with the session. + /// [DataMember] public List FeatureUses = new List(); + /// + /// List of exceptions associated with the session. + /// [DataMember] public List Exceptions = new List(); } @@ -76,11 +95,17 @@ namespace ICSharpCode.UsageDataCollector /// or have multiple OS installations pointing to the same database (e.g. when SharpDevelop is used on an USB stick). /// [DataContract] - sealed class UsageDataEnvironmentProperty + public sealed class UsageDataEnvironmentProperty { + /// + /// Name of the property. + /// [DataMember] public string Name; + /// + /// Value of the property. + /// [DataMember] public string Value; } @@ -89,7 +114,7 @@ namespace ICSharpCode.UsageDataCollector /// Represents a feature being used. /// [DataContract] - sealed class UsageDataFeatureUse + public sealed class UsageDataFeatureUse { /// /// The time when the feature was used. @@ -124,7 +149,7 @@ namespace ICSharpCode.UsageDataCollector /// The exception message might e.g. contain names of the file the user is working on. /// [DataContract] - sealed class UsageDataException + public sealed class UsageDataException { /// /// The time when the exception occurred. diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs index d06886fa14..beb7810726 100644 --- a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/UsageDataUploader.cs @@ -13,8 +13,13 @@ using System.IO; using System.IO.Compression; using System.Linq; using System.Runtime.Serialization; +using System.ServiceModel; +using System.ServiceModel.Channels; using System.Xml; +using ICSharpCode.UsageDataCollector.DataContract; +using ICSharpCode.UsageDataCollector.Service; + namespace ICSharpCode.UsageDataCollector { /// @@ -70,7 +75,20 @@ namespace ICSharpCode.UsageDataCollector /// /// The database version is not compatible with this /// version of the AnalyticsSessionWriter. - public void StartUpload() + public void StartUpload(string uploadUrl) + { + EndpointAddress epa = new EndpointAddress(uploadUrl); + BasicHttpBinding binding = new BasicHttpBinding(); + binding.Security.Mode = BasicHttpSecurityMode.None; + StartUpload(binding, epa); + } + + /// + /// Starts the upload of the usage data. + /// + /// The database version is not compatible with this + /// version of the AnalyticsSessionWriter. + public void StartUpload(Binding binding, EndpointAddress endpoint) { UsageDataMessage message; using (SQLiteConnection connection = OpenConnection()) { @@ -85,15 +103,32 @@ namespace ICSharpCode.UsageDataCollector } } if (message != null) { + string commaSeparatedSessionIDList = GetCommaSeparatedIDList(message.Sessions); + DataContractSerializer serializer = new DataContractSerializer(typeof(UsageDataMessage)); - using (FileStream fs = new FileStream(Path.Combine(Path.GetTempPath(), "SharpDevelopUsageData.xml.gz"), FileMode.Create, FileAccess.Write)) { - using (GZipStream zip = new GZipStream(fs, CompressionMode.Compress)) { + byte[] data; + using (MemoryStream ms = new MemoryStream()) { + using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress)) { serializer.WriteObject(zip, message); } + data = ms.ToArray(); } + message = null; + File.WriteAllBytes(Path.Combine(Path.GetTempPath(), "SharpDevelopUsageData.xml.gz"), data); + + UDCUploadServiceClient client = new UDCUploadServiceClient(binding, endpoint); + client.BeginUploadUsageData("sharpdevelop", new MemoryStream(data), + ar => UsageDataUploaded(ar, client, commaSeparatedSessionIDList), null); } } + void UsageDataUploaded(IAsyncResult result, UDCUploadServiceClient client, string commaSeparatedSessionIDList) + { + client.EndUploadUsageData(result); + + RemoveUploadedData(commaSeparatedSessionIDList); + } + Version expectedDBVersion = new Version(1, 0, 1); void CheckDatabaseVersion(SQLiteConnection connection) @@ -224,9 +259,8 @@ namespace ICSharpCode.UsageDataCollector /// /// Removes the data that was successfully uploaded and sets the 'lastUpload' property. /// - void RemoveUploadedData(IEnumerable sessions) + void RemoveUploadedData(string commaSeparatedSessionIDList) { - string commaSeparatedSessionIDList = GetCommaSeparatedIDList(sessions); using (SQLiteConnection connection = OpenConnection()) { using (SQLiteTransaction transaction = connection.BeginTransaction()) { using (SQLiteCommand cmd = connection.CreateCommand()) { diff --git a/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/app.config b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/app.config new file mode 100644 index 0000000000..2e6835f33c --- /dev/null +++ b/src/AddIns/Misc/UsageDataCollector/UsageDataCollector/app.config @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file