~halega/+junk/sharpdevelop

« back to all changes in this revision

Viewing changes to src/AddIns/Misc/UsageDataCollector/UsageDataCollector.AddIn/AnalyticsMonitor.cs

  • Committer: sk
  • Date: 2011-09-10 05:17:57 UTC
  • Revision ID: halega@halega.com-20110910051757-qfouz1llya9m6boy
4.1.0.7915 Release Candidate 1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
// Copyright (c) AlphaSierraPapa for the SharpDevelop Team (for details please see \doc\copyright.txt)
 
2
// This code is distributed under the GNU LGPL (for details please see \doc\license.txt)
 
3
 
 
4
using System;
 
5
using System.Collections.Generic;
 
6
using System.Globalization;
 
7
using System.IO;
 
8
using System.Linq;
 
9
using System.Threading;
 
10
using System.Windows.Media;
 
11
using ICSharpCode.Core;
 
12
using ICSharpCode.Core.Services;
 
13
using ICSharpCode.SharpDevelop;
 
14
using ICSharpCode.UsageDataCollector.Contracts;
 
15
 
 
16
namespace ICSharpCode.UsageDataCollector
 
17
{
 
18
        /// <summary>
 
19
        /// Main singleton class of the analytics. This class is thread-safe.
 
20
        /// </summary>
 
21
        public sealed partial class AnalyticsMonitor : IAnalyticsMonitor
 
22
        {
 
23
                const string UploadUrl = "http://usagedatacollector.sharpdevelop.net/upload/UploadUsageData.svc";
 
24
                const string ProductName = "sharpdevelop";
 
25
                public static readonly Uri PrivacyStatementUrl = new Uri("http://www.icsharpcode.net/OpenSource/SD/UsageDataCollector/");
 
26
                
 
27
                public static readonly AnalyticsMonitor Instance = new AnalyticsMonitor();
 
28
                
 
29
                public static bool EnabledIsUndecided {
 
30
                        get {
 
31
                                return string.IsNullOrEmpty(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled"));
 
32
                        }
 
33
                }
 
34
                
 
35
                /// <summary>
 
36
                /// Allows to enable/disable the usage data monitoring.
 
37
                /// </summary>
 
38
                public static bool Enabled {
 
39
                        get {
 
40
                                return string.Equals(PropertyService.Get("ICSharpCode.UsageDataCollector.Enabled"), bool.TrueString, StringComparison.OrdinalIgnoreCase);
 
41
                        }
 
42
                        set {
 
43
                                PropertyService.Set("ICSharpCode.UsageDataCollector.Enabled", value.ToString());
 
44
                                // Initially opening the session takes some time; which is bad for the startpage
 
45
                                // because the animation would start with a delay. We solve this by calling Open/CloseSession
 
46
                                // on a background thread.
 
47
                                ThreadPool.QueueUserWorkItem(delegate { AsyncEnableDisable(); } );
 
48
                        }
 
49
                }
 
50
                
 
51
                static void AsyncEnableDisable()
 
52
                {
 
53
                        if (Enabled) {
 
54
                                Instance.OpenSession();
 
55
                        } else {
 
56
                                Instance.CloseSession();
 
57
                                Instance.TryDeleteDatabase();
 
58
                        }
 
59
                }
 
60
                
 
61
                readonly object lockObj = new object();
 
62
                string dbFileName;
 
63
                UsageDataSessionWriter session;
 
64
                
 
65
                private AnalyticsMonitor()
 
66
                {
 
67
                        var container = ServiceManager.Instance.GetRequiredService<ThreadSafeServiceContainer>();
 
68
                        container.TryAddService(typeof(IAnalyticsMonitor), this);
 
69
                        dbFileName = Path.Combine(PropertyService.ConfigDirectory, "usageData.dat");
 
70
                        
 
71
                        SharpDevelop.Gui.WorkbenchSingleton.WorkbenchUnloaded += delegate { CloseSession(); };
 
72
                }
 
73
                
 
74
                static Guid FindUserId()
 
75
                {
 
76
                        // Ensure we assign only 1 ID to each user; even when he has multiple UDC databases because there
 
77
                        // are multiple SharpDevelop versions installed. We do this by reading out the userID GUID from
 
78
                        // the existing databases in any neighbor config directory.
 
79
                        string[] otherSharpDevelopVersions;
 
80
                        try {
 
81
                                otherSharpDevelopVersions = Directory.GetDirectories(Path.Combine(PropertyService.ConfigDirectory, ".."));
 
82
                        } catch (IOException) {
 
83
                                otherSharpDevelopVersions = new string[0];
 
84
                        } catch (UnauthorizedAccessException) {
 
85
                                otherSharpDevelopVersions = new string[0];
 
86
                        }
 
87
                        LoggingService.Debug("Looking for existing UDC database in " + otherSharpDevelopVersions.Length + " directories");
 
88
                        foreach (string path in otherSharpDevelopVersions) {
 
89
                                string dbFileName = Path.Combine(path, "usageData.dat");
 
90
                                if (File.Exists(dbFileName)) {
 
91
                                        LoggingService.Info("Found existing UDC database: " + dbFileName);
 
92
                                        Guid? guid = UsageDataSessionWriter.RetrieveUserId(dbFileName);
 
93
                                        if (guid.HasValue) {
 
94
                                                LoggingService.Info("Found GUID in existing UDC database: " + guid.Value);
 
95
                                                return guid.Value;
 
96
                                        }
 
97
                                }
 
98
                        }
 
99
                        LoggingService.Info("Did not find existing UDC database; creating new GUID.");
 
100
                        return Guid.NewGuid();
 
101
                }
 
102
                
 
103
                /// <summary>
 
104
                /// Opens the database connection, updates the database if required.
 
105
                /// Will start an upload to the server, if required.
 
106
                /// </summary>
 
107
                public void OpenSession()
 
108
                {
 
109
                        IEnumerable<UsageDataEnvironmentProperty> appEnvironmentProperties = GetAppProperties();
 
110
                        bool sessionOpened = false;
 
111
                        lock (lockObj) {
 
112
                                if (session == null) {
 
113
                                        try {
 
114
                                                session = new UsageDataSessionWriter(dbFileName, FindUserId);
 
115
                                        } catch (IncompatibleDatabaseException ex) {
 
116
                                                if (ex.ActualVersion < ex.ExpectedVersion) {
 
117
                                                        LoggingService.Info("AnalyticsMonitor: " + ex.Message + ", removing old database");
 
118
                                                        Guid? oldUserId = UsageDataSessionWriter.RetrieveUserId(dbFileName);
 
119
                                                        // upgrade database by deleting the old one
 
120
                                                        TryDeleteDatabase();
 
121
                                                        try {
 
122
                                                                session = new UsageDataSessionWriter(dbFileName, () => (oldUserId ?? FindUserId()));
 
123
                                                        } catch (IncompatibleDatabaseException ex2) {
 
124
                                                                LoggingService.Warn("AnalyticsMonitor: Could not upgrade database: " + ex2.Message);
 
125
                                                        }
 
126
                                                } else {
 
127
                                                        LoggingService.Warn("AnalyticsMonitor: " + ex.Message);
 
128
                                                }
 
129
                                        }
 
130
                                        
 
131
                                        if (session != null) {
 
132
                                                session.OnException = MessageService.ShowException;
 
133
                                                session.AddEnvironmentData(appEnvironmentProperties);
 
134
                                                
 
135
                                                sessionOpened = true;
 
136
                                        }
 
137
                                }
 
138
                        }
 
139
                        if (sessionOpened) {
 
140
                                UsageDataUploader uploader = new UsageDataUploader(dbFileName, ProductName);
 
141
                                uploader.EnvironmentDataForDummySession = appEnvironmentProperties;
 
142
                                ThreadPool.QueueUserWorkItem(delegate { uploader.StartUpload(UploadUrl); });
 
143
                        }
 
144
                }
 
145
                
 
146
                static IEnumerable<UsageDataEnvironmentProperty> GetAppProperties()
 
147
                {
 
148
                        List<UsageDataEnvironmentProperty> properties = new List<UsageDataEnvironmentProperty> {
 
149
                                new UsageDataEnvironmentProperty { Name = "appVersion", Value = RevisionClass.Major + "." + RevisionClass.Minor + "." + RevisionClass.Build + "." + RevisionClass.Revision },
 
150
                                new UsageDataEnvironmentProperty { Name = "language", Value = ResourceService.Language },
 
151
                                new UsageDataEnvironmentProperty { Name = "culture", Value = CultureInfo.CurrentCulture.Name },
 
152
                                new UsageDataEnvironmentProperty { Name = "userAddInCount", Value = AddInTree.AddIns.Where(a => !a.IsPreinstalled).Count().ToString() },
 
153
                                new UsageDataEnvironmentProperty { Name = "branch", Value = BranchName },
 
154
                                new UsageDataEnvironmentProperty { Name = "commit", Value = CommitHash },
 
155
                                new UsageDataEnvironmentProperty { Name = "renderingTier", Value = (RenderCapability.Tier >> 16).ToString() }
 
156
                        };
 
157
                        string PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432");
 
158
                        if (string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
 
159
                                PROCESSOR_ARCHITECTURE = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
 
160
                        }
 
161
                        if (!string.IsNullOrEmpty(PROCESSOR_ARCHITECTURE)) {
 
162
                                properties.Add(new UsageDataEnvironmentProperty { Name = "architecture", Value = PROCESSOR_ARCHITECTURE });
 
163
                        }
 
164
                        #if DEBUG
 
165
                        properties.Add(new UsageDataEnvironmentProperty { Name = "debug", Value = "true" });
 
166
                        #endif
 
167
                        return properties;
 
168
                }
 
169
                
 
170
                /// <summary>
 
171
                /// Retrieves all stored data as XML text.
 
172
                /// </summary>
 
173
                /// <exception cref="IncompatibleDatabaseException">The database version is not compatible with this
 
174
                /// version of the AnalyticsSessionWriter.</exception>
 
175
                public string GetTextForStoredData()
 
176
                {
 
177
                        lock (lockObj) {
 
178
                                if (session != null)
 
179
                                        session.Flush();
 
180
                        }
 
181
                        UsageDataUploader uploader = new UsageDataUploader(dbFileName, ProductName);
 
182
                        return uploader.GetTextForStoredData();
 
183
                }
 
184
                
 
185
                void TryDeleteDatabase()
 
186
                {
 
187
                        lock (lockObj) {
 
188
                                CloseSession();
 
189
                                try {
 
190
                                        File.Delete(dbFileName);
 
191
                                } catch (IOException ex) {
 
192
                                        LoggingService.Warn("AnalyticsMonitor: Could delete database: " + ex.Message);
 
193
                                } catch (AccessViolationException ex) {
 
194
                                        LoggingService.Warn("AnalyticsMonitor: Could delete database: " + ex.Message);
 
195
                                }
 
196
                        }
 
197
                }
 
198
                
 
199
                public void CloseSession()
 
200
                {
 
201
                        lock (lockObj) {
 
202
                                if (session != null) {
 
203
                                        session.Dispose();
 
204
                                        session = null;
 
205
                                }
 
206
                        }
 
207
                }
 
208
                
 
209
                public void TrackException(Exception exception)
 
210
                {
 
211
                        lock (lockObj) {
 
212
                                if (session != null) {
 
213
                                        session.AddException(exception);
 
214
                                }
 
215
                        }
 
216
                }
 
217
                
 
218
                public IAnalyticsMonitorTrackedFeature TrackFeature(string featureName, string activationMethod)
 
219
                {
 
220
                        TrackedFeature feature = new TrackedFeature();
 
221
                        lock (lockObj) {
 
222
                                if (session != null) {
 
223
                                        feature.Feature = session.AddFeatureUse(featureName, activationMethod);
 
224
                                }
 
225
                        }
 
226
                        return feature;
 
227
                }
 
228
                
 
229
                sealed class TrackedFeature : IAnalyticsMonitorTrackedFeature
 
230
                {
 
231
                        internal FeatureUse Feature;
 
232
                        
 
233
                        public void EndTracking()
 
234
                        {
 
235
                                if (Feature != null)
 
236
                                        Feature.TrackEndTime();
 
237
                        }
 
238
                }
 
239
        }
 
240
        
 
241
        public class AutoStartCommand : AbstractCommand
 
242
        {
 
243
                public override void Run()
 
244
                {
 
245
                        if (AnalyticsMonitor.Enabled)
 
246
                                AnalyticsMonitor.Instance.OpenSession();
 
247
                }
 
248
        }
 
249
}