diff --git a/Exceptron.Client/Configuration/ExceptronConfiguration.cs b/Exceptron.Client/Configuration/ExceptronConfiguration.cs
new file mode 100644
index 000000000..695710699
--- /dev/null
+++ b/Exceptron.Client/Configuration/ExceptronConfiguration.cs
@@ -0,0 +1,74 @@
+using System.ComponentModel;
+using System.Configuration;
+using Exceptron.Client.Message;
+
+namespace Exceptron.Client.Configuration
+{
+
+ public class ExceptronConfiguration : ConfigurationSection
+ {
+ public ExceptronConfiguration()
+ {
+ Host = "http://exceptron.azurewebsites.net/api/v1/";
+ IncludeMachineName = true;
+ }
+
+ public static ExceptronConfiguration ReadConfig(string sectionName = "exceptron")
+ {
+ var configSection = ConfigurationManager.GetSection(sectionName);
+
+ if (configSection == null)
+ {
+ throw new ConfigurationErrorsException("ExceptronConfiguration section missing.");
+ }
+
+ return (ExceptronConfiguration)configSection;
+ }
+
+
+ ///
+ /// exceptron api address. Do not modify this property.
+ ///
+ [EditorBrowsable(EditorBrowsableState.Never)]
+ public string Host { get; set; }
+
+ ///
+ /// If ExceptronClinet should throw exceptions in case of an error. Default:
+ ///
+ ///
+ /// Its recommended that this flag is set to True during development and in production systems.
+ /// If an exception is thrown while this flag is set to the thrown exception will be returned in
+ ///
+ [ConfigurationProperty("throwExceptions", DefaultValue = false)]
+ public bool ThrowExceptions
+ {
+ get { return (bool)this["throwExceptions"]; }
+ set { this["throwExceptions"] = value; }
+ }
+
+ ///
+ /// The API of this application. Can find your API key in application settings page.
+ ///
+ [ConfigurationProperty("apiKey")]
+ public string ApiKey
+ {
+ get { return (string)this["apiKey"]; }
+ set { this["apiKey"] = value; }
+ }
+
+
+ ///
+ /// If the machine name should be attached to the exception report
+ ///
+ /// Machine name can be usefull in webfarm enviroments when multiple
+ /// servers are running the same app and the issue could be machine specific.
+ /// Hoewever, You might want to disable this feature for privacy reasons.
+ [ConfigurationProperty("includeMachineName", DefaultValue = true)]
+ public bool IncludeMachineName
+ {
+ get { return (bool)this["includeMachineName"]; }
+ set { this["includeMachineName"] = value; }
+ }
+
+ }
+}
diff --git a/Exceptron.Client/ExceptionData.cs b/Exceptron.Client/ExceptionData.cs
new file mode 100644
index 000000000..3e804e265
--- /dev/null
+++ b/Exceptron.Client/ExceptionData.cs
@@ -0,0 +1,62 @@
+using System;
+using System.Web;
+
+namespace Exceptron.Client
+{
+ ///
+ /// Represents information that will be used to construct an exception report.
+ ///
+ public class ExceptionData
+ {
+ ///
+ /// Exception that is being reported
+ ///
+ public Exception Exception { get; set; }
+
+ ///
+ /// Component that experianced this exception.
+ ///
+ ///
+ /// It is common to use the logger name that was used to log the exception as the component.
+ ///
+ ///
+ /// DataAccess, Configuration, Registration, etc.
+ ///
+ public string Component { get; set; }
+
+ ///
+ /// ID that will uniquely identify the user
+ ///
+ ///
+ /// This Id does not have to be tied to the user's identity.
+ /// You can use a system generated unique ID such as GUID.
+ /// This field is used to report how many unique users are experiencing an error.
+ ///
+ ///
+ /// "62E5C8EF-0CA2-43AB-B278-FC6994F776ED"
+ /// "Timmy@aol.com"
+ /// "26437"
+ ///
+ public string UserId { get; set; }
+
+ ///
+ /// Any message that should be attached to this exceptions
+ ///
+ ///
+ /// Something went wrong while checking for application updates.
+ ///
+ public string Message { get; set; }
+
+ ///
+ /// Severity of the exception being reported
+ ///
+ public ExceptionSeverity Severity { get; set; }
+
+
+ ///
+ /// that triggered this exception. If no is provided
+ /// will try to get the current from
+ ///
+ public HttpContext HttpContext { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/ExceptionSeverity.cs b/Exceptron.Client/ExceptionSeverity.cs
new file mode 100644
index 000000000..aafa97e69
--- /dev/null
+++ b/Exceptron.Client/ExceptionSeverity.cs
@@ -0,0 +1,28 @@
+namespace Exceptron.Client
+{
+ ///
+ /// Severity of the exception being reported
+ ///
+ public enum ExceptionSeverity
+ {
+ ///
+ /// Excepted Error. Can be ignored
+ ///
+ None = 0,
+
+ ///
+ /// Error that can be handled gracefully
+ ///
+ Warning = 1,
+
+ ///
+ /// Blocking user from completing their intended action
+ ///
+ Error = 2,
+
+ ///
+ /// Will most likely cause the application to crash
+ ///
+ Fatal = 3
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/Exceptron.Client.csproj b/Exceptron.Client/Exceptron.Client.csproj
new file mode 100644
index 000000000..98c232372
--- /dev/null
+++ b/Exceptron.Client/Exceptron.Client.csproj
@@ -0,0 +1,101 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {B1784698-592E-4132-BDFA-9817409E3A96}
+ Library
+ Properties
+ Exceptron.Client
+ Exceptron.Client
+ v2.0
+ 512
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+ bin\Release\Exceptron.Client.xml
+
+
+ bin\Pilot\
+ TRACE
+ true
+ pdbonly
+ AnyCPU
+ bin\Release\Exceptrack.Driver.dll.CodeAnalysisLog.xml
+ true
+ GlobalSuppressions.cs
+ prompt
+ MinimumRecommendedRules.ruleset
+ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets
+ false
+ ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ PreserveNewest
+ Designer
+
+
+ Designer
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Exceptron.Client/Exceptron.Client.nuspec b/Exceptron.Client/Exceptron.Client.nuspec
new file mode 100644
index 000000000..163ba4cef
--- /dev/null
+++ b/Exceptron.Client/Exceptron.Client.nuspec
@@ -0,0 +1,20 @@
+
+
+
+ $id$
+ $version$
+ $id$
+ $author$
+ $author$
+ https://github.com/Exceptron/Exceptron.Net
+ https://www.exceptron.com/logos/Exceptron128.png
+ false
+ Exceptron.Client is a .NET wrapper for exceptron's REST API. exceptron help you report your application error to a central location for review, reporting and analytics.
+ en-US
+ Copyright © 2012
+ Exception Logging Analytics Reporting Exceptron
+
+
+
+
+
\ No newline at end of file
diff --git a/Exceptron.Client/ExceptronApiException.cs b/Exceptron.Client/ExceptronApiException.cs
new file mode 100644
index 000000000..beca6443b
--- /dev/null
+++ b/Exceptron.Client/ExceptronApiException.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Net;
+
+namespace Exceptron.Client
+{
+ public class ExceptronApiException : Exception
+ {
+ public ExceptronApiException(WebException innerException, string message)
+ : base(message, innerException)
+ {
+ Response = (HttpWebResponse)innerException.Response;
+ }
+
+ public HttpWebResponse Response { get; private set; }
+ }
+}
diff --git a/Exceptron.Client/ExceptronClient.cs b/Exceptron.Client/ExceptronClient.cs
new file mode 100644
index 000000000..dc67bee2e
--- /dev/null
+++ b/Exceptron.Client/ExceptronClient.cs
@@ -0,0 +1,316 @@
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Reflection;
+using System.Threading;
+using System.Web;
+using Exceptron.Client.Configuration;
+using Exceptron.Client.Message;
+
+namespace Exceptron.Client
+{
+ public class ExceptronClient : IExceptronClient
+ {
+ internal IRestClient RestClient { private get; set; }
+
+ ///
+ /// Version of Client
+ ///
+ public string ClientVersion
+ {
+ get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
+ }
+
+
+ ///
+ /// Name of Client
+ ///
+ public string ClientName
+ {
+ get { return "Official .NET"; }
+ }
+
+ ///
+ /// Client Configuration
+ ///
+ public ExceptronConfiguration Configuration { get; private set; }
+
+
+ ///
+ /// Framework Type of the Host Application (.Net/mono)
+ ///
+ public string FrameworkType { get; set; }
+
+ ///
+ /// Creates a new instance of
+ /// Loads from application config file.
+ ///
+ /// Version of the currently running application
+ public ExceptronClient(Version applicationVersion)
+ : this(ExceptronConfiguration.ReadConfig(), applicationVersion)
+ {
+ FrameworkType = ".Net";
+ }
+
+ private readonly string _applicationVersion;
+ private readonly string _maxFrameworkVersion;
+
+
+ /// exceptron client configuration
+ ///
+ public ExceptronClient(ExceptronConfiguration exceptronConfiguration, Version applicationVersion)
+ {
+ if (exceptronConfiguration == null)
+ throw new ArgumentNullException("exceptronConfiguration");
+
+ if (applicationVersion == null)
+ throw new ArgumentNullException("applicationVersion");
+
+ if (string.IsNullOrEmpty(exceptronConfiguration.ApiKey))
+ throw new ArgumentException("An API Key was not provided");
+
+ Configuration = exceptronConfiguration;
+
+ RestClient = new RestClient();
+
+ _applicationVersion = applicationVersion.ToString();
+
+ _maxFrameworkVersion = GetMaximumFrameworkVersion();
+
+ FrameworkType = ".Net";
+ }
+
+ ///
+ /// Submit an exception to exceptron Servers.
+ ///
+ /// Exception that is being reported
+ /// Component that experienced this exception.
+
+ /// Severity of the exception being reported
+ /// Any message that should be attached to this exceptions
+ /// ID that will uniquely identify the user
+ /// in which the exception occurred. If no is provided
+ /// will try to get the current from
+ ///
+ public ExceptionResponse SubmitException(Exception exception, string component, ExceptionSeverity severity = ExceptionSeverity.None, string message = null, string userId = null, HttpContext httpContext = null)
+ {
+ var exceptionData = new ExceptionData
+ {
+ Exception = exception,
+ Component = component,
+ Severity = severity,
+ Message = message,
+ UserId = userId,
+ HttpContext = httpContext
+ };
+
+ return SubmitException(exceptionData);
+ }
+
+ ///
+ /// Submit an exception to exceptron Servers.
+ ///
+ /// Exception data to be reported to the server
+ public ExceptionResponse SubmitException(ExceptionData exceptionData)
+ {
+ try
+ {
+ ValidateState(exceptionData);
+
+ var report = new ExceptionReport();
+
+ report.ap = Configuration.ApiKey;
+ report.dn = ClientName;
+ report.dv = ClientVersion;
+ report.aver = _applicationVersion;
+
+ report.ext = exceptionData.Exception.GetType().FullName;
+ report.stk = ConvertToFrames(exceptionData.Exception);
+ report.exm = exceptionData.Exception.Message;
+
+ report.cmp = exceptionData.Component;
+ report.uid = exceptionData.UserId;
+ report.msg = exceptionData.Message;
+ report.sv = (int)exceptionData.Severity;
+ report.fv = _maxFrameworkVersion;
+ report.ft = FrameworkType;
+
+ SetHttpInfo(exceptionData, report);
+ SetEnviromentInfo(report);
+
+ return RestClient.Put(Configuration.Host, report);
+ }
+ catch (Exception e)
+ {
+ Trace.WriteLine("Unable to submit exception to exceptron. ", e.ToString());
+
+ if (Configuration.ThrowExceptions)
+ {
+ throw;
+ }
+
+ return new ExceptionResponse { Exception = e };
+ }
+ }
+
+ private void ValidateState(ExceptionData exceptionData)
+ {
+ if (string.IsNullOrEmpty(Configuration.ApiKey))
+ throw new InvalidOperationException("ApiKey has not been provided for this client.");
+
+ if (exceptionData == null)
+ throw new ArgumentNullException("exceptionData");
+
+ if (exceptionData.Exception == null)
+ throw new ArgumentException("ExceptionData.Exception Cannot be null.", "exceptionData");
+ }
+
+ private void SetEnviromentInfo(ExceptionReport report)
+ {
+ report.cul = Thread.CurrentThread.CurrentCulture.Name;
+
+ try
+ {
+ report.os = Environment.OSVersion.VersionString;
+ }
+ catch (Exception)
+ {
+ if (Configuration.ThrowExceptions) throw;
+ }
+
+ if (Configuration.IncludeMachineName)
+ {
+ try
+ {
+ report.hn = Environment.MachineName;
+ }
+ catch (Exception)
+ {
+ if (Configuration.ThrowExceptions) throw;
+ }
+ }
+ }
+
+ private void SetHttpInfo(ExceptionData exceptionData, ExceptionReport report)
+ {
+ if (exceptionData.HttpContext == null && HttpContext.Current == null)
+ return;
+
+ if (exceptionData.HttpContext == null)
+ {
+ exceptionData.HttpContext = HttpContext.Current;
+ }
+
+ try
+ {
+
+ report.hm = exceptionData.HttpContext.Request.HttpMethod;
+
+ //TODO:find proper way to find http status code.
+ /*
+ var httpException = exceptionData.Exception as HttpException;
+ if (httpException != null)
+ {
+ report.sc = httpException.GetHttpCode();
+ }*/
+
+ report.url = exceptionData.HttpContext.Request.Url.ToString();
+ report.ua = exceptionData.HttpContext.Request.UserAgent;
+ }
+ catch (Exception)
+ {
+ if (Configuration.ThrowExceptions) throw;
+ }
+ }
+
+ internal static List ConvertToFrames(Exception exception)
+ {
+ if (exception == null) return null;
+
+ var stackTrace = new StackTrace(exception, true);
+
+ var frames = stackTrace.GetFrames();
+
+ if (frames == null) return null;
+
+ var result = new List();
+
+ for (int index = 0; index < frames.Length; index++)
+ {
+ var frame = frames[index];
+ var method = frame.GetMethod();
+ var declaringType = method.DeclaringType;
+
+ var currentFrame = new Frame
+ {
+ i = index,
+ fn = frame.GetFileName(),
+ ln = frame.GetFileLineNumber(),
+ m = method.ToString(),
+ };
+
+
+ currentFrame.m = currentFrame.m.Substring(currentFrame.m.IndexOf(' ')).Trim();
+
+
+ if (declaringType != null)
+ {
+ currentFrame.c = declaringType.FullName;
+ }
+
+ result.Add(currentFrame);
+ }
+
+
+ return result;
+ }
+
+ private string GetMaximumFrameworkVersion()
+ {
+ var clrVersion = Environment.Version;
+
+ if (clrVersion.Major == 2)
+ {
+ //Check if 2.0 or 3.5
+ try
+ {
+ Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089");
+ return "3.5";
+ }
+ catch (Exception)
+ {
+ }
+
+ return "2.0";
+ }
+
+ if (clrVersion.Major == 4)
+ {
+ //Check if 4.0 or 4.5
+ try
+ {
+ Assembly.Load("System.Threading.Tasks.Parallel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
+ return "4.5";
+ }
+ catch (Exception)
+ {
+ }
+
+ return "4.0";
+ }
+
+ return "Unknown";
+ }
+ }
+}
diff --git a/Exceptron.Client/IExceptronClient.cs b/Exceptron.Client/IExceptronClient.cs
new file mode 100644
index 000000000..518e70a4d
--- /dev/null
+++ b/Exceptron.Client/IExceptronClient.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Web;
+using Exceptron.Client.Configuration;
+using Exceptron.Client.Message;
+
+namespace Exceptron.Client
+{
+ public interface IExceptronClient
+ {
+ ///
+ /// Client Configuration
+ ///
+ ExceptronConfiguration Configuration { get; }
+
+ ///
+ /// Submit an exception to exceptron Servers.
+ ///
+ /// Exception data to be reported to the server
+ ExceptionResponse SubmitException(ExceptionData exceptionData);
+
+ ///
+ /// Submit an exception to exceptron Servers.
+ ///
+ /// Exception that is being reported
+ /// Component that experienced this exception.
+ /// Severity of the exception being reported
+ /// Any message that should be attached to this exceptions
+ /// ID that will uniquely identify the user
+ /// in which the exception occurred. If no is provided
+ /// will try to get the current from
+ ///
+ ExceptionResponse SubmitException(Exception exception, string component, ExceptionSeverity severity = ExceptionSeverity.None, string message = null, string userId = null, HttpContext httpContext = null);
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/IRestClient.cs b/Exceptron.Client/IRestClient.cs
new file mode 100644
index 000000000..b2ed3357a
--- /dev/null
+++ b/Exceptron.Client/IRestClient.cs
@@ -0,0 +1,7 @@
+namespace Exceptron.Client
+{
+ internal interface IRestClient
+ {
+ TResponse Put(string url, object report) where TResponse : class, new();
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/Message/ExceptionReport.cs b/Exceptron.Client/Message/ExceptionReport.cs
new file mode 100644
index 000000000..5a6878459
--- /dev/null
+++ b/Exceptron.Client/Message/ExceptionReport.cs
@@ -0,0 +1,111 @@
+using System.Collections.Generic;
+
+namespace Exceptron.Client.Message
+{
+ internal class ExceptionReport
+ {
+ ///
+ /// API key
+ ///
+ public string ap { get; set; }
+
+ ///
+ /// Application Version
+ ///
+ public string aver { get; set; }
+
+ ///
+ /// Exception Severity
+ ///
+ public int sv { get; set; }
+
+ ///
+ /// User or Instance ID
+ ///
+ public string uid { get; set; }
+
+ ///
+ /// Type of exception
+ ///
+ public string ext { get; set; }
+
+ ///
+ /// Exception message
+ ///
+ public string exm { get; set; }
+
+ ///
+ /// List of frames that make up the StackTrace of the exception
+ ///
+ public List stk { get; set; }
+
+ ///
+ /// Component that experienced this exception
+ ///
+ public string cmp { get; set; }
+
+ ///
+ /// Message that was logged along with the exception.
+ ///
+ public string msg { get; set; }
+
+ ///
+ /// User's culture in
+ ///
+ /// http://msdn.microsoft.com/en-us/library/system.globalization.cultureinfo.name.aspx
+ public string cul { get; set; }
+
+ ///
+ /// OS Version
+ ///
+ public string os { get; set; }
+
+ ///
+ /// Name of the Client that generated and is sending this message
+ ///
+ public string dn { get; set; }
+
+ ///
+ /// Version of the Client that generated and is sending this message
+ ///
+ public string dv { get; set; }
+
+ ///
+ /// Host name of the machine that encountered this exception
+ ///
+ public string hn { get; set; }
+
+ ///
+ /// Request url
+ /// Only used for exception in context of a web request/
+ public string url { get; set; }
+
+ ///
+ /// Browser useragent
+ ///
+ /// Only used for exception in context of a web request/
+ public string ua { get; set; }
+
+ ///
+ /// HTTP response status code
+ ///
+ /// Only used for exception in context of a web request/
+ public int sc { get; set; }
+
+ ///
+ /// Indicates the HTTP data transfer method used by the client.
+ ///
+ /// GET, POST, PUT, DELETE
+ public string hm { get; set; }
+
+ ///
+ /// Framework Version (CLR) of the Host Application
+ ///
+ public string fv { get; set; }
+
+ ///
+ /// Framework Type of the Host Application
+ ///
+ public string ft { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/Message/ExceptionResponse.cs b/Exceptron.Client/Message/ExceptionResponse.cs
new file mode 100644
index 000000000..4ba531adb
--- /dev/null
+++ b/Exceptron.Client/Message/ExceptionResponse.cs
@@ -0,0 +1,34 @@
+using System;
+using Exceptron.Client.Configuration;
+
+namespace Exceptron.Client.Message
+{
+ public class ExceptionResponse
+ {
+ ///
+ /// Exception report reference ID. This ID will be shared across
+ /// similar exceptions
+ ///
+ public string RefId { get; internal set; }
+
+ ///
+ /// Was the report successfully processed on the server
+ ///
+ public bool Successful
+ {
+ get
+ {
+ return !string.IsNullOrEmpty(RefId);
+ }
+ }
+
+ ///
+ /// Exception that caused the message to fail.
+ ///
+ ///
+ /// This property will only be populated if is set to />
+ /// Exception is thrown if is set to .
+ ///
+ public Exception Exception { get; internal set; }
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/Message/Frame.cs b/Exceptron.Client/Message/Frame.cs
new file mode 100644
index 000000000..97dae0416
--- /dev/null
+++ b/Exceptron.Client/Message/Frame.cs
@@ -0,0 +1,30 @@
+namespace Exceptron.Client.Message
+{
+ internal class Frame
+ {
+ ///
+ /// Order of current frame
+ ///
+ public int i { get; set; }
+
+ ///
+ /// Line number of the current frame
+ ///
+ public int ln { get; set; }
+
+ ///
+ /// File name of the current frame
+ ///
+ public string fn { get; set; }
+
+ ///
+ /// Method name for current frame
+ ///
+ public string m { get; set; }
+
+ ///
+ /// Class name for current frame
+ ///
+ public string c { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/Exceptron.Client/NuGet/web.config.transform b/Exceptron.Client/NuGet/web.config.transform
new file mode 100644
index 000000000..b3a77b15f
--- /dev/null
+++ b/Exceptron.Client/NuGet/web.config.transform
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Exceptron.Client/Properties/AssemblyInfo.cs b/Exceptron.Client/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..ffc438e05
--- /dev/null
+++ b/Exceptron.Client/Properties/AssemblyInfo.cs
@@ -0,0 +1,40 @@
+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("Exceptron.Client")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyDescription(".NET client for exceptron API. In-cloud exception aggregation and analytics.")]
+[assembly: AssemblyCompany("Exceptron Solutions Inc.")]
+[assembly: AssemblyProduct("Exceptron.Client")]
+[assembly: AssemblyCopyright("Copyright © 2012")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// 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("a463887e-594f-4733-b227-a79f4ffb2158")]
+
+// 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("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly: InternalsVisibleTo("Exceptron.Client.Tests")]
+[assembly: InternalsVisibleTo("Exceptron.Api.v1.Tests")]
+[assembly: InternalsVisibleTo("Exceptron.Rush")]
+[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
diff --git a/Exceptron.Client/RestClient.cs b/Exceptron.Client/RestClient.cs
new file mode 100644
index 000000000..3ff2ca01a
--- /dev/null
+++ b/Exceptron.Client/RestClient.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Net;
+using System.Text;
+using Exceptron.Client.fastJSON;
+
+namespace Exceptron.Client
+{
+ public sealed class RestClient : IRestClient
+ {
+ public TResponse Put(string url, object content) where TResponse : class ,new()
+ {
+
+ if (content == null)
+ throw new ArgumentNullException("content can not be null", "content");
+
+ if (string.IsNullOrEmpty(url))
+ throw new ArgumentNullException("url can not be null or empty", "url");
+
+ Trace.WriteLine("Attempting PUT to " + url);
+
+ var json = JSON.Instance.ToJSON(content);
+
+ var bytes = Encoding.UTF8.GetBytes(json);
+ var request = (HttpWebRequest)WebRequest.Create(url);
+ request.Timeout = 10000;
+ request.Method = "PUT";
+ request.ContentType = "application/json";
+ request.ContentLength = bytes.Length;
+ request.Accept = "application/json";
+
+ var dataStream = request.GetRequestStream();
+ dataStream.Write(bytes, 0, bytes.Length);
+ dataStream.Close();
+
+ string responseContent = string.Empty;
+
+ try
+ {
+ var webResponse = request.GetResponse();
+ responseContent = ReadResponse(webResponse);
+ var response = JSON.Instance.ToObject(responseContent);
+
+ return response;
+ }
+ catch (WebException e)
+ {
+ Trace.WriteLine(e.ToString());
+ responseContent = ReadResponse(e.Response);
+ throw new ExceptronApiException(e, responseContent);
+ }
+ finally
+ {
+ Trace.WriteLine(responseContent);
+ }
+ }
+
+
+ public static string ReadResponse(WebResponse webResponse)
+ {
+ if (webResponse == null) return string.Empty;
+
+ var responseStream = webResponse.GetResponseStream();
+
+ if (responseStream == null) return string.Empty;
+
+ var decodedStream = new StreamReader(responseStream, Encoding.GetEncoding(1252));
+ return decodedStream.ReadToEnd();
+ }
+ }
+}
diff --git a/Exceptron.Client/fastJSON/Getters.cs b/Exceptron.Client/fastJSON/Getters.cs
new file mode 100644
index 000000000..f6e2b8057
--- /dev/null
+++ b/Exceptron.Client/fastJSON/Getters.cs
@@ -0,0 +1,21 @@
+//http://fastjson.codeplex.com/
+//http://fastjson.codeplex.com/license
+
+using System;
+using System.Collections.Generic;
+
+namespace Exceptron.Client.fastJSON
+{
+ internal class Getters
+ {
+ public string Name;
+ public JSON.GenericGetter Getter;
+ public Type propertyType;
+ }
+
+ internal class DatasetSchema
+ {
+ public List Info { get; set; }
+ public string Name { get; set; }
+ }
+}
diff --git a/Exceptron.Client/fastJSON/JSON.cs b/Exceptron.Client/fastJSON/JSON.cs
new file mode 100644
index 000000000..45ad1210a
--- /dev/null
+++ b/Exceptron.Client/fastJSON/JSON.cs
@@ -0,0 +1,834 @@
+//http://fastjson.codeplex.com/
+//http://fastjson.codeplex.com/license
+
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Data;
+using System.Globalization;
+using System.IO;
+using System.Reflection;
+using System.Reflection.Emit;
+using System.Xml.Serialization;
+
+namespace Exceptron.Client.fastJSON
+{
+
+ internal class JSON
+ {
+ public readonly static JSON Instance = new JSON();
+
+ private JSON()
+ {
+ UseSerializerExtension = false;
+ SerializeNullValues = false;
+ UseOptimizedDatasetSchema = false;
+ UsingGlobalTypes = false;
+ }
+ public bool UseOptimizedDatasetSchema = true;
+ public bool UseFastGuid = true;
+ public bool UseSerializerExtension = true;
+ public bool IndentOutput = false;
+ public bool SerializeNullValues = true;
+ public bool UseUTCDateTime = false;
+ public bool ShowReadOnlyProperties = false;
+ public bool UsingGlobalTypes = true;
+
+ public string ToJSON(object obj)
+ {
+ return ToJSON(obj, UseSerializerExtension, UseFastGuid, UseOptimizedDatasetSchema, SerializeNullValues);
+ }
+
+
+ public string ToJSON(object obj,
+ bool enableSerializerExtensions,
+ bool enableFastGuid,
+ bool enableOptimizedDatasetSchema,
+ bool serializeNullValues)
+ {
+ return new JSONSerializer(enableOptimizedDatasetSchema, enableFastGuid, enableSerializerExtensions, serializeNullValues, IndentOutput).ConvertToJSON(obj);
+ }
+
+
+ public T ToObject(string json)
+ {
+ return (T)ToObject(json, typeof(T));
+ }
+
+
+ public object ToObject(string json, Type type)
+ {
+ var ht = new JsonParser(json).Decode() as Dictionary;
+ if (ht == null) return null;
+ return ParseDictionary(ht, null, type);
+ }
+
+#if CUSTOMTYPE
+ internal SafeDictionary _customSerializer = new SafeDictionary();
+ internal SafeDictionary _customDeserializer = new SafeDictionary();
+
+ public void RegisterCustomType(Type type, Serialize serializer, Deserialize deserializer)
+ {
+ if (type != null && serializer != null && deserializer != null)
+ {
+ _customSerializer.Add(type, serializer);
+ _customDeserializer.Add(type, deserializer);
+ // reset property cache
+ _propertycache = new SafeDictionary>();
+ }
+ }
+
+ internal bool IsTypeRegistered(Type t)
+ {
+ Serialize s;
+ return _customSerializer.TryGetValue(t, out s);
+ }
+#endif
+
+ #region [ PROPERTY GET SET CACHE ]
+
+ readonly SafeDictionary _tyname = new SafeDictionary();
+ internal string GetTypeAssemblyName(Type t)
+ {
+ string val = "";
+ if (_tyname.TryGetValue(t, out val))
+ return val;
+ else
+ {
+ string s = t.AssemblyQualifiedName;
+ _tyname.Add(t, s);
+ return s;
+ }
+ }
+
+ readonly SafeDictionary _typecache = new SafeDictionary();
+ private Type GetTypeFromCache(string typename)
+ {
+ Type val = null;
+ if (_typecache.TryGetValue(typename, out val))
+ return val;
+ else
+ {
+ Type t = Type.GetType(typename);
+ _typecache.Add(typename, t);
+ return t;
+ }
+ }
+
+ readonly SafeDictionary _constrcache = new SafeDictionary();
+ private delegate object CreateObject();
+ private object FastCreateInstance(Type objtype)
+ {
+ try
+ {
+ CreateObject c = null;
+ if (_constrcache.TryGetValue(objtype, out c))
+ {
+ return c();
+ }
+ else
+ {
+ DynamicMethod dynMethod = new DynamicMethod("_", objtype, null, true);
+ ILGenerator ilGen = dynMethod.GetILGenerator();
+
+ ilGen.Emit(OpCodes.Newobj, objtype.GetConstructor(Type.EmptyTypes));
+ ilGen.Emit(OpCodes.Ret);
+ c = (CreateObject)dynMethod.CreateDelegate(typeof(CreateObject));
+ _constrcache.Add(objtype, c);
+ return c();
+ }
+ }
+ catch (Exception exc)
+ {
+ throw new Exception(string.Format("Failed to fast create instance for type '{0}' from assemebly '{1}'",
+ objtype.FullName, objtype.AssemblyQualifiedName), exc);
+ }
+ }
+
+ private struct myPropInfo
+ {
+ public bool filled;
+ public Type pt;
+ public Type bt;
+ public Type changeType;
+ public bool isDictionary;
+ public bool isValueType;
+ public bool isGenericType;
+ public bool isArray;
+ public bool isByteArray;
+ public bool isGuid;
+#if !SILVERLIGHT
+ public bool isDataSet;
+ public bool isDataTable;
+ public bool isHashtable;
+#endif
+ public GenericSetter setter;
+ public bool isEnum;
+ public bool isDateTime;
+ public Type[] GenericTypes;
+ public bool isInt;
+ public bool isLong;
+ public bool isString;
+ public bool isBool;
+ public bool isClass;
+ public GenericGetter getter;
+ public bool isStringDictionary;
+ public string Name;
+#if CUSTOMTYPE
+ public bool isCustomType;
+#endif
+ public bool CanWrite;
+ }
+
+ readonly SafeDictionary> _propertycache = new SafeDictionary>();
+ private SafeDictionary Getproperties(Type type, string typename)
+ {
+ SafeDictionary sd = null;
+ if (_propertycache.TryGetValue(typename, out sd))
+ {
+ return sd;
+ }
+ else
+ {
+ sd = new SafeDictionary();
+ var pr = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ foreach (var p in pr)
+ {
+ myPropInfo d = CreateMyProp(p.PropertyType, p.Name);
+ d.CanWrite = p.CanWrite;
+ d.setter = CreateSetMethod(p);
+ d.getter = CreateGetMethod(p);
+ sd.Add(p.Name, d);
+ }
+
+ _propertycache.Add(typename, sd);
+ return sd;
+ }
+ }
+
+ private myPropInfo CreateMyProp(Type t, string name)
+ {
+ myPropInfo d = new myPropInfo();
+ d.filled = true;
+ d.CanWrite = true;
+ d.pt = t;
+ d.Name = name;
+ d.isDictionary = t.Name.Contains("Dictionary");
+ if (d.isDictionary)
+ d.GenericTypes = t.GetGenericArguments();
+ d.isValueType = t.IsValueType;
+ d.isGenericType = t.IsGenericType;
+ d.isArray = t.IsArray;
+ if (d.isArray)
+ d.bt = t.GetElementType();
+ if (d.isGenericType)
+ d.bt = t.GetGenericArguments()[0];
+ d.isByteArray = t == typeof(byte[]);
+ d.isGuid = (t == typeof(Guid) || t == typeof(Guid?));
+#if !SILVERLIGHT
+ d.isHashtable = t == typeof(Hashtable);
+ d.isDataSet = t == typeof(DataSet);
+ d.isDataTable = t == typeof(DataTable);
+#endif
+
+ d.changeType = GetChangeType(t);
+ d.isEnum = t.IsEnum;
+ d.isDateTime = t == typeof(DateTime) || t == typeof(DateTime?);
+ d.isInt = t == typeof(int) || t == typeof(int?);
+ d.isLong = t == typeof(long) || t == typeof(long?);
+ d.isString = t == typeof(string);
+ d.isBool = t == typeof(bool) || t == typeof(bool?);
+ d.isClass = t.IsClass;
+
+ if (d.isDictionary && d.GenericTypes.Length > 0 && d.GenericTypes[0] == typeof(string))
+ d.isStringDictionary = true;
+
+#if CUSTOMTYPE
+ if (IsTypeRegistered(t))
+ d.isCustomType = true;
+#endif
+ return d;
+ }
+
+ private delegate void GenericSetter(object target, object value);
+
+ private static GenericSetter CreateSetMethod(PropertyInfo propertyInfo)
+ {
+ MethodInfo setMethod = propertyInfo.GetSetMethod(nonPublic: true);
+ if (setMethod == null)
+ return null;
+
+ var arguments = new Type[2];
+ arguments[0] = arguments[1] = typeof(object);
+
+ DynamicMethod setter = new DynamicMethod("_", typeof(void), arguments, true);
+ ILGenerator il = setter.GetILGenerator();
+ il.Emit(OpCodes.Ldarg_0);
+ il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
+ il.Emit(OpCodes.Ldarg_1);
+
+ if (propertyInfo.PropertyType.IsClass)
+ il.Emit(OpCodes.Castclass, propertyInfo.PropertyType);
+ else
+ il.Emit(OpCodes.Unbox_Any, propertyInfo.PropertyType);
+
+ il.EmitCall(OpCodes.Callvirt, setMethod, null);
+ il.Emit(OpCodes.Ret);
+
+ return (GenericSetter)setter.CreateDelegate(typeof(GenericSetter));
+ }
+
+ internal delegate object GenericGetter(object obj);
+
+
+ private GenericGetter CreateGetMethod(PropertyInfo propertyInfo)
+ {
+ MethodInfo getMethod = propertyInfo.GetGetMethod();
+ if (getMethod == null)
+ return null;
+
+ var arguments = new Type[1];
+ arguments[0] = typeof(object);
+
+ DynamicMethod getter = new DynamicMethod("_", typeof(object), arguments, true);
+ ILGenerator il = getter.GetILGenerator();
+ il.Emit(OpCodes.Ldarg_0);
+ il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
+ il.EmitCall(OpCodes.Callvirt, getMethod, null);
+
+ if (!propertyInfo.PropertyType.IsClass)
+ il.Emit(OpCodes.Box, propertyInfo.PropertyType);
+
+ il.Emit(OpCodes.Ret);
+
+ return (GenericGetter)getter.CreateDelegate(typeof(GenericGetter));
+ }
+
+ readonly SafeDictionary> _getterscache = new SafeDictionary>();
+ internal List GetGetters(Type type)
+ {
+ List val = null;
+ if (_getterscache.TryGetValue(type, out val))
+ return val;
+
+ var props = type.GetProperties(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
+ var getters = new List();
+ foreach (var p in props)
+ {
+ if (!p.CanWrite && ShowReadOnlyProperties == false) continue;
+
+ var att = p.GetCustomAttributes(typeof(XmlIgnoreAttribute), false);
+ if (att != null && att.Length > 0)
+ continue;
+
+ GenericGetter g = CreateGetMethod(p);
+ if (g != null)
+ {
+ Getters gg = new Getters();
+ gg.Name = p.Name;
+ gg.Getter = g;
+ gg.propertyType = p.PropertyType;
+ getters.Add(gg);
+ }
+ }
+
+
+ _getterscache.Add(type, getters);
+ return getters;
+ }
+
+ private object ChangeType(object value, Type conversionType)
+ {
+ if (conversionType == typeof(int))
+ return (int)CreateLong((string)value);
+
+ else if (conversionType == typeof(long))
+ return CreateLong((string)value);
+
+ else if (conversionType == typeof(string))
+ return value;
+
+ else if (conversionType == typeof(Guid))
+ return CreateGuid((string)value);
+
+ else if (conversionType.IsEnum)
+ return CreateEnum(conversionType, (string)value);
+
+ return Convert.ChangeType(value, conversionType, CultureInfo.InvariantCulture);
+ }
+ #endregion
+
+
+ private object ParseDictionary(Dictionary d, Dictionary globaltypes, Type type)
+ {
+ object tn = "";
+ if (d.TryGetValue("$types", out tn))
+ {
+ UsingGlobalTypes = true;
+ globaltypes = new Dictionary();
+ foreach (var kv in (Dictionary)tn)
+ {
+ globaltypes.Add((string)kv.Value, kv.Key);
+ }
+ }
+
+ bool found = d.TryGetValue("$type", out tn);
+#if !SILVERLIGHT
+ if (found == false && type == typeof(Object))
+ {
+ return CreateDataset(d, globaltypes);
+ }
+#endif
+ if (found)
+ {
+ if (UsingGlobalTypes)
+ {
+ object tname = "";
+ if (globaltypes.TryGetValue((string)tn, out tname))
+ tn = tname;
+ }
+ type = GetTypeFromCache((string)tn);
+ }
+
+ if (type == null)
+ throw new Exception("Cannot determine type");
+
+ string typename = type.FullName;
+ object o = FastCreateInstance(type);
+ var props = Getproperties(type, typename);
+ foreach (var name in d.Keys)
+ {
+ if (name == "$map")
+ {
+ ProcessMap(o, props, (Dictionary)d[name]);
+ continue;
+ }
+ myPropInfo pi;
+ if (props.TryGetValue(name, out pi) == false)
+ continue;
+ if (pi.filled)
+ {
+ object v = d[name];
+
+ if (v != null)
+ {
+ object oset = null;
+
+ if (pi.isInt)
+ oset = (int)CreateLong((string)v);
+#if CUSTOMTYPE
+ else if (pi.isCustomType)
+ oset = CreateCustom((string)v, pi.pt);
+#endif
+ else if (pi.isLong)
+ oset = CreateLong((string)v);
+
+ else if (pi.isString)
+ oset = v;
+
+ else if (pi.isBool)
+ oset = (bool)v;
+
+ else if (pi.isGenericType && pi.isValueType == false && pi.isDictionary == false)
+#if SILVERLIGHT
+ oset = CreateGenericList((List